aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2012-04-27 19:52:05 +1000
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2012-04-27 19:52:05 +1000
commit437f68a9f320a3cafd063ae03313ce83db96668e (patch)
tree89c5c7ba4fd1edd4572d8857c3099546b6e43346
parentUpdate ejabberd version number to 2.1.11 (diff)
Merge SQL and Mnesia code into one module (EJAB-1560)
-rw-r--r--src/acl.erl10
-rw-r--r--src/ejabberd.app6
-rw-r--r--src/ejabberd_admin.erl13
-rw-r--r--src/ejabberd_auth_external.erl21
-rw-r--r--src/ejabberd_config.erl29
-rw-r--r--src/ejabberd_piefxis.erl70
-rw-r--r--src/ejd2odbc.erl2
-rw-r--r--src/gen_mod.erl14
-rw-r--r--src/jd2ejd.erl46
-rw-r--r--src/mod_announce.erl180
-rw-r--r--src/mod_announce_odbc.erl885
-rw-r--r--src/mod_blocking.erl292
-rw-r--r--src/mod_blocking_odbc.erl365
-rw-r--r--src/mod_configure.erl12
-rw-r--r--src/mod_irc/Makefile.win325
-rw-r--r--src/mod_irc/mod_irc.erl206
-rw-r--r--src/mod_irc/mod_irc_odbc.erl1033
-rw-r--r--src/mod_last.erl53
-rw-r--r--src/mod_last_odbc.erl204
-rw-r--r--src/mod_muc/Makefile.win325
-rw-r--r--src/mod_muc/mod_muc.erl279
-rw-r--r--src/mod_muc/mod_muc_odbc.erl875
-rw-r--r--src/mod_muc/mod_muc_room.erl100
-rw-r--r--src/mod_offline.erl435
-rw-r--r--src/mod_offline_odbc.erl548
-rw-r--r--src/mod_privacy.erl592
-rw-r--r--src/mod_privacy_odbc.erl878
-rw-r--r--src/mod_private.erl86
-rw-r--r--src/mod_private_odbc.erl136
-rw-r--r--src/mod_roster.erl666
-rw-r--r--src/mod_roster_odbc.erl1211
-rw-r--r--src/mod_shared_roster.erl300
-rw-r--r--src/mod_shared_roster_odbc.erl1165
-rw-r--r--src/mod_vcard.erl319
-rw-r--r--src/mod_vcard_odbc.erl659
-rw-r--r--src/mod_vcard_xupdate.erl58
-rw-r--r--src/mod_vcard_xupdate_odbc.erl128
-rw-r--r--src/web/ejabberd_web_admin.erl27
38 files changed, 2849 insertions, 9064 deletions
diff --git a/src/acl.erl b/src/acl.erl
index dd7c441de..6ce11f99a 100644
--- a/src/acl.erl
+++ b/src/acl.erl
@@ -241,9 +241,9 @@ is_glob_match(String, Glob) ->
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
loaded_shared_roster_module(Host) ->
- case {gen_mod:is_loaded(Host, mod_shared_roster_odbc),
- gen_mod:is_loaded(Host, mod_shared_roster_ldap)} of
- {true, _} -> mod_shared_roster_odbc;
- {_, true} -> mod_shared_roster_ldap;
- _ -> mod_shared_roster
+ case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
+ true ->
+ mod_shared_roster_ldap;
+ false ->
+ mod_shared_roster
end.
diff --git a/src/ejabberd.app b/src/ejabberd.app
index 2f08601a8..1af67fbae 100644
--- a/src/ejabberd.app
+++ b/src/ejabberd.app
@@ -78,16 +78,12 @@
mod_irc,
mod_irc_connection,
mod_last,
- mod_last_odbc,
mod_muc,
mod_muc_log,
mod_muc_room,
mod_offline,
- mod_offline_odbc,
mod_privacy,
- mod_privacy_odbc,
mod_private,
- mod_private_odbc,
mod_proxy65,
mod_proxy65_lib,
mod_proxy65_service,
@@ -96,14 +92,12 @@
mod_pubsub,
mod_register,
mod_roster,
- mod_roster_odbc,
mod_service_log,
mod_shared_roster,
mod_stats,
mod_time,
mod_vcard,
mod_vcard_ldap,
- mod_vcard_odbc,
mod_version,
node_buddy,
node_club,
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index b7fbbd00c..40c8b8dca 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -373,13 +373,16 @@ import_dir(Path) ->
%%%
delete_expired_messages() ->
- {atomic, ok} = mod_offline:remove_expired_messages(),
- ok.
+ lists:foreach(
+ fun(Host) ->
+ {atomic, ok} = mod_offline:remove_expired_messages(Host)
+ end, ?MYHOSTS).
delete_old_messages(Days) ->
- {atomic, _} = mod_offline:remove_old_messages(Days),
- ok.
-
+ lists:foreach(
+ fun(Host) ->
+ {atomic, _} = mod_offline:remove_old_messages(Days, Host)
+ end, ?MYHOSTS).
%%%
%%% Mnesia management
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index 06ec5548f..ba89534d1 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -293,27 +293,20 @@ get_last_access(User, Server) ->
get_last_info(User, Server) ->
case get_mod_last_enabled(Server) of
mod_last -> mod_last:get_last_info(User, Server);
- mod_last_odbc -> mod_last_odbc:get_last_info(User, Server);
no_mod_last -> mod_last_required
end.
-%% @spec (Server) -> mod_last | mod_last_odbc | no_mod_last
+%% @spec (Server) -> mod_last | no_mod_last
get_mod_last_enabled(Server) ->
- ML = gen_mod:is_loaded(Server, mod_last),
- MLO = gen_mod:is_loaded(Server, mod_last_odbc),
- case {ML, MLO} of
- {true, _} -> mod_last;
- {false, true} -> mod_last_odbc;
- {false, false} -> no_mod_last
+ case gen_mod:is_loaded(Server, mod_last) of
+ true -> mod_last;
+ false -> no_mod_last
end.
get_mod_last_configured(Server) ->
- ML = is_configured(Server, mod_last),
- MLO = is_configured(Server, mod_last_odbc),
- case {ML, MLO} of
- {true, _} -> mod_last;
- {false, true} -> mod_last_odbc;
- {false, false} -> no_mod_last
+ case is_configured(Server, mod_last) of
+ true -> mod_last;
+ false -> no_mod_last
end.
is_configured(Host, Module) ->
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 503537e76..f83d85671 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -467,6 +467,8 @@ process_host_term(Term, Host, State) ->
State;
{odbc_server, ODBC_server} ->
add_option({odbc_server, Host}, ODBC_server, State);
+ {modules, Modules} ->
+ add_option({modules, Host}, replace_modules(Modules), State);
{Opt, Val} ->
add_option({Opt, Host}, Val, State)
end.
@@ -610,3 +612,30 @@ is_file_readable(Path) ->
{error, _Reason} ->
false
end.
+
+replace_module(mod_announce_odbc) -> {mod_announce, odbc};
+replace_module(mod_blocking_odbc) -> {mod_blocking, odbc};
+replace_module(mod_irc_odbc) -> {mod_irc, odbc};
+replace_module(mod_last_odbc) -> {mod_last, odbc};
+replace_module(mod_muc_odbc) -> {mod_muc, odbc};
+replace_module(mod_offline_odbc) -> {mod_offline, odbc};
+replace_module(mod_privacy_odbc) -> {mod_privacy, odbc};
+replace_module(mod_private_odbc) -> {mod_private, odbc};
+replace_module(mod_roster_odbc) -> {mod_roster, odbc};
+replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, odbc};
+replace_module(mod_vcard_odbc) -> {mod_vcard, odbc};
+replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, odbc};
+replace_module(Module) -> Module.
+
+replace_modules(Modules) ->
+ lists:map(
+ fun({Module, Opts}) ->
+ case replace_module(Module) of
+ {NewModule, DBType} ->
+ NewOpts = [{db_type, DBType} |
+ lists:keydelete(db_type, 1, Opts)],
+ {NewModule, NewOpts};
+ NewModule ->
+ {NewModule, Opts}
+ end
+ end, Modules).
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 0ed1f6c0c..b667301a5 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -289,9 +289,10 @@ create_user(User,Password,Domain) ->
populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
io:format("Trying to add/update roster list...",[]),
- case loaded_module(Domain,[mod_roster_odbc,mod_roster]) of
- {ok, M} ->
- case M:set_items(User, Domain, exmpp_xml:xmlel_to_xmlelement(El)) of
+ case loaded_module(Domain, mod_roster) of
+ {ok, _DBType} ->
+ case mod_roster:set_items(User, Domain,
+ exmpp_xml:xmlel_to_xmlelement(El)) of
{atomic, ok} ->
io:format(" DONE.~n",[]),
ok;
@@ -302,7 +303,7 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
{error, not_found}
end;
E -> io:format(" ERROR: ~p~n",[E]),
- ?ERROR_MSG("No modules loaded [mod_roster, mod_roster_odbc] ~s ~n",
+ ?ERROR_MSG("No modules loaded [mod_roster] ~s ~n",
[exmpp_xml:document_to_list(El)]),
{error, not_found}
end;
@@ -330,10 +331,10 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
io:format("Trying to add/update vCards...",[]),
- case loaded_module(Domain,[mod_vcard,mod_vcard_odbc]) of
- {ok, M} -> FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
+ case loaded_module(Domain, mod_vcard) of
+ {ok, _} -> FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
IQ = iq_to_old_iq(#iq{type = set, payload = El}),
- case M:process_sm_iq(FullUser, FullUser , IQ) of
+ case mod_vcard:process_sm_iq(FullUser, FullUser , IQ) of
{error,_Err} ->
io:format(" ERROR.~n",[]),
?ERROR_MSG("Error processing vcard ~s : ~p ~n",
@@ -343,7 +344,7 @@ populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
end;
_ ->
io:format(" ERROR.~n",[]),
- ?ERROR_MSG("No modules loaded [mod_vcard, mod_vcard_odbc] ~s ~n",
+ ?ERROR_MSG("No modules loaded [mod_vcard] ~s ~n",
[exmpp_xml:document_to_list(El)]),
{error, not_found}
end;
@@ -356,8 +357,8 @@ populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
io:format("Trying to add/update offline-messages...",[]),
- case loaded_module(Domain, [mod_offline, mod_offline_odbc]) of
- {ok, M} ->
+ case loaded_module(Domain, mod_offline) of
+ {ok, _DBType} ->
ok = exmpp_xml:foreach(
fun (_Element, {xmlcdata, _}) ->
ok;
@@ -367,11 +368,11 @@ populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
FullUser = jid_to_old_jid(exmpp_jid:make(User,
Domain)),
OldChild = exmpp_xml:xmlel_to_xmlelement(Child),
- _R = M:store_packet(FullFrom, FullUser, OldChild)
+ _R = mod_offline:store_packet(FullFrom, FullUser, OldChild)
end, El), io:format(" DONE.~n",[]);
_ ->
io:format(" ERROR.~n",[]),
- ?ERROR_MSG("No modules loaded [mod_offline, mod_offline_odbc] ~s ~n",
+ ?ERROR_MSG("No modules loaded [mod_offline] ~s ~n",
[exmpp_xml:document_to_list(El)]),
{error, not_found}
end;
@@ -384,15 +385,15 @@ populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:private'}) ->
io:format("Trying to add/update private storage...",[]),
- case loaded_module(Domain,[mod_private_odbc,mod_private]) of
- {ok, M} ->
+ case loaded_module(Domain, mod_private) of
+ {ok, _DBType} ->
FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
IQ = iq_to_old_iq(#iq{type = set,
ns = 'jabber:iq:private',
kind = request,
iq_ns = 'jabberd:client',
payload = El}),
- case M:process_sm_iq(FullUser, FullUser, IQ ) of
+ case mod_private:process_sm_iq(FullUser, FullUser, IQ ) of
{error, _Err} ->
io:format(" ERROR.~n",[]),
?ERROR_MSG("Error processing private storage ~s : ~p ~n",
@@ -401,7 +402,7 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:private'}) ->
end;
_ ->
io:format(" ERROR.~n",[]),
- ?ERROR_MSG("No modules loaded [mod_private, mod_private_odbc] ~s ~n",
+ ?ERROR_MSG("No modules loaded [mod_private] ~s ~n",
[exmpp_xml:document_to_list(El)]),
{error, not_found}
end;
@@ -415,13 +416,12 @@ populate_user(_User, _Domain, _El) ->
%%%==================================
%%%% Utilities
-loaded_module(Domain,Options) ->
- LoadedModules = gen_mod:loaded_modules(Domain),
- case lists:filter(fun(Module) ->
- lists:member(Module, LoadedModules)
- end, Options) of
- [M|_] -> {ok, M};
- [] -> {error,not_found}
+loaded_module(Domain, Module) ->
+ case gen_mod:is_loaded(Domain, Module) of
+ true ->
+ {ok, gen_mod:db_type(Domain, Module)};
+ false ->
+ {error, not_found}
end.
jid_to_old_jid(Jid) ->
@@ -574,13 +574,13 @@ build_password_string(Password) when is_list(Password) ->
%% @spec (InfoName::atom(), Username::string(), Host::string()) -> string()
extract_user_info(roster, Username, Host) ->
- case loaded_module(Host,[mod_roster_odbc,mod_roster]) of
- {ok, M} ->
+ case loaded_module(Host, mod_roster) of
+ {ok, _DBType} ->
From = To = jlib:make_jid(Username, Host, ""),
SubelGet = {xmlelement, "query", [{"xmlns",?NS_ROSTER}], []},
%%IQGet = #iq{type=get, xmlns=?NS_ROSTER, payload=SubelGet}, % this is for 3.0.0 version
IQGet = {iq, "", get, ?NS_ROSTER, "" , SubelGet},
- Res = M:process_local_iq(From, To, IQGet),
+ Res = mod_roster:process_local_iq(From, To, IQGet),
%%[El] = Res#iq.payload, % this is for 3.0.0 version
{iq, _, result, _, _, Els} = Res,
case Els of
@@ -592,8 +592,8 @@ extract_user_info(roster, Username, Host) ->
end;
extract_user_info(offline, Username, Host) ->
- case loaded_module(Host,[mod_offline,mod_offline_odbc]) of
- {ok, mod_offline} ->
+ case loaded_module(Host, mod_offline) of
+ {ok, mnesia} ->
Els = mnesia_pop_offline_messages([], Username, Host),
case Els of
[] -> "";
@@ -601,30 +601,30 @@ extract_user_info(offline, Username, Host) ->
OfEl = {xmlelement, "offline-messages", [], Els},
exmpp_xml:document_to_list(OfEl)
end;
- {ok, mod_offline_odbc} ->
+ {ok, odbc} ->
"";
_E ->
""
end;
extract_user_info(private, Username, Host) ->
- case loaded_module(Host,[mod_private,mod_private_odbc]) of
- {ok, mod_private} ->
+ case loaded_module(Host, mod_private) of
+ {ok, mnesia} ->
get_user_private_mnesia(Username, Host);
- {ok, mod_private_odbc} ->
+ {ok, odbc} ->
"";
_E ->
""
end;
extract_user_info(vcard, Username, Host) ->
- case loaded_module(Host,[mod_vcard, mod_vcard_odbc, mod_vcard_odbc]) of
- {ok, M} ->
+ case loaded_module(Host, mod_vcard) of
+ {ok, _DBType} ->
From = To = jlib:make_jid(Username, Host, ""),
SubelGet = {xmlelement, "vCard", [{"xmlns",?NS_VCARD}], []},
%%IQGet = #iq{type=get, xmlns=?NS_VCARD, payload=SubelGet}, % this is for 3.0.0 version
IQGet = {iq, "", get, ?NS_VCARD, "" , SubelGet},
- Res = M:process_sm_iq(From, To, IQGet),
+ Res = mod_vcard:process_sm_iq(From, To, IQGet),
%%[El] = Res#iq.payload, % this is for 3.0.0 version
{iq, _, result, _, _, Els} = Res,
case Els of
diff --git a/src/ejd2odbc.erl b/src/ejd2odbc.erl
index a2331137e..7d96aac34 100644
--- a/src/ejd2odbc.erl
+++ b/src/ejd2odbc.erl
@@ -383,7 +383,7 @@ export_privacy(Server, Output) ->
fun({Name, List}) ->
SName = ejabberd_odbc:escape(Name),
RItems = lists:map(
- fun mod_privacy_odbc:item_to_raw/1,
+ fun mod_privacy:item_to_raw/1,
List),
ID = integer_to_list(get_id()),
["delete from privacy_list "
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index 941717f64..4f5c06e7a 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -34,6 +34,8 @@
get_opt/2,
get_opt/3,
get_opt_host/3,
+ db_type/1,
+ db_type/2,
get_module_opt/4,
get_module_opt_host/3,
loaded_modules/1,
@@ -192,6 +194,18 @@ get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, Default),
ejabberd_regexp:greplace(Val, "@HOST@", Host).
+db_type(Opts) ->
+ case get_opt(db_type, Opts, mnesia) of
+ odbc -> odbc;
+ _ -> mnesia
+ end.
+
+db_type(Host, Module) ->
+ case get_module_opt(Host, Module, db_type, mnesia) of
+ odbc -> odbc;
+ _ -> mnesia
+ end.
+
loaded_modules(Host) ->
ets:select(ejabberd_modules,
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
index 0770e0794..15a467b1e 100644
--- a/src/jd2ejd.erl
+++ b/src/jd2ejd.erl
@@ -116,54 +116,28 @@ xdb_data(_User, _Server, {xmlcdata, _CData}) ->
ok;
xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
From = jlib:make_jid(User, Server, ""),
- LServer = jlib:nameprep(Server),
case xml:get_attr_s("xmlns", Attrs) of
?NS_AUTH ->
Password = xml:get_tag_cdata(El),
ejabberd_auth:set_password(User, Server, Password),
ok;
?NS_ROSTER ->
- case lists:member(mod_roster_odbc,
- gen_mod:loaded_modules(LServer)) of
- true ->
- catch mod_roster_odbc:set_items(User, Server, El);
- false ->
- catch mod_roster:set_items(User, Server, El)
- end,
+ catch mod_roster:set_items(User, Server, El),
ok;
?NS_LAST ->
TimeStamp = xml:get_attr_s("last", Attrs),
Status = xml:get_tag_cdata(El),
- case lists:member(mod_last_odbc,
- gen_mod:loaded_modules(LServer)) of
- true ->
- catch mod_last_odbc:store_last_info(
- User,
- Server,
- list_to_integer(TimeStamp),
- Status);
- false ->
- catch mod_last:store_last_info(
- User,
- Server,
- list_to_integer(TimeStamp),
- Status)
- end,
+ catch mod_last:store_last_info(
+ User,
+ Server,
+ list_to_integer(TimeStamp),
+ Status),
ok;
?NS_VCARD ->
- case lists:member(mod_vcard_odbc,
- gen_mod:loaded_modules(LServer)) of
- true ->
- catch mod_vcard_odbc:process_sm_iq(
- From,
- jlib:make_jid("", Server, ""),
- #iq{type = set, xmlns = ?NS_VCARD, sub_el = El});
- false ->
- catch mod_vcard:process_sm_iq(
- From,
- jlib:make_jid("", Server, ""),
- #iq{type = set, xmlns = ?NS_VCARD, sub_el = El})
- end,
+ catch mod_vcard:process_sm_iq(
+ From,
+ jlib:make_jid("", Server, ""),
+ #iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
ok;
"jabber:x:offline" ->
process_offline(Server, From, El),
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index d398c2b36..d270a6e4e 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -56,12 +56,21 @@
-define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
tokenize(Node) -> string:tokens(Node, "/#").
-start(Host, _Opts) ->
- mnesia:create_table(motd, [{disc_copies, [node()]},
- {attributes, record_info(fields, motd)}]),
- mnesia:create_table(motd_users, [{disc_copies, [node()]},
- {attributes, record_info(fields, motd_users)}]),
- update_tables(),
+start(Host, Opts) ->
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(motd,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, motd)}]),
+ mnesia:create_table(motd_users,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, motd_users)}]),
+ update_tables();
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(local_send_to_resource_hook, Host,
?MODULE, announce, 50),
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
@@ -743,16 +752,33 @@ announce_all_hosts_motd(From, To, Packet) ->
end.
announce_motd(Host, Packet) ->
- announce_motd_update(Host, Packet),
- Sessions = ejabberd_sm:get_vh_session_list(Host),
- announce_online1(Sessions, Host, Packet),
- F = fun() ->
- lists:foreach(
- fun({U, S, _R}) ->
- mnesia:write(#motd_users{us = {U, S}})
- end, Sessions)
- end,
- mnesia:transaction(F).
+ LServer = jlib:nameprep(Host),
+ announce_motd_update(LServer, Packet),
+ Sessions = ejabberd_sm:get_vh_session_list(LServer),
+ announce_online1(Sessions, LServer, Packet),
+ case gen_mod:db_type(LServer, ?MODULE) of
+ mnesia ->
+ F = fun() ->
+ lists:foreach(
+ fun({U, S, _R}) ->
+ mnesia:write(#motd_users{us = {U, S}})
+ end, Sessions)
+ end,
+ mnesia:transaction(F);
+ odbc ->
+ F = fun() ->
+ lists:foreach(
+ fun({U, _S, _R}) ->
+ Username = ejabberd_odbc:escape(U),
+ odbc_queries:update_t(
+ "motd",
+ ["username", "xml"],
+ [Username, ""],
+ ["username='", Username, "'"])
+ end, Sessions)
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F)
+ end.
announce_motd_update(From, To, Packet) ->
Host = To#jid.lserver,
@@ -778,10 +804,23 @@ announce_all_hosts_motd_update(From, To, Packet) ->
announce_motd_update(LServer, Packet) ->
announce_motd_delete(LServer),
- F = fun() ->
- mnesia:write(#motd{server = LServer, packet = Packet})
- end,
- mnesia:transaction(F).
+ case gen_mod:db_type(LServer, ?MODULE) of
+ mnesia ->
+ F = fun() ->
+ mnesia:write(#motd{server = LServer, packet = Packet})
+ end,
+ mnesia:transaction(F);
+ odbc ->
+ XML = ejabberd_odbc:escape(xml:element_to_binary(Packet)),
+ F = fun() ->
+ odbc_queries:update_t(
+ "motd",
+ ["username", "xml"],
+ ["", XML],
+ ["username=''"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F)
+ end.
announce_motd_delete(From, To, Packet) ->
Host = To#jid.lserver,
@@ -806,21 +845,32 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
end.
announce_motd_delete(LServer) ->
- F = fun() ->
- mnesia:delete({motd, LServer}),
- mnesia:write_lock_table(motd_users),
- Users = mnesia:select(
- motd_users,
- [{#motd_users{us = '$1', _ = '_'},
- [{'==', {element, 2, '$1'}, LServer}],
- ['$1']}]),
- lists:foreach(fun(US) ->
- mnesia:delete({motd_users, US})
- end, Users)
- end,
- mnesia:transaction(F).
+ case gen_mod:db_type(LServer, ?MODULE) of
+ mnesia ->
+ F = fun() ->
+ mnesia:delete({motd, LServer}),
+ mnesia:write_lock_table(motd_users),
+ Users = mnesia:select(
+ motd_users,
+ [{#motd_users{us = '$1', _ = '_'},
+ [{'==', {element, 2, '$1'}, LServer}],
+ ['$1']}]),
+ lists:foreach(fun(US) ->
+ mnesia:delete({motd_users, US})
+ end, Users)
+ end,
+ mnesia:transaction(F);
+ odbc ->
+ F = fun() ->
+ ejabberd_odbc:sql_query_t(["delete from motd;"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F)
+ end.
-send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
+send_motd(JID) ->
+ send_motd(JID, gen_mod:db_type(JID#jid.lserver, ?MODULE)).
+
+send_motd(#jid{luser = LUser, lserver = LServer} = JID, mnesia) ->
case catch mnesia:dirty_read({motd, LServer}) of
[#motd{packet = Packet}] ->
US = {LUser, LServer},
@@ -837,15 +887,69 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
end;
_ ->
ok
- end.
+ end;
+send_motd(#jid{luser = LUser, lserver = LServer} = JID, odbc) when LUser /= "" ->
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select xml from motd where username='';"]) of
+ {selected, ["xml"], [{XML}]} ->
+ case xml_stream:parse_element(XML) of
+ {error, _} ->
+ ok;
+ Packet ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select username from motd "
+ "where username='", Username, "';"]) of
+ {selected, ["username"], []} ->
+ Local = jlib:make_jid("", LServer, ""),
+ ejabberd_router:route(Local, JID, Packet),
+ F = fun() ->
+ odbc_queries:update_t(
+ "motd",
+ ["username", "xml"],
+ [Username, ""],
+ ["username='", Username, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F);
+ _ ->
+ ok
+ end
+ end;
+ _ ->
+ ok
+ end;
+send_motd(_, odbc) ->
+ ok.
get_stored_motd(LServer) ->
+ case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
+ {ok, Packet} ->
+ {xml:get_subtag_cdata(Packet, "subject"),
+ xml:get_subtag_cdata(Packet, "body")};
+ error ->
+ {"", ""}
+ end.
+
+get_stored_motd_packet(LServer, mnesia) ->
case catch mnesia:dirty_read({motd, LServer}) of
[#motd{packet = Packet}] ->
- {xml:get_subtag_cdata(Packet, "subject"),
- xml:get_subtag_cdata(Packet, "body")};
+ {ok, Packet};
_ ->
- {"", ""}
+ error
+ end;
+get_stored_motd_packet(LServer, odbc) ->
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select xml from motd where username='';"]) of
+ {selected, ["xml"], [{XML}]} ->
+ case xml_stream:parse_element(XML) of
+ {error, _} ->
+ error;
+ Packet ->
+ {ok, Packet}
+ end;
+ _ ->
+ error
end.
%% This function is similar to others, but doesn't perform any ACL verification
diff --git a/src/mod_announce_odbc.erl b/src/mod_announce_odbc.erl
deleted file mode 100644
index 101f8aae2..000000000
--- a/src/mod_announce_odbc.erl
+++ /dev/null
@@ -1,885 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_announce_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Manage announce messages
-%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%% Implements a small subset of XEP-0133: Service Administration
-%%% Version 1.1 (2005-08-19)
-
--module(mod_announce_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
- init/0,
- stop/1,
- announce/3,
- send_motd/1,
- disco_identity/5,
- disco_features/5,
- disco_items/5,
- send_announcement_to_all/3,
- announce_commands/4,
- announce_items/4]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("adhoc.hrl").
-
--define(PROCNAME, ejabberd_announce).
-
--define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
-tokenize(Node) -> string:tokens(Node, "/#").
-
-start(Host, _Opts) ->
- ejabberd_hooks:add(local_send_to_resource_hook, Host,
- ?MODULE, announce, 50),
- ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
- ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
- ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 50),
- ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, announce_items, 50),
- ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
- ejabberd_hooks:add(user_available_hook, Host,
- ?MODULE, send_motd, 50),
- register(gen_mod:get_module_proc(Host, ?PROCNAME),
- proc_lib:spawn(?MODULE, init, [])).
-
-init() ->
- loop().
-
-loop() ->
- receive
- {announce_all, From, To, Packet} ->
- announce_all(From, To, Packet),
- loop();
- {announce_all_hosts_all, From, To, Packet} ->
- announce_all_hosts_all(From, To, Packet),
- loop();
- {announce_online, From, To, Packet} ->
- announce_online(From, To, Packet),
- loop();
- {announce_all_hosts_online, From, To, Packet} ->
- announce_all_hosts_online(From, To, Packet),
- loop();
- {announce_motd, From, To, Packet} ->
- announce_motd(From, To, Packet),
- loop();
- {announce_all_hosts_motd, From, To, Packet} ->
- announce_all_hosts_motd(From, To, Packet),
- loop();
- {announce_motd_update, From, To, Packet} ->
- announce_motd_update(From, To, Packet),
- loop();
- {announce_all_hosts_motd_update, From, To, Packet} ->
- announce_all_hosts_motd_update(From, To, Packet),
- loop();
- {announce_motd_delete, From, To, Packet} ->
- announce_motd_delete(From, To, Packet),
- loop();
- {announce_all_hosts_motd_delete, From, To, Packet} ->
- announce_all_hosts_motd_delete(From, To, Packet),
- loop();
- _ ->
- loop()
- end.
-
-stop(Host) ->
- ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, announce_commands, 50),
- ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, announce_items, 50),
- ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50),
- ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
- ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50),
- ejabberd_hooks:delete(local_send_to_resource_hook, Host,
- ?MODULE, announce, 50),
- ejabberd_hooks:delete(user_available_hook, Host,
- ?MODULE, send_motd, 50),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- exit(whereis(Proc), stop),
- {wait, Proc}.
-
-%% Announcing via messages to a custom resource
-announce(From, To, Packet) ->
- case To of
- #jid{luser = "", lresource = Res} ->
- {xmlelement, Name, _Attrs, _Els} = Packet,
- Proc = gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME),
- case {Res, Name} of
- {"announce/all", "message"} ->
- Proc ! {announce_all, From, To, Packet},
- stop;
- {"announce/all-hosts/all", "message"} ->
- Proc ! {announce_all_hosts_all, From, To, Packet},
- stop;
- {"announce/online", "message"} ->
- Proc ! {announce_online, From, To, Packet},
- stop;
- {"announce/all-hosts/online", "message"} ->
- Proc ! {announce_all_hosts_online, From, To, Packet},
- stop;
- {"announce/motd", "message"} ->
- Proc ! {announce_motd, From, To, Packet},
- stop;
- {"announce/all-hosts/motd", "message"} ->
- Proc ! {announce_all_hosts_motd, From, To, Packet},
- stop;
- {"announce/motd/update", "message"} ->
- Proc ! {announce_motd_update, From, To, Packet},
- stop;
- {"announce/all-hosts/motd/update", "message"} ->
- Proc ! {announce_all_hosts_motd_update, From, To, Packet},
- stop;
- {"announce/motd/delete", "message"} ->
- Proc ! {announce_motd_delete, From, To, Packet},
- stop;
- {"announce/all-hosts/motd/delete", "message"} ->
- Proc ! {announce_all_hosts_motd_delete, From, To, Packet},
- stop;
- _ ->
- ok
- end;
- _ ->
- ok
- end.
-
-%%-------------------------------------------------------------------------
-%% Announcing via ad-hoc commands
--define(INFO_COMMAND(Lang, Node),
- [{xmlelement, "identity",
- [{"category", "automation"},
- {"type", "command-node"},
- {"name", get_title(Lang, Node)}], []}]).
-
-disco_identity(Acc, _From, _To, Node, Lang) ->
- LNode = tokenize(Node),
- case LNode of
- ?NS_ADMINL("announce") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("announce-allhosts") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("announce-all") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("announce-all-allhosts") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("set-motd") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("set-motd-allhosts") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("edit-motd") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("edit-motd-allhosts") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("delete-motd") ->
- ?INFO_COMMAND(Lang, Node);
- ?NS_ADMINL("delete-motd-allhosts") ->
- ?INFO_COMMAND(Lang, Node);
- _ ->
- Acc
- end.
-
-%%-------------------------------------------------------------------------
-
--define(INFO_RESULT(Allow, Feats),
- case Allow of
- deny ->
- {error, ?ERR_FORBIDDEN};
- allow ->
- {result, Feats}
- end).
-
-disco_features(Acc, From, #jid{lserver = LServer} = _To,
- "announce", _Lang) ->
- case gen_mod:is_loaded(LServer, mod_adhoc) of
- false ->
- Acc;
- _ ->
- Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case {acl:match_rule(LServer, Access1, From),
- acl:match_rule(global, Access2, From)} of
- {deny, deny} ->
- {error, ?ERR_FORBIDDEN};
- _ ->
- {result, []}
- end
- end;
-
-disco_features(Acc, From, #jid{lserver = LServer} = _To,
- Node, _Lang) ->
- case gen_mod:is_loaded(LServer, mod_adhoc) of
- false ->
- Acc;
- _ ->
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Allow = acl:match_rule(LServer, Access, From),
- AccessGlobal = gen_mod:get_module_opt(global, ?MODULE, access, none),
- AllowGlobal = acl:match_rule(global, AccessGlobal, From),
- case Node of
- ?NS_ADMIN ++ "#announce" ->
- ?INFO_RESULT(Allow, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#announce-all" ->
- ?INFO_RESULT(Allow, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#set-motd" ->
- ?INFO_RESULT(Allow, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#edit-motd" ->
- ?INFO_RESULT(Allow, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#delete-motd" ->
- ?INFO_RESULT(Allow, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#announce-allhosts" ->
- ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#announce-all-allhosts" ->
- ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#set-motd-allhosts" ->
- ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#edit-motd-allhosts" ->
- ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
- ?NS_ADMIN ++ "#delete-motd-allhosts" ->
- ?INFO_RESULT(AllowGlobal, [?NS_COMMANDS]);
- _ ->
- Acc
- end
- end.
-
-%%-------------------------------------------------------------------------
-
--define(NODE_TO_ITEM(Lang, Server, Node),
- {xmlelement, "item",
- [{"jid", Server},
- {"node", Node},
- {"name", get_title(Lang, Node)}],
- []}).
-
--define(ITEMS_RESULT(Allow, Items),
- case Allow of
- deny ->
- {error, ?ERR_FORBIDDEN};
- allow ->
- {result, Items}
- end).
-
-disco_items(Acc, From, #jid{lserver = LServer, server = Server} = _To,
- "", Lang) ->
- case gen_mod:is_loaded(LServer, mod_adhoc) of
- false ->
- Acc;
- _ ->
- Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case {acl:match_rule(LServer, Access1, From),
- acl:match_rule(global, Access2, From)} of
- {deny, deny} ->
- Acc;
- _ ->
- Items = case Acc of
- {result, I} -> I;
- _ -> []
- end,
- Nodes = [?NODE_TO_ITEM(Lang, Server, "announce")],
- {result, Items ++ Nodes}
- end
- end;
-
-disco_items(Acc, From, #jid{lserver = LServer} = To, "announce", Lang) ->
- case gen_mod:is_loaded(LServer, mod_adhoc) of
- false ->
- Acc;
- _ ->
- announce_items(Acc, From, To, Lang)
- end;
-
-disco_items(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
- case gen_mod:is_loaded(LServer, mod_adhoc) of
- false ->
- Acc;
- _ ->
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Allow = acl:match_rule(LServer, Access, From),
- AccessGlobal = gen_mod:get_module_opt(global, ?MODULE, access, none),
- AllowGlobal = acl:match_rule(global, AccessGlobal, From),
- case Node of
- ?NS_ADMIN ++ "#announce" ->
- ?ITEMS_RESULT(Allow, []);
- ?NS_ADMIN ++ "#announce-all" ->
- ?ITEMS_RESULT(Allow, []);
- ?NS_ADMIN ++ "#set-motd" ->
- ?ITEMS_RESULT(Allow, []);
- ?NS_ADMIN ++ "#edit-motd" ->
- ?ITEMS_RESULT(Allow, []);
- ?NS_ADMIN ++ "#delete-motd" ->
- ?ITEMS_RESULT(Allow, []);
- ?NS_ADMIN ++ "#announce-allhosts" ->
- ?ITEMS_RESULT(AllowGlobal, []);
- ?NS_ADMIN ++ "#announce-all-allhosts" ->
- ?ITEMS_RESULT(AllowGlobal, []);
- ?NS_ADMIN ++ "#set-motd-allhosts" ->
- ?ITEMS_RESULT(AllowGlobal, []);
- ?NS_ADMIN ++ "#edit-motd-allhosts" ->
- ?ITEMS_RESULT(AllowGlobal, []);
- ?NS_ADMIN ++ "#delete-motd-allhosts" ->
- ?ITEMS_RESULT(AllowGlobal, []);
- _ ->
- Acc
- end
- end.
-
-%%-------------------------------------------------------------------------
-
-announce_items(Acc, From, #jid{lserver = LServer, server = Server} = _To, Lang) ->
- Access1 = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Nodes1 = case acl:match_rule(LServer, Access1, From) of
- allow ->
- [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-all"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#set-motd"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#edit-motd"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#delete-motd")];
- deny ->
- []
- end,
- Access2 = gen_mod:get_module_opt(global, ?MODULE, access, none),
- Nodes2 = case acl:match_rule(global, Access2, From) of
- allow ->
- [?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-allhosts"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#announce-all-allhosts"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#set-motd-allhosts"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#edit-motd-allhosts"),
- ?NODE_TO_ITEM(Lang, Server, ?NS_ADMIN ++ "#delete-motd-allhosts")];
- deny ->
- []
- end,
- case {Nodes1, Nodes2} of
- {[], []} ->
- Acc;
- _ ->
- Items = case Acc of
- {result, I} -> I;
- _ -> []
- end,
- {result, Items ++ Nodes1 ++ Nodes2}
- end.
-
-%%-------------------------------------------------------------------------
-
-commands_result(Allow, From, To, Request) ->
- case Allow of
- deny ->
- {error, ?ERR_FORBIDDEN};
- allow ->
- announce_commands(From, To, Request)
- end.
-
-
-announce_commands(Acc, From, #jid{lserver = LServer} = To,
- #adhoc_request{ node = Node} = Request) ->
- LNode = tokenize(Node),
- F = fun() ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- Allow = acl:match_rule(global, Access, From),
- commands_result(Allow, From, To, Request)
- end,
- R = case LNode of
- ?NS_ADMINL("announce-allhosts") -> F();
- ?NS_ADMINL("announce-all-allhosts") -> F();
- ?NS_ADMINL("set-motd-allhosts") -> F();
- ?NS_ADMINL("edit-motd-allhosts") -> F();
- ?NS_ADMINL("delete-motd-allhosts") -> F();
- _ ->
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access, none),
- Allow = acl:match_rule(LServer, Access, From),
- case LNode of
- ?NS_ADMINL("announce") ->
- commands_result(Allow, From, To, Request);
- ?NS_ADMINL("announce-all") ->
- commands_result(Allow, From, To, Request);
- ?NS_ADMINL("set-motd") ->
- commands_result(Allow, From, To, Request);
- ?NS_ADMINL("edit-motd") ->
- commands_result(Allow, From, To, Request);
- ?NS_ADMINL("delete-motd") ->
- commands_result(Allow, From, To, Request);
- _ ->
- unknown
- end
- end,
- case R of
- unknown -> Acc;
- _ -> {stop, R}
- end.
-
-%%-------------------------------------------------------------------------
-
-announce_commands(From, To,
- #adhoc_request{lang = Lang,
- node = Node,
- action = Action,
- xdata = XData} = Request) ->
- %% If the "action" attribute is not present, it is
- %% understood as "execute". If there was no <actions/>
- %% element in the first response (which there isn't in our
- %% case), "execute" and "complete" are equivalent.
- ActionIsExecute = lists:member(Action,
- ["", "execute", "complete"]),
- if Action == "cancel" ->
- %% User cancels request
- adhoc:produce_response(Request,
- #adhoc_response{status = canceled});
- XData == false, ActionIsExecute ->
- %% User requests form
- Elements = generate_adhoc_form(Lang, Node, To#jid.lserver),
- adhoc:produce_response(
- Request,
- #adhoc_response{status = executing,
- elements = [Elements]});
- XData /= false, ActionIsExecute ->
- %% User returns form.
- case jlib:parse_xdata_submit(XData) of
- invalid ->
- {error, ?ERR_BAD_REQUEST};
- Fields ->
- handle_adhoc_form(From, To, Request, Fields)
- end;
- true ->
- {error, ?ERR_BAD_REQUEST}
- end.
-
--define(VVALUE(Val),
- {xmlelement, "value", [], [{xmlcdata, Val}]}).
--define(TVFIELD(Type, Var, Val),
- {xmlelement, "field", [{"type", Type},
- {"var", Var}],
- vvaluel(Val)}).
--define(HFIELD(), ?TVFIELD("hidden", "FORM_TYPE", ?NS_ADMIN)).
-
-vvaluel(Val) ->
- case Val of
- "" -> [];
- _ -> [?VVALUE(Val)]
- end.
-
-generate_adhoc_form(Lang, Node, ServerHost) ->
- LNode = tokenize(Node),
- {OldSubject, OldBody} = if (LNode == ?NS_ADMINL("edit-motd"))
- or (LNode == ?NS_ADMINL("edit-motd-allhosts")) ->
- get_stored_motd(ServerHost);
- true ->
- {[], []}
- end,
- {xmlelement, "x",
- [{"xmlns", ?NS_XDATA},
- {"type", "form"}],
- [?HFIELD(),
- {xmlelement, "title", [], [{xmlcdata, get_title(Lang, Node)}]}]
- ++
- if (LNode == ?NS_ADMINL("delete-motd"))
- or (LNode == ?NS_ADMINL("delete-motd-allhosts")) ->
- [{xmlelement, "field",
- [{"var", "confirm"},
- {"type", "boolean"},
- {"label", translate:translate(Lang, "Really delete message of the day?")}],
- [{xmlelement, "value",
- [],
- [{xmlcdata, "true"}]}]}];
- true ->
- [{xmlelement, "field",
- [{"var", "subject"},
- {"type", "text-single"},
- {"label", translate:translate(Lang, "Subject")}],
- vvaluel(OldSubject)},
- {xmlelement, "field",
- [{"var", "body"},
- {"type", "text-multi"},
- {"label", translate:translate(Lang, "Message body")}],
- vvaluel(OldBody)}]
- end}.
-
-join_lines([]) ->
- [];
-join_lines(Lines) ->
- join_lines(Lines, []).
-join_lines([Line|Lines], Acc) ->
- join_lines(Lines, ["\n",Line|Acc]);
-join_lines([], Acc) ->
- %% Remove last newline
- lists:flatten(lists:reverse(tl(Acc))).
-
-handle_adhoc_form(From, #jid{lserver = LServer} = To,
- #adhoc_request{lang = Lang,
- node = Node,
- sessionid = SessionID},
- Fields) ->
- Confirm = case lists:keysearch("confirm", 1, Fields) of
- {value, {"confirm", ["true"]}} ->
- true;
- {value, {"confirm", ["1"]}} ->
- true;
- _ ->
- false
- end,
- Subject = case lists:keysearch("subject", 1, Fields) of
- {value, {"subject", SubjectLines}} ->
- %% There really shouldn't be more than one
- %% subject line, but can we stop them?
- join_lines(SubjectLines);
- _ ->
- []
- end,
- Body = case lists:keysearch("body", 1, Fields) of
- {value, {"body", BodyLines}} ->
- join_lines(BodyLines);
- _ ->
- []
- end,
- Response = #adhoc_response{lang = Lang,
- node = Node,
- sessionid = SessionID,
- status = completed},
- Packet = {xmlelement, "message", [{"type", "normal"}],
- if Subject /= [] ->
- [{xmlelement, "subject", [],
- [{xmlcdata, Subject}]}];
- true ->
- []
- end ++
- if Body /= [] ->
- [{xmlelement, "body", [],
- [{xmlcdata, Body}]}];
- true ->
- []
- end},
-
- Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
- case {Node, Body} of
- {?NS_ADMIN ++ "#delete-motd", _} ->
- if Confirm ->
- Proc ! {announce_motd_delete, From, To, Packet},
- adhoc:produce_response(Response);
- true ->
- adhoc:produce_response(Response)
- end;
- {?NS_ADMIN ++ "#delete-motd-allhosts", _} ->
- if Confirm ->
- Proc ! {announce_all_hosts_motd_delete, From, To, Packet},
- adhoc:produce_response(Response);
- true ->
- adhoc:produce_response(Response)
- end;
- {_, []} ->
- %% An announce message with no body is definitely an operator error.
- %% Throw an error and give him/her a chance to send message again.
- {error, ?ERRT_NOT_ACCEPTABLE(
- Lang,
- "No body provided for announce message")};
- %% Now send the packet to ?PROCNAME.
- %% We don't use direct announce_* functions because it
- %% leads to large delay in response and <iq/> queries processing
- {?NS_ADMIN ++ "#announce", _} ->
- Proc ! {announce_online, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#announce-allhosts", _} ->
- Proc ! {announce_all_hosts_online, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#announce-all", _} ->
- Proc ! {announce_all, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#announce-all-allhosts", _} ->
- Proc ! {announce_all_hosts_all, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#set-motd", _} ->
- Proc ! {announce_motd, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#set-motd-allhosts", _} ->
- Proc ! {announce_all_hosts_motd, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#edit-motd", _} ->
- Proc ! {announce_motd_update, From, To, Packet},
- adhoc:produce_response(Response);
- {?NS_ADMIN ++ "#edit-motd-allhosts", _} ->
- Proc ! {announce_all_hosts_motd_update, From, To, Packet},
- adhoc:produce_response(Response);
- _ ->
- %% This can't happen, as we haven't registered any other
- %% command nodes.
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-get_title(Lang, "announce") ->
- translate:translate(Lang, "Announcements");
-get_title(Lang, ?NS_ADMIN ++ "#announce-all") ->
- translate:translate(Lang, "Send announcement to all users");
-get_title(Lang, ?NS_ADMIN ++ "#announce-all-allhosts") ->
- translate:translate(Lang, "Send announcement to all users on all hosts");
-get_title(Lang, ?NS_ADMIN ++ "#announce") ->
- translate:translate(Lang, "Send announcement to all online users");
-get_title(Lang, ?NS_ADMIN ++ "#announce-allhosts") ->
- translate:translate(Lang, "Send announcement to all online users on all hosts");
-get_title(Lang, ?NS_ADMIN ++ "#set-motd") ->
- translate:translate(Lang, "Set message of the day and send to online users");
-get_title(Lang, ?NS_ADMIN ++ "#set-motd-allhosts") ->
- translate:translate(Lang, "Set message of the day on all hosts and send to online users");
-get_title(Lang, ?NS_ADMIN ++ "#edit-motd") ->
- translate:translate(Lang, "Update message of the day (don't send)");
-get_title(Lang, ?NS_ADMIN ++ "#edit-motd-allhosts") ->
- translate:translate(Lang, "Update message of the day on all hosts (don't send)");
-get_title(Lang, ?NS_ADMIN ++ "#delete-motd") ->
- translate:translate(Lang, "Delete message of the day");
-get_title(Lang, ?NS_ADMIN ++ "#delete-motd-allhosts") ->
- translate:translate(Lang, "Delete message of the day on all hosts").
-
-%%-------------------------------------------------------------------------
-
-announce_all(From, To, Packet) ->
- Host = To#jid.lserver,
- Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
- case acl:match_rule(Host, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- Local = jlib:make_jid("", To#jid.server, ""),
- lists:foreach(
- fun({User, Server}) ->
- Dest = jlib:make_jid(User, Server, ""),
- ejabberd_router:route(Local, Dest, Packet)
- end, ejabberd_auth:get_vh_registered_users(Host))
- end.
-
-announce_all_hosts_all(From, To, Packet) ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case acl:match_rule(global, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- Local = jlib:make_jid("", To#jid.server, ""),
- lists:foreach(
- fun({User, Server}) ->
- Dest = jlib:make_jid(User, Server, ""),
- ejabberd_router:route(Local, Dest, Packet)
- end, ejabberd_auth:dirty_get_registered_users())
- end.
-
-announce_online(From, To, Packet) ->
- Host = To#jid.lserver,
- Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
- case acl:match_rule(Host, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- announce_online1(ejabberd_sm:get_vh_session_list(Host),
- To#jid.server,
- Packet)
- end.
-
-announce_all_hosts_online(From, To, Packet) ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case acl:match_rule(global, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- announce_online1(ejabberd_sm:dirty_get_sessions_list(),
- To#jid.server,
- Packet)
- end.
-
-announce_online1(Sessions, Server, Packet) ->
- Local = jlib:make_jid("", Server, ""),
- lists:foreach(
- fun({U, S, R}) ->
- Dest = jlib:make_jid(U, S, R),
- ejabberd_router:route(Local, Dest, Packet)
- end, Sessions).
-
-announce_motd(From, To, Packet) ->
- Host = To#jid.lserver,
- Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
- case acl:match_rule(Host, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- announce_motd(Host, Packet)
- end.
-
-announce_all_hosts_motd(From, To, Packet) ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case acl:match_rule(global, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- Hosts = ?MYHOSTS,
- [announce_motd(Host, Packet) || Host <- Hosts]
- end.
-
-announce_motd(Host, Packet) ->
- announce_motd_update(Host, Packet),
- Sessions = ejabberd_sm:get_vh_session_list(Host),
- announce_online1(Sessions, Host, Packet),
- F = fun() ->
- lists:foreach(
- fun({U, _S, _R}) ->
- Username = ejabberd_odbc:escape(U),
- odbc_queries:update_t(
- "motd",
- ["username", "xml"],
- [Username, ""],
- ["username='", Username, "'"])
- end, Sessions)
- end,
- LServer = jlib:nameprep(Host),
- ejabberd_odbc:sql_transaction(LServer, F).
-
-announce_motd_update(From, To, Packet) ->
- Host = To#jid.lserver,
- Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
- case acl:match_rule(Host, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- announce_motd_update(Host, Packet)
- end.
-
-announce_all_hosts_motd_update(From, To, Packet) ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case acl:match_rule(global, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- Hosts = ?MYHOSTS,
- [announce_motd_update(Host, Packet) || Host <- Hosts]
- end.
-
-announce_motd_update(LServer, Packet) ->
- announce_motd_delete(LServer),
- XML = ejabberd_odbc:escape(xml:element_to_binary(Packet)),
- F = fun() ->
- odbc_queries:update_t(
- "motd",
- ["username", "xml"],
- ["", XML],
- ["username=''"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-announce_motd_delete(From, To, Packet) ->
- Host = To#jid.lserver,
- Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
- case acl:match_rule(Host, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- announce_motd_delete(Host)
- end.
-
-announce_all_hosts_motd_delete(From, To, Packet) ->
- Access = gen_mod:get_module_opt(global, ?MODULE, access, none),
- case acl:match_rule(global, Access, From) of
- deny ->
- Err = jlib:make_error_reply(Packet, ?ERR_FORBIDDEN),
- ejabberd_router:route(To, From, Err);
- allow ->
- Hosts = ?MYHOSTS,
- [announce_motd_delete(Host) || Host <- Hosts]
- end.
-
-announce_motd_delete(LServer) ->
- F = fun() ->
- ejabberd_odbc:sql_query_t(["delete from motd;"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-send_motd(#jid{luser = LUser, lserver = LServer} = JID) when LUser /= "" ->
- case catch ejabberd_odbc:sql_query(
- LServer, ["select xml from motd where username='';"]) of
- {selected, ["xml"], [{XML}]} ->
- case xml_stream:parse_element(XML) of
- {error, _} ->
- ok;
- Packet ->
- Username = ejabberd_odbc:escape(LUser),
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select username from motd "
- "where username='", Username, "';"]) of
- {selected, ["username"], []} ->
- Local = jlib:make_jid("", LServer, ""),
- ejabberd_router:route(Local, JID, Packet),
- F = fun() ->
- odbc_queries:update_t(
- "motd",
- ["username", "xml"],
- [Username, ""],
- ["username='", Username, "'"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F);
- _ ->
- ok
- end
- end;
- _ ->
- ok
- end;
-send_motd(_) ->
- ok.
-
-get_stored_motd(LServer) ->
- case catch ejabberd_odbc:sql_query(
- LServer, ["select xml from motd where username='';"]) of
- {selected, ["xml"], [{XML}]} ->
- case xml_stream:parse_element(XML) of
- {error, _} ->
- {"", ""};
- Packet ->
- {xml:get_subtag_cdata(Packet, "subject"),
- xml:get_subtag_cdata(Packet, "body")}
- end;
- _ ->
- {"", ""}
- end.
-
-%% This function is similar to others, but doesn't perform any ACL verification
-send_announcement_to_all(Host, SubjectS, BodyS) ->
- SubjectEls = if SubjectS /= [] ->
- [{xmlelement, "subject", [], [{xmlcdata, SubjectS}]}];
- true ->
- []
- end,
- BodyEls = if BodyS /= [] ->
- [{xmlelement, "body", [], [{xmlcdata, BodyS}]}];
- true ->
- []
- end,
- Packet = {xmlelement, "message", [{"type", "normal"}], SubjectEls ++ BodyEls},
- Sessions = ejabberd_sm:dirty_get_sessions_list(),
- Local = jlib:make_jid("", Host, ""),
- lists:foreach(
- fun({U, S, R}) ->
- Dest = jlib:make_jid(U, S, R),
- ejabberd_router:route(Local, Dest, Packet)
- end, Sessions).
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index e099291b9..7cb0f5c82 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -92,16 +92,6 @@ process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
process_iq_set(Acc, _, _, _) ->
Acc.
-is_list_needdb(Items) ->
- lists:any(
- fun(X) ->
- case X#listitem.type of
- subscription -> true;
- group -> true;
- _ -> false
- end
- end, Items).
-
list_to_blocklist_jids([], JIDs) ->
JIDs;
@@ -148,6 +138,35 @@ parse_blocklist_items([_ | Els], JIDs) ->
parse_blocklist_items(Els, JIDs).
process_blocklist_block(LUser, LServer, JIDs) ->
+ Filter = fun(List) ->
+ AlreadyBlocked = list_to_blocklist_jids(List, []),
+ lists:foldr(
+ fun(JID, List1) ->
+ case lists:member(JID, AlreadyBlocked) of
+ true ->
+ List1;
+ false ->
+ [#listitem{type = jid,
+ value = JID,
+ action = deny,
+ order = 0,
+ match_all = true}
+ | List1]
+ end
+ end, List, JIDs)
+ end,
+ case process_blocklist_block(LUser, LServer, Filter,
+ gen_mod:db_type(LServer, mod_privacy)) of
+ {atomic, {ok, Default, List}} ->
+ UserList = make_userlist(Default, List),
+ broadcast_list_update(LUser, LServer, Default, UserList),
+ broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
+ {result, [], UserList};
+ _ ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR}
+ end.
+
+process_blocklist_block(LUser, LServer, Filter, mnesia) ->
F =
fun() ->
case mnesia:wread({privacy, {LUser, LServer}}) of
@@ -173,40 +192,72 @@ process_blocklist_block(LUser, LServer, JIDs) ->
List = []
end
end,
-
- AlreadyBlocked = list_to_blocklist_jids(List, []),
- NewList =
- lists:foldr(fun(JID, List1) ->
- case lists:member(JID, AlreadyBlocked) of
- true ->
- List1;
- false ->
- [#listitem{type = jid,
- value = JID,
- action = deny,
- order = 0,
- match_all = true
- } | List1]
- end
- end, List, JIDs),
+ NewList = Filter(List),
NewLists = [{NewDefault, NewList} | NewLists1],
mnesia:write(P#privacy{default = NewDefault,
lists = NewLists}),
{ok, NewDefault, NewList}
end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
+ mnesia:transaction(F);
+process_blocklist_block(LUser, LServer, Filter, odbc) ->
+ F = fun() ->
+ Default =
+ case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+ {selected, ["name"], []} ->
+ Name = "Blocked contacts",
+ mod_privacy:sql_add_privacy_list(LUser, Name),
+ mod_privacy:sql_set_default_privacy_list(
+ LUser, Name),
+ Name;
+ {selected, ["name"], [{Name}]} ->
+ Name
+ end,
+ {selected, ["id"], [{ID}]} =
+ mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
+ case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+ {selected,
+ ["t", "value", "action", "ord",
+ "match_all", "match_iq", "match_message",
+ "match_presence_in",
+ "match_presence_out"],
+ RItems = [_|_]} ->
+ List = lists:map(
+ fun mod_privacy:raw_to_item/1,
+ RItems);
+ _ ->
+ List = []
+ end,
+ NewList = Filter(List),
+ NewRItems = lists:map(
+ fun mod_privacy:item_to_raw/1,
+ NewList),
+ mod_privacy:sql_set_privacy_list(
+ ID, NewRItems),
+ {ok, Default, NewList}
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
+
+process_blocklist_unblock_all(LUser, LServer) ->
+ Filter = fun(List) ->
+ lists:filter(
+ fun(#listitem{action = A}) ->
+ A =/= deny
+ end, List)
+ end,
+ case process_blocklist_unblock_all(
+ LUser, LServer, Filter, gen_mod:db_type(LServer, mod_privacy)) of
+ {atomic, ok} ->
+ {result, []};
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
+ broadcast_blocklist_event(LUser, LServer, unblock_all),
{result, [], UserList};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
-process_blocklist_unblock_all(LUser, LServer) ->
+process_blocklist_unblock_all(LUser, LServer, Filter, mnesia) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
@@ -218,12 +269,7 @@ process_blocklist_unblock_all(LUser, LServer) ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove all deny items
- NewList =
- lists:filter(
- fun(#listitem{action = A}) ->
- A =/= deny
- end, List),
-
+ NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
@@ -235,21 +281,67 @@ process_blocklist_unblock_all(LUser, LServer) ->
end
end
end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, ok} ->
+ mnesia:transaction(F);
+process_blocklist_unblock_all(LUser, LServer, Filter, odbc) ->
+ F = fun() ->
+ case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+ {selected, ["name"], []} ->
+ ok;
+ {selected, ["name"], [{Default}]} ->
+ {selected, ["id"], [{ID}]} =
+ mod_privacy:sql_get_privacy_list_id_t(
+ LUser, Default),
+ case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+ {selected,
+ ["t", "value", "action", "ord",
+ "match_all", "match_iq", "match_message",
+ "match_presence_in",
+ "match_presence_out"],
+ RItems = [_|_]} ->
+ List = lists:map(
+ fun mod_privacy:raw_to_item/1,
+ RItems),
+ NewList = Filter(List),
+ NewRItems = lists:map(
+ fun mod_privacy:item_to_raw/1,
+ NewList),
+ mod_privacy:sql_set_privacy_list(
+ ID, NewRItems),
+ {ok, Default, NewList};
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
+
+process_blocklist_unblock(LUser, LServer, JIDs) ->
+ Filter = fun(List) ->
+ lists:filter(
+ fun(#listitem{action = deny,
+ type = jid,
+ value = JID}) ->
+ not(lists:member(JID, JIDs));
+ (_) ->
+ true
+ end, List)
+ end,
+ case process_blocklist_unblock(LUser, LServer, Filter,
+ gen_mod:db_type(LServer, mod_privacy)) of
+ {atomic, ok} ->
{result, []};
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, unblock_all),
+ broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
{result, [], UserList};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
-process_blocklist_unblock(LUser, LServer, JIDs) ->
+process_blocklist_unblock(LUser, LServer, Filter, mnesia) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
@@ -261,16 +353,7 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove matching deny items
- NewList =
- lists:filter(
- fun(#listitem{action = deny,
- type = jid,
- value = JID}) ->
- not(lists:member(JID, JIDs));
- (_) ->
- true
- end, List),
-
+ NewList = Filter(List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
@@ -282,22 +365,44 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
end
end
end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, ok} ->
- {result, []};
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
- {result, [], UserList};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
+ mnesia:transaction(F);
+process_blocklist_unblock(LUser, LServer, Filter, odbc) ->
+ F = fun() ->
+ case mod_privacy:sql_get_default_privacy_list_t(LUser) of
+ {selected, ["name"], []} ->
+ ok;
+ {selected, ["name"], [{Default}]} ->
+ {selected, ["id"], [{ID}]} =
+ mod_privacy:sql_get_privacy_list_id_t(
+ LUser, Default),
+ case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
+ {selected,
+ ["t", "value", "action", "ord",
+ "match_all", "match_iq", "match_message",
+ "match_presence_in",
+ "match_presence_out"],
+ RItems = [_|_]} ->
+ List = lists:map(
+ fun mod_privacy:raw_to_item/1,
+ RItems),
+ NewList = Filter(List),
+ NewRItems = lists:map(
+ fun mod_privacy:item_to_raw/1,
+ NewList),
+ mod_privacy:sql_set_privacy_list(
+ ID, NewRItems),
+ {ok, Default, NewList};
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
make_userlist(Name, List) ->
- NeedDb = is_list_needdb(List),
+ NeedDb = mod_privacy:is_list_needdb(List),
#userlist{name = Name, list = List, needdb = NeedDb}.
broadcast_list_update(LUser, LServer, Name, UserList) ->
@@ -315,25 +420,52 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
[{blocking, Event}]}).
process_blocklist_get(LUser, LServer) ->
+ case process_blocklist_get(
+ LUser, LServer, gen_mod:db_type(LServer, mod_privacy)) of
+ error ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ List ->
+ JIDs = list_to_blocklist_jids(List, []),
+ Items = lists:map(
+ fun(JID) ->
+ ?DEBUG("JID: ~p",[JID]),
+ {xmlelement, "item",
+ [{"jid", jlib:jid_to_string(JID)}], []}
+ end, JIDs),
+ {result,
+ [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
+ Items}]}
+ end.
+
+process_blocklist_get(LUser, LServer, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
+ error;
[] ->
- {result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]};
+ [];
[#privacy{default = Default, lists = Lists}] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
- JIDs = list_to_blocklist_jids(List, []),
- Items = lists:map(
- fun(JID) ->
- ?DEBUG("JID: ~p",[JID]),
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(JID)}], []}
- end, JIDs),
- {result,
- [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
- Items}]};
+ List;
_ ->
- {result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]}
+ []
end
+ end;
+process_blocklist_get(LUser, LServer, odbc) ->
+ case catch mod_privacy:sql_get_default_privacy_list(LUser, LServer) of
+ {selected, ["name"], []} ->
+ [];
+ {selected, ["name"], [{Default}]} ->
+ case catch mod_privacy:sql_get_privacy_list_data(
+ LUser, LServer, Default) of
+ {selected, ["t", "value", "action", "ord", "match_all",
+ "match_iq", "match_message",
+ "match_presence_in", "match_presence_out"],
+ RItems} ->
+ lists:map(fun mod_privacy:raw_to_item/1, RItems);
+ {'EXIT', _} ->
+ error
+ end;
+ {'EXIT', _} ->
+ error
end.
diff --git a/src/mod_blocking_odbc.erl b/src/mod_blocking_odbc.erl
deleted file mode 100644
index 016e7945e..000000000
--- a/src/mod_blocking_odbc.erl
+++ /dev/null
@@ -1,365 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_blocking_odbc.erl
-%%% Author : Stephan Maka
-%%% Purpose : XEP-0191: Simple Communications Blocking
-%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_blocking_odbc).
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
- process_iq/3,
- process_iq_set/4,
- process_iq_get/5]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- ejabberd_hooks:add(privacy_iq_get, Host,
- ?MODULE, process_iq_get, 40),
- ejabberd_hooks:add(privacy_iq_set, Host,
- ?MODULE, process_iq_set, 40),
- mod_disco:register_feature(Host, ?NS_BLOCKING),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
- ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
- ejabberd_hooks:delete(privacy_iq_get, Host,
- ?MODULE, process_iq_get, 40),
- ejabberd_hooks:delete(privacy_iq_set, Host,
- ?MODULE, process_iq_set, 40),
- mod_disco:unregister_feature(Host, ?NS_BLOCKING),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
-
-process_iq(_From, _To, IQ) ->
- SubEl = IQ#iq.sub_el,
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
-
-process_iq_get(_, From, _To,
- #iq{xmlns = ?NS_BLOCKING,
- sub_el = {xmlelement, "blocklist", _, _}},
- _) ->
- #jid{luser = LUser, lserver = LServer} = From,
- {stop, process_blocklist_get(LUser, LServer)};
-
-process_iq_get(Acc, _, _, _, _) ->
- Acc.
-
-process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
- sub_el = {xmlelement, SubElName, _, SubEls}}) ->
- #jid{luser = LUser, lserver = LServer} = From,
- Res =
- case {SubElName, xml:remove_cdata(SubEls)} of
- {"block", []} ->
- {error, ?ERR_BAD_REQUEST};
- {"block", Els} ->
- JIDs = parse_blocklist_items(Els, []),
- process_blocklist_block(LUser, LServer, JIDs);
- {"unblock", []} ->
- process_blocklist_unblock_all(LUser, LServer);
- {"unblock", Els} ->
- JIDs = parse_blocklist_items(Els, []),
- process_blocklist_unblock(LUser, LServer, JIDs);
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end,
- {stop, Res};
-
-process_iq_set(Acc, _, _, _) ->
- Acc.
-
-is_list_needdb(Items) ->
- lists:any(
- fun(X) ->
- case X#listitem.type of
- subscription -> true;
- group -> true;
- _ -> false
- end
- end, Items).
-
-list_to_blocklist_jids([], JIDs) ->
- JIDs;
-
-list_to_blocklist_jids([#listitem{type = jid,
- action = deny,
- value = JID} = Item | Items], JIDs) ->
- case Item of
- #listitem{match_all = true} ->
- Match = true;
- #listitem{match_iq = true,
- match_message = true,
- match_presence_in = true,
- match_presence_out = true} ->
- Match = true;
- _ ->
- Match = false
- end,
- if
- Match ->
- list_to_blocklist_jids(Items, [JID | JIDs]);
- true ->
- list_to_blocklist_jids(Items, JIDs)
- end;
-
-% Skip Privacy List items than cannot be mapped to Blocking items
-list_to_blocklist_jids([_ | Items], JIDs) ->
- list_to_blocklist_jids(Items, JIDs).
-
-parse_blocklist_items([], JIDs) ->
- JIDs;
-
-parse_blocklist_items([{xmlelement, "item", Attrs, _} | Els], JIDs) ->
- case xml:get_attr("jid", Attrs) of
- {value, JID1} ->
- JID = jlib:jid_tolower(jlib:string_to_jid(JID1)),
- parse_blocklist_items(Els, [JID | JIDs]);
- false ->
- % Tolerate missing jid attribute
- parse_blocklist_items(Els, JIDs)
- end;
-
-parse_blocklist_items([_ | Els], JIDs) ->
- % Tolerate unknown elements
- parse_blocklist_items(Els, JIDs).
-
-process_blocklist_block(LUser, LServer, JIDs) ->
- F = fun() ->
- Default =
- case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
- {selected, ["name"], []} ->
- Name = "Blocked contacts",
- mod_privacy_odbc:sql_add_privacy_list(LUser, Name),
- mod_privacy_odbc:sql_set_default_privacy_list(
- LUser, Name),
- Name;
- {selected, ["name"], [{Name}]} ->
- Name
- end,
- {selected, ["id"], [{ID}]} =
- mod_privacy_odbc:sql_get_privacy_list_id_t(LUser, Default),
- case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
- {selected,
- ["t", "value", "action", "ord",
- "match_all", "match_iq", "match_message",
- "match_presence_in",
- "match_presence_out"],
- RItems = [_|_]} ->
- List = lists:map(
- fun mod_privacy_odbc:raw_to_item/1,
- RItems);
- _ ->
- List = []
- end,
- AlreadyBlocked = list_to_blocklist_jids(List, []),
- NewList =
- lists:foldr(
- fun(JID, List1) ->
- case lists:member(JID, AlreadyBlocked) of
- true ->
- List1;
- false ->
- [#listitem{type = jid,
- value = JID,
- action = deny,
- order = 0,
- match_all = true
- } | List1]
- end
- end, List, JIDs),
- NewRItems = lists:map(
- fun mod_privacy_odbc:item_to_raw/1,
- NewList),
- mod_privacy_odbc:sql_set_privacy_list(
- ID, NewRItems),
- {ok, Default, NewList}
- end,
- case ejabberd_odbc:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
- {result, [], UserList};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-process_blocklist_unblock_all(LUser, LServer) ->
- F = fun() ->
- case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
- {selected, ["name"], []} ->
- ok;
- {selected, ["name"], [{Default}]} ->
- {selected, ["id"], [{ID}]} =
- mod_privacy_odbc:sql_get_privacy_list_id_t(
- LUser, Default),
- case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
- {selected,
- ["t", "value", "action", "ord",
- "match_all", "match_iq", "match_message",
- "match_presence_in",
- "match_presence_out"],
- RItems = [_|_]} ->
- List = lists:map(
- fun mod_privacy_odbc:raw_to_item/1,
- RItems),
- NewList =
- lists:filter(
- fun(#listitem{action = A}) ->
- A =/= deny
- end, List),
- NewRItems = lists:map(
- fun mod_privacy_odbc:item_to_raw/1,
- NewList),
- mod_privacy_odbc:sql_set_privacy_list(
- ID, NewRItems),
- {ok, Default, NewList};
- _ ->
- ok
- end;
- _ ->
- ok
- end
- end,
- case ejabberd_odbc:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, ok} ->
- {result, []};
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, unblock_all),
- {result, [], UserList};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-process_blocklist_unblock(LUser, LServer, JIDs) ->
- F = fun() ->
- case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser) of
- {selected, ["name"], []} ->
- ok;
- {selected, ["name"], [{Default}]} ->
- {selected, ["id"], [{ID}]} =
- mod_privacy_odbc:sql_get_privacy_list_id_t(
- LUser, Default),
- case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID) of
- {selected,
- ["t", "value", "action", "ord",
- "match_all", "match_iq", "match_message",
- "match_presence_in",
- "match_presence_out"],
- RItems = [_|_]} ->
- List = lists:map(
- fun mod_privacy_odbc:raw_to_item/1,
- RItems),
- NewList =
- lists:filter(
- fun(#listitem{action = deny,
- type = jid,
- value = JID}) ->
- not(lists:member(JID, JIDs));
- (_) ->
- true
- end, List),
- NewRItems = lists:map(
- fun mod_privacy_odbc:item_to_raw/1,
- NewList),
- mod_privacy_odbc:sql_set_privacy_list(
- ID, NewRItems),
- {ok, Default, NewList};
- _ ->
- ok
- end;
- _ ->
- ok
- end
- end,
- case ejabberd_odbc:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, ok} ->
- {result, []};
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, Default, UserList),
- broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
- {result, [], UserList};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-make_userlist(Name, List) ->
- NeedDb = is_list_needdb(List),
- #userlist{name = Name, list = List, needdb = NeedDb}.
-
-broadcast_list_update(LUser, LServer, Name, UserList) ->
- ejabberd_router:route(
- jlib:make_jid(LUser, LServer, ""),
- jlib:make_jid(LUser, LServer, ""),
- {xmlelement, "broadcast", [],
- [{privacy_list, UserList, Name}]}).
-
-broadcast_blocklist_event(LUser, LServer, Event) ->
- JID = jlib:make_jid(LUser, LServer, ""),
- ejabberd_router:route(
- JID, JID,
- {xmlelement, "broadcast", [],
- [{blocking, Event}]}).
-
-process_blocklist_get(LUser, LServer) ->
- case catch mod_privacy_odbc:sql_get_default_privacy_list(LUser, LServer) of
- {selected, ["name"], []} ->
- {result, [{xmlelement, "blocklist",
- [{"xmlns", ?NS_BLOCKING}], []}]};
- {selected, ["name"], [{Default}]} ->
- case catch mod_privacy_odbc:sql_get_privacy_list_data(
- LUser, LServer, Default) of
- {selected, ["t", "value", "action", "ord", "match_all",
- "match_iq", "match_message",
- "match_presence_in", "match_presence_out"],
- RItems} ->
- List = lists:map(fun mod_privacy_odbc:raw_to_item/1, RItems),
- JIDs = list_to_blocklist_jids(List, []),
- Items = lists:map(
- fun(JID) ->
- ?DEBUG("JID: ~p",[JID]),
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string(JID)}], []}
- end, JIDs),
- {result,
- [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
- Items}]};
- {'EXIT', _} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
- {'EXIT', _} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 29b7276ca..44fee1e93 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -1782,15 +1782,13 @@ stop_node(From, Host, ENode, Action, XData) ->
get_last_info(User, Server) ->
- ML = lists:member(mod_last, gen_mod:loaded_modules(Server)),
- MLO = lists:member(mod_last_odbc, gen_mod:loaded_modules(Server)),
- case {ML, MLO} of
- {true, _} -> mod_last:get_last_info(User, Server);
- {false, true} -> mod_last_odbc:get_last_info(User, Server);
- {false, false} -> not_found
+ case gen_mod:is_loaded(Server, mod_last) of
+ true ->
+ mod_last:get_last_info(User, Server);
+ false ->
+ not_found
end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
adhoc_sm_commands(_Acc, From,
diff --git a/src/mod_irc/Makefile.win32 b/src/mod_irc/Makefile.win32
index 70b71e7d6..fb0671104 100644
--- a/src/mod_irc/Makefile.win32
+++ b/src/mod_irc/Makefile.win32
@@ -4,7 +4,7 @@ include ..\Makefile.inc
EFLAGS = -I .. -pz ..
OUTDIR = ..
-BEAMS = ..\iconv.beam ..\mod_irc.beam ..\mod_irc_odbc.beam ..\mod_irc_connection.beam
+BEAMS = ..\iconv.beam ..\mod_irc.beam ..\mod_irc_connection.beam
SOURCE = iconv_erl.c
OBJECT = iconv_erl.o
@@ -25,9 +25,6 @@ $(OUTDIR)\iconv.beam : iconv.erl
$(OUTDIR)\mod_irc.beam : mod_irc.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc.erl
-$(OUTDIR)\mod_irc_odbc.beam : mod_irc_odbc.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_odbc.erl
-
$(OUTDIR)\mod_irc_connection.beam : mod_irc_connection.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl
diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl
index f616a92fd..12c291944 100644
--- a/src/mod_irc/mod_irc.erl
+++ b/src/mod_irc/mod_irc.erl
@@ -98,11 +98,17 @@ stop(Host) ->
%%--------------------------------------------------------------------
init([Host, Opts]) ->
iconv:start(),
- mnesia:create_table(irc_custom,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, irc_custom)}]),
MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
- update_table(MyHost),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(irc_custom,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, irc_custom)}]),
+ update_table(MyHost);
+ _ ->
+ ok
+ end,
Access = gen_mod:get_opt(access, Opts, all),
catch ets:new(irc_connection, [named_table,
public,
@@ -218,7 +224,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
Info = ejabberd_hooks:run_fold(
disco_info, ServerHost, [],
[ServerHost, ?MODULE, "", ""]),
- case iq_disco(Node, Lang) of
+ case iq_disco(ServerHost, Node, Lang) of
[] ->
Res = IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -263,7 +269,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS},
{"node", Node}],
- command_items(Host, Lang)}]},
+ command_items(ServerHost,
+ Host, Lang)}]},
Res = jlib:iq_to_xml(ResIQ);
_ ->
Res = jlib:make_error_reply(
@@ -273,7 +280,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
From,
Res);
#iq{xmlns = ?NS_REGISTER} = IQ ->
- process_register(Host, From, To, IQ);
+ process_register(ServerHost, Host, From, To, IQ);
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
lang = Lang} = IQ ->
Res = IQ#iq{type = result,
@@ -287,7 +294,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
#iq{type = set, xmlns = ?NS_COMMANDS,
lang = _Lang, sub_el = SubEl} = IQ ->
Request = adhoc:parse_request(IQ),
- case lists:keysearch(Request#adhoc_request.node, 1, commands()) of
+ case lists:keysearch(Request#adhoc_request.node,
+ 1, commands(ServerHost)) of
{value, {_, _, Function}} ->
case catch Function(From, To, Request) of
{'EXIT', Reason} ->
@@ -394,7 +402,7 @@ closed_connection(Host, From, Server) ->
ets:delete(irc_connection, {From, Server, Host}).
-iq_disco([], Lang) ->
+iq_disco(_ServerHost, [], Lang) ->
[{xmlelement, "identity",
[{"category", "conference"},
{"type", "irc"},
@@ -404,8 +412,8 @@ iq_disco([], Lang) ->
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
{xmlelement, "feature", [{"var", ?NS_VCARD}], []},
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
-iq_disco(Node, Lang) ->
- case lists:keysearch(Node, 1, commands()) of
+iq_disco(ServerHost, Node, Lang) ->
+ case lists:keysearch(Node, 1, commands(ServerHost)) of
{value, {_, Name, _}} ->
[{xmlelement, "identity",
[{"category", "automation"},
@@ -428,20 +436,23 @@ iq_get_vcard(Lang) ->
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
"\nCopyright (c) 2003-2012 ProcessOne"}]}].
-command_items(Host, Lang) ->
+command_items(ServerHost, Host, Lang) ->
lists:map(fun({Node, Name, _Function})
-> {xmlelement, "item",
[{"jid", Host},
{"node", Node},
{"name", translate:translate(Lang, Name)}], []}
- end, commands()).
+ end, commands(ServerHost)).
-commands() ->
+commands(ServerHost) ->
[{"join", "Join channel", fun adhoc_join/3},
- {"register", "Configure username, encoding, port and password", fun adhoc_register/3}].
+ {"register", "Configure username, encoding, port and password",
+ fun(From, To, Request) ->
+ adhoc_register(ServerHost, From, To, Request)
+ end}].
-process_register(Host, From, To, #iq{} = IQ) ->
- case catch process_irc_register(Host, From, To, IQ) of
+process_register(ServerHost, Host, From, To, #iq{} = IQ) ->
+ case catch process_irc_register(ServerHost, Host, From, To, IQ) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]);
ResIQ ->
@@ -471,7 +482,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).
-process_irc_register(Host, From, _To,
+process_irc_register(ServerHost, Host, From, _To,
#iq{type = Type, xmlns = XMLNS,
lang = Lang, sub_el = SubEl} = IQ) ->
case Type of
@@ -497,7 +508,8 @@ process_irc_register(Host, From, _To,
xml:get_tag_attr_s("node", SubEl),
"/"),
case set_form(
- Host, From, Node, Lang, XData) of
+ ServerHost, Host, From,
+ Node, Lang, XData) of
{result, Res} ->
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -517,7 +529,7 @@ process_irc_register(Host, From, _To,
get ->
Node =
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
- case get_form(Host, From, Node, Lang) of
+ case get_form(ServerHost, Host, From, Node, Lang) of
{result, Res} ->
IQ#iq{type = result,
sub_el = [{xmlelement, "query",
@@ -530,23 +542,52 @@ process_irc_register(Host, From, _To,
end
end.
+get_data(ServerHost, Host, From) ->
+ LServer = jlib:nameprep(ServerHost),
+ get_data(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)).
-
-get_form(Host, From, [], Lang) ->
- #jid{user = User, server = Server,
- luser = LUser, lserver = LServer} = From,
+get_data(_LServer, Host, From, mnesia) ->
+ #jid{luser = LUser, lserver = LServer} = From,
US = {LUser, LServer},
+ case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
+ {'EXIT', _Reason} ->
+ error;
+ [] ->
+ empty;
+ [#irc_custom{data = Data}] ->
+ Data
+ end;
+get_data(LServer, Host, From, odbc) ->
+ SJID = ejabberd_odbc:escape(
+ jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:jid_remove_resource(From)))),
+ SHost = ejabberd_odbc:escape(Host),
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select data from irc_custom where "
+ "jid='", SJID, "' and host='", SHost, "';"]) of
+ {selected, ["data"], [{SData}]} ->
+ ejabberd_odbc:decode_term(SData);
+ {'EXIT', _} ->
+ error;
+ {selected, _, _} ->
+ empty
+ end.
+
+get_form(ServerHost, Host, From, [], Lang) ->
+ #jid{user = User, server = Server} = From,
DefaultEncoding = get_default_encoding(Host),
Customs =
- case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
- {'EXIT', _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
- [] ->
- {User, []};
- [#irc_custom{data = Data}] ->
- {xml:get_attr_s(username, Data),
- xml:get_attr_s(connections_params, Data)}
- end,
+ case get_data(ServerHost, Host, From) of
+ error ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ empty ->
+ {User, []};
+ Data ->
+ {xml:get_attr_s(username, Data),
+ xml:get_attr_s(connections_params, Data)}
+ end,
case Customs of
{error, _Error} ->
Customs;
@@ -614,15 +655,41 @@ get_form(Host, From, [], Lang) ->
]}]}
end;
-get_form(_Host, _, _, _Lang) ->
+get_form(_ServerHost, _Host, _, _, _Lang) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
+set_data(ServerHost, Host, From, Data) ->
+ LServer = jlib:nameprep(ServerHost),
+ set_data(LServer, Host, From, Data, gen_mod:db_type(LServer, ?MODULE)).
-
-set_form(Host, From, [], _Lang, XData) ->
+set_data(_LServer, Host, From, Data, mnesia) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
US = {LUser, LServer},
+ F = fun() ->
+ mnesia:write(#irc_custom{us_host = {US, Host}, data = Data})
+ end,
+ mnesia:transaction(F);
+set_data(LServer, Host, From, Data, odbc) ->
+ SJID = ejabberd_odbc:escape(
+ jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:jid_remove_resource(From)))),
+ SHost = ejabberd_odbc:escape(Host),
+ SData = ejabberd_odbc:encode_term(Data),
+ F = fun() ->
+ odbc_queries:update_t(
+ "irc_custom",
+ ["jid", "host", "data"],
+ [SJID, SHost, SData],
+ ["jid='", SJID,
+ "' and host='",
+ SHost, "'"]),
+ ok
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
+
+set_form(ServerHost, Host, From, [], _Lang, XData) ->
case {lists:keysearch("username", 1, XData),
lists:keysearch("connections_params", 1, XData)} of
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
@@ -633,17 +700,11 @@ set_form(Host, From, [], _Lang, XData) ->
{ok, Tokens, _} ->
case erl_parse:parse_term(Tokens) of
{ok, ConnectionsParams} ->
- case mnesia:transaction(
- fun() ->
- mnesia:write(
- #irc_custom{us_host =
- {US, Host},
- data =
- [{username,
- Username},
- {connections_params,
- ConnectionsParams}]})
- end) of
+ case set_data(ServerHost, Host, From,
+ [{username,
+ Username},
+ {connections_params,
+ ConnectionsParams}]) of
{atomic, _} ->
{result, []};
_ ->
@@ -660,7 +721,7 @@ set_form(Host, From, [], _Lang, XData) ->
end;
-set_form(_Host, _, _, _Lang, _XData) ->
+set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
{error, ?ERR_SERVICE_UNAVAILABLE}.
@@ -679,16 +740,14 @@ get_default_encoding(ServerHost) ->
Result.
get_connection_params(Host, ServerHost, From, IRCServer) ->
- #jid{user = User, server = _Server,
- luser = LUser, lserver = LServer} = From,
- US = {LUser, LServer},
+ #jid{user = User, server = _Server} = From,
DefaultEncoding = get_default_encoding(ServerHost),
- case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
- {'EXIT', _Reason} ->
+ case get_data(ServerHost, Host, From) of
+ error ->
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
- [] ->
+ empty ->
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
- [#irc_custom{data = Data}] ->
+ Data ->
Username = xml:get_attr_s(username, Data),
{NewUsername, NewEncoding, NewPort, NewPassword} =
case lists:keysearch(IRCServer, 1, xml:get_attr_s(connections_params, Data)) of
@@ -785,28 +844,27 @@ adhoc_join(From, To, #adhoc_request{lang = Lang,
end
end.
-adhoc_register(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
+adhoc_register(_ServerHost, _From, _To, #adhoc_request{action = "cancel"} = Request) ->
adhoc:produce_response(Request,
#adhoc_response{status = canceled});
-adhoc_register(From, To, #adhoc_request{lang = Lang,
- node = _Node,
- xdata = XData,
- action = Action} = Request) ->
- #jid{user = User, luser = LUser, lserver = LServer} = From,
+adhoc_register(ServerHost, From, To, #adhoc_request{lang = Lang,
+ node = _Node,
+ xdata = XData,
+ action = Action} = Request) ->
+ #jid{user = User} = From,
#jid{lserver = Host} = To,
- US = {LUser, LServer},
%% Generate form for setting username and encodings. If the user
%% hasn't begun to fill out the form, generate an initial form
%% based on current values.
if XData == false ->
- case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
- {'EXIT', _Reason} ->
+ case get_data(ServerHost, Host, From) of
+ error ->
Username = User,
ConnectionsParams = [];
- [] ->
+ empty ->
Username = User,
ConnectionsParams = [];
- [#irc_custom{data = Data}] ->
+ Data ->
Username = xml:get_attr_s(username, Data),
ConnectionsParams = xml:get_attr_s(connections_params, Data)
end,
@@ -832,17 +890,11 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
if Error /= false ->
Error;
Action == "complete" ->
- case mnesia:transaction(
- fun () ->
- mnesia:write(
- #irc_custom{us_host =
- {US, Host},
- data =
- [{username,
- Username},
- {connections_params,
- ConnectionsParams}]})
- end) of
+ case set_data(ServerHost, Host, From,
+ [{username,
+ Username},
+ {connections_params,
+ ConnectionsParams}]) of
{atomic, _} ->
adhoc:produce_response(Request, #adhoc_response{status = completed});
_ ->
diff --git a/src/mod_irc/mod_irc_odbc.erl b/src/mod_irc/mod_irc_odbc.erl
deleted file mode 100644
index ab0fafe14..000000000
--- a/src/mod_irc/mod_irc_odbc.erl
+++ /dev/null
@@ -1,1033 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_irc_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : IRC transport
-%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_irc_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_server).
--behaviour(gen_mod).
-
-%% API
--export([start_link/2,
- start/2,
- stop/1,
- closed_connection/3,
- get_connection_params/3]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("adhoc.hrl").
-
--define(DEFAULT_IRC_ENCODING, "iso8859-1").
--define(DEFAULT_IRC_PORT, 6667).
--define(POSSIBLE_ENCODINGS, ["koi8-r", "iso8859-1", "iso8859-2", "utf-8", "utf-8+latin-1"]).
-
--record(irc_connection, {jid_server_host, pid}).
-
--record(state, {host, server_host, access}).
-
--define(PROCNAME, ejabberd_mod_irc).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link(Host, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
-start(Host, Opts) ->
- start_supervisor(Host),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- ChildSpec =
- {Proc,
- {?MODULE, start_link, [Host, Opts]},
- temporary,
- 1000,
- worker,
- [?MODULE]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
- stop_supervisor(Host),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:call(Proc, stop),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([Host, Opts]) ->
- iconv:start(),
- MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
- Access = gen_mod:get_opt(access, Opts, all),
- catch ets:new(irc_connection, [named_table,
- public,
- {keypos, #irc_connection.jid_server_host}]),
- ejabberd_router:register_route(MyHost),
- {ok, #state{host = MyHost,
- server_host = Host,
- access = Access}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call(stop, _From, State) ->
- {stop, normal, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({route, From, To, Packet},
- #state{host = Host,
- server_host = ServerHost,
- access = Access} = State) ->
- case catch do_route(Host, ServerHost, Access, From, To, Packet) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p", [Reason]);
- _ ->
- ok
- end,
- {noreply, State};
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, State) ->
- ejabberd_router:unregister_route(State#state.host),
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-start_supervisor(Host) ->
- Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
- ChildSpec =
- {Proc,
- {ejabberd_tmp_sup, start_link,
- [Proc, mod_irc_connection]},
- permanent,
- infinity,
- supervisor,
- [ejabberd_tmp_sup]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop_supervisor(Host) ->
- Proc = gen_mod:get_module_proc(Host, ejabberd_mod_irc_sup),
- supervisor:terminate_child(ejabberd_sup, Proc),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-do_route(Host, ServerHost, Access, From, To, Packet) ->
- case acl:match_rule(ServerHost, Access, From) of
- allow ->
- do_route1(Host, ServerHost, From, To, Packet);
- _ ->
- {xmlelement, _Name, Attrs, _Els} = Packet,
- Lang = xml:get_attr_s("xml:lang", Attrs),
- ErrText = "Access denied by service policy",
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(To, From, Err)
- end.
-
-do_route1(Host, ServerHost, From, To, Packet) ->
- #jid{user = ChanServ, resource = Resource} = To,
- {xmlelement, _Name, _Attrs, _Els} = Packet,
- case ChanServ of
- "" ->
- case Resource of
- "" ->
- case jlib:iq_query_info(Packet) of
- #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
- sub_el = SubEl, lang = Lang} = IQ ->
- Node = xml:get_tag_attr_s("node", SubEl),
- Info = ejabberd_hooks:run_fold(
- disco_info, ServerHost, [],
- [ServerHost, ?MODULE, "", ""]),
- case iq_disco(ServerHost, Node, Lang) of
- [] ->
- Res = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- []}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- DiscoInfo ->
- Res = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- DiscoInfo ++ Info}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res))
- end;
- #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS,
- sub_el = SubEl, lang = Lang} = IQ ->
- Node = xml:get_tag_attr_s("node", SubEl),
- case Node of
- [] ->
- ResIQ = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- []}]},
- Res = jlib:iq_to_xml(ResIQ);
- "join" ->
- ResIQ = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- []}]},
- Res = jlib:iq_to_xml(ResIQ);
- "register" ->
- ResIQ = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- []}]},
- Res = jlib:iq_to_xml(ResIQ);
- ?NS_COMMANDS ->
- ResIQ = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS},
- {"node", Node}],
- command_items(ServerHost,
- Host, Lang)}]},
- Res = jlib:iq_to_xml(ResIQ);
- _ ->
- Res = jlib:make_error_reply(
- Packet, ?ERR_ITEM_NOT_FOUND)
- end,
- ejabberd_router:route(To,
- From,
- Res);
- #iq{xmlns = ?NS_REGISTER} = IQ ->
- process_register(ServerHost, Host, From, To, IQ);
- #iq{type = get, xmlns = ?NS_VCARD = XMLNS,
- lang = Lang} = IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [{xmlelement, "vCard",
- [{"xmlns", XMLNS}],
- iq_get_vcard(Lang)}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- #iq{type = set, xmlns = ?NS_COMMANDS,
- lang = _Lang, sub_el = SubEl} = IQ ->
- Request = adhoc:parse_request(IQ),
- case lists:keysearch(Request#adhoc_request.node,
- 1, commands(ServerHost)) of
- {value, {_, _, Function}} ->
- case catch Function(From, To, Request) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p~nfor ad-hoc handler of ~p",
- [Reason, {From, To, IQ}]),
- Res = IQ#iq{type = error, sub_el = [SubEl,
- ?ERR_INTERNAL_SERVER_ERROR]};
- ignore ->
- Res = ignore;
- {error, Error} ->
- Res = IQ#iq{type = error, sub_el = [SubEl, Error]};
- Command ->
- Res = IQ#iq{type = result, sub_el = [Command]}
- end,
- if Res /= ignore ->
- ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
- true ->
- ok
- end;
- _ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_ITEM_NOT_FOUND),
- ejabberd_router:route(To, From, Err)
- end;
- #iq{} = _IQ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
- ejabberd_router:route(To, From, Err);
- _ ->
- ok
- end;
- _ ->
- Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
- ejabberd_router:route(To, From, Err)
- end;
- _ ->
- case string:tokens(ChanServ, "%") of
- [[_ | _] = Channel, [_ | _] = Server] ->
- case ets:lookup(irc_connection, {From, Server, Host}) of
- [] ->
- ?DEBUG("open new connection~n", []),
- {Username, Encoding, Port, Password} = get_connection_params(
- Host, ServerHost, From, Server),
- ConnectionUsername =
- case Packet of
- %% If the user tries to join a
- %% chatroom, the packet for sure
- %% contains the desired username.
- {xmlelement, "presence", _, _} ->
- Resource;
- %% Otherwise, there is no firm
- %% conclusion from the packet.
- %% Better to use the configured
- %% username (which defaults to the
- %% username part of the JID).
- _ ->
- Username
- end,
- {ok, Pid} = mod_irc_connection:start(
- From, Host, ServerHost, Server,
- ConnectionUsername, Encoding, Port,
- Password, ?MODULE),
- ets:insert(
- irc_connection,
- #irc_connection{jid_server_host = {From, Server, Host},
- pid = Pid}),
- mod_irc_connection:route_chan(
- Pid, Channel, Resource, Packet),
- ok;
- [R] ->
- Pid = R#irc_connection.pid,
- ?DEBUG("send to process ~p~n",
- [Pid]),
- mod_irc_connection:route_chan(
- Pid, Channel, Resource, Packet),
- ok
- end;
- _ ->
- case string:tokens(ChanServ, "!") of
- [[_ | _] = Nick, [_ | _] = Server] ->
- case ets:lookup(irc_connection, {From, Server, Host}) of
- [] ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_SERVICE_UNAVAILABLE),
- ejabberd_router:route(To, From, Err);
- [R] ->
- Pid = R#irc_connection.pid,
- ?DEBUG("send to process ~p~n",
- [Pid]),
- mod_irc_connection:route_nick(
- Pid, Nick, Packet),
- ok
- end;
- _ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_BAD_REQUEST),
- ejabberd_router:route(To, From, Err)
- end
- end
- end.
-
-
-closed_connection(Host, From, Server) ->
- ets:delete(irc_connection, {From, Server, Host}).
-
-
-iq_disco(_ServerHost, [], Lang) ->
- [{xmlelement, "identity",
- [{"category", "conference"},
- {"type", "irc"},
- {"name", translate:translate(Lang, "IRC Transport")}], []},
- {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
- {xmlelement, "feature", [{"var", ?NS_MUC}], []},
- {xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
- {xmlelement, "feature", [{"var", ?NS_VCARD}], []},
- {xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
-iq_disco(ServerHost, Node, Lang) ->
- case lists:keysearch(Node, 1, commands(ServerHost)) of
- {value, {_, Name, _}} ->
- [{xmlelement, "identity",
- [{"category", "automation"},
- {"type", "command-node"},
- {"name", translate:translate(Lang, Name)}], []},
- {xmlelement, "feature",
- [{"var", ?NS_COMMANDS}], []},
- {xmlelement, "feature",
- [{"var", ?NS_XDATA}], []}];
- _ ->
- []
- end.
-
-iq_get_vcard(Lang) ->
- [{xmlelement, "FN", [],
- [{xmlcdata, "ejabberd/mod_irc"}]},
- {xmlelement, "URL", [],
- [{xmlcdata, ?EJABBERD_URI}]},
- {xmlelement, "DESC", [],
- [{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
- "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-command_items(ServerHost, Host, Lang) ->
- lists:map(fun({Node, Name, _Function})
- -> {xmlelement, "item",
- [{"jid", Host},
- {"node", Node},
- {"name", translate:translate(Lang, Name)}], []}
- end, commands(ServerHost)).
-
-commands(ServerHost) ->
- [{"join", "Join channel", fun adhoc_join/3},
- {"register", "Configure username, encoding, port and password",
- fun(From, To, Request) ->
- adhoc_register(ServerHost, From, To, Request)
- end}].
-
-process_register(ServerHost, Host, From, To, #iq{} = IQ) ->
- case catch process_irc_register(ServerHost, Host, From, To, IQ) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p", [Reason]);
- ResIQ ->
- if
- ResIQ /= ignore ->
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(ResIQ));
- true ->
- ok
- end
- end.
-
-find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
- find_xdata_el1(SubEls).
-
-find_xdata_el1([]) ->
- false;
-
-find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
- case xml:get_attr_s("xmlns", Attrs) of
- ?NS_XDATA ->
- {xmlelement, Name, Attrs, SubEls};
- _ ->
- find_xdata_el1(Els)
- end;
-
-find_xdata_el1([_ | Els]) ->
- find_xdata_el1(Els).
-
-process_irc_register(ServerHost, Host, From, _To,
- #iq{type = Type, xmlns = XMLNS,
- lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- XDataEl = find_xdata_el(SubEl),
- case XDataEl of
- false ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]};
- {xmlelement, _Name, Attrs, _SubEls} ->
- case xml:get_attr_s("type", Attrs) of
- "cancel" ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}], []}]};
- "submit" ->
- XData = jlib:parse_xdata_submit(XDataEl),
- case XData of
- invalid ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_BAD_REQUEST]};
- _ ->
- Node = string:tokens(
- xml:get_tag_attr_s("node", SubEl),
- "/"),
- case set_form(
- ServerHost, Host, From,
- Node, Lang, XData) of
- {result, Res} ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- Res
- }]};
- {error, Error} ->
- IQ#iq{type = error,
- sub_el = [SubEl, Error]}
- end
- end;
- _ ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_BAD_REQUEST]}
- end
- end;
- get ->
- Node =
- string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
- case get_form(ServerHost, Host, From, Node, Lang) of
- {result, Res} ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- Res
- }]};
- {error, Error} ->
- IQ#iq{type = error,
- sub_el = [SubEl, Error]}
- end
- end.
-
-
-
-get_form(ServerHost, Host, From, [], Lang) ->
- #jid{user = User, server = Server} = From,
- LServer = jlib:nameprep(ServerHost),
- SJID = ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From)))),
- SHost = ejabberd_odbc:escape(Host),
- DefaultEncoding = get_default_encoding(Host),
- Customs =
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select data from irc_custom where "
- "jid='", SJID, "' and host='", SHost, "';"]) of
- {selected, ["data"], [{SData}]} ->
- Data = ejabberd_odbc:decode_term(SData),
- {xml:get_attr_s(username, Data),
- xml:get_attr_s(connections_params, Data)};
- {'EXIT', _} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
- {selected, _, _} ->
- {User, []}
- end,
- case Customs of
- {error, _Error} ->
- Customs;
- {Username, ConnectionsParams} ->
- {result,
- [{xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "You need an x:data capable client "
- "to configure mod_irc settings")}]},
- {xmlelement, "x", [{"xmlns", ?NS_XDATA}],
- [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "Registration in mod_irc for ") ++ User ++ "@" ++ Server}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "Enter username, encodings, ports and passwords you wish to use for "
- "connecting to IRC servers")}]},
- {xmlelement, "field", [{"type", "text-single"},
- {"label",
- translate:translate(
- Lang, "IRC Username")},
- {"var", "username"}],
- [{xmlelement, "value", [], [{xmlcdata, Username}]}]},
- {xmlelement, "field", [{"type", "fixed"}],
- [{xmlelement, "value", [],
- [{xmlcdata,
- lists:flatten(
- io_lib:format(
- translate:translate(
- Lang,
- "If you want to specify different ports, "
- "passwords, encodings for IRC servers, fill "
- "this list with values in format "
- "'{\"irc server\", \"encoding\", port, \"password\"}'. "
- "By default this service use \"~s\" encoding, port ~p, "
- "empty password."),
- [DefaultEncoding, ?DEFAULT_IRC_PORT]))}]}]},
- {xmlelement, "field", [{"type", "fixed"}],
- [{xmlelement, "value", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, "
- "{\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."
- )}]}]},
- {xmlelement, "field", [{"type", "text-multi"},
- {"label",
- translate:translate(Lang, "Connections parameters")},
- {"var", "connections_params"}],
- lists:map(
- fun(S) ->
- {xmlelement, "value", [], [{xmlcdata, S}]}
- end,
- string:tokens(
- lists:flatten(
- io_lib:format("~p.", [ConnectionsParams])),
- "\n"))
- }
- ]}]}
- end;
-
-get_form(_ServerHost, _Host, _, _, _Lang) ->
- {error, ?ERR_SERVICE_UNAVAILABLE}.
-
-
-
-
-set_form(ServerHost, Host, From, [], _Lang, XData) ->
- LServer = jlib:nameprep(ServerHost),
- SJID = ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From)))),
- SHost = ejabberd_odbc:escape(Host),
- case {lists:keysearch("username", 1, XData),
- lists:keysearch("connections_params", 1, XData)} of
- {{value, {_, [Username]}}, {value, {_, Strings}}} ->
- EncString = lists:foldl(fun(S, Res) ->
- Res ++ S ++ "\n"
- end, "", Strings),
- case erl_scan:string(EncString) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, ConnectionsParams} ->
- SData = ejabberd_odbc:encode_term(
- [{username,
- Username},
- {connections_params,
- ConnectionsParams}]),
- case ejabberd_odbc:sql_transaction(
- LServer,
- fun() ->
- odbc_queries:update_t(
- "irc_custom",
- ["jid", "host", "data"],
- [SJID, SHost, SData],
- ["jid='", SJID,
- "' and host='",
- SHost, "'"]),
- ok
- end) of
- {atomic, _} ->
- {result, []};
- _ ->
- {error, ?ERR_NOT_ACCEPTABLE}
- end;
- _ ->
- {error, ?ERR_NOT_ACCEPTABLE}
- end;
- _ ->
- {error, ?ERR_NOT_ACCEPTABLE}
- end;
- _ ->
- {error, ?ERR_NOT_ACCEPTABLE}
- end;
-
-
-set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
- {error, ?ERR_SERVICE_UNAVAILABLE}.
-
-
-%% Host = "irc.example.com"
-%% ServerHost = "example.com"
-get_connection_params(Host, From, IRCServer) ->
- [_ | HostTail] = string:tokens(Host, "."),
- ServerHost = string:join(HostTail, "."),
- get_connection_params(Host, ServerHost, From, IRCServer).
-
-get_default_encoding(ServerHost) ->
- Result = gen_mod:get_module_opt(
- ServerHost, ?MODULE, default_encoding,
- ?DEFAULT_IRC_ENCODING),
- ?INFO_MSG("The default_encoding configured for host ~p is: ~p~n", [ServerHost, Result]),
- Result.
-
-get_connection_params(Host, ServerHost, From, IRCServer) ->
- #jid{user = User, server = _Server} = From,
- LServer = jlib:nameprep(ServerHost),
- SJID = ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From)))),
- SHost = ejabberd_odbc:escape(Host),
- DefaultEncoding = get_default_encoding(ServerHost),
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select data from irc_custom where "
- "jid='", SJID, "' and host='", SHost, "';"]) of
- {'EXIT', _Reason} ->
- {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
- {selected, ["data"], []} ->
- {User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
- {selected, ["data"], [{SData}]} ->
- Data = ejabberd_odbc:decode_term(SData),
- Username = xml:get_attr_s(username, Data),
- {NewUsername, NewEncoding, NewPort, NewPassword} =
- case lists:keysearch(
- IRCServer, 1,
- xml:get_attr_s(connections_params, Data)) of
- {value, {_, Encoding, Port, Password}} ->
- {Username, Encoding, Port, Password};
- {value, {_, Encoding, Port}} ->
- {Username, Encoding, Port, ""};
- {value, {_, Encoding}} ->
- {Username, Encoding, ?DEFAULT_IRC_PORT, ""};
- _ ->
- {Username, DefaultEncoding, ?DEFAULT_IRC_PORT, ""}
- end,
- {NewUsername,
- NewEncoding,
- if
- NewPort >= 0 andalso NewPort =< 65535 ->
- NewPort;
- true ->
- ?DEFAULT_IRC_PORT
- end,
- NewPassword}
- end.
-
-adhoc_join(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
- adhoc:produce_response(Request,
- #adhoc_response{status = canceled});
-adhoc_join(From, To, #adhoc_request{lang = Lang,
- node = _Node,
- action = _Action,
- xdata = XData} = Request) ->
- %% Access control has already been taken care of in do_route.
- if XData == false ->
- Form =
- {xmlelement, "x",
- [{"xmlns", ?NS_XDATA},
- {"type", "form"}],
- [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "Join IRC channel")}]},
- {xmlelement, "field",
- [{"var", "channel"},
- {"type", "text-single"},
- {"label", translate:translate(Lang, "IRC channel (don't put the first #)")}],
- [{xmlelement, "required", [], []}]},
- {xmlelement, "field",
- [{"var", "server"},
- {"type", "text-single"},
- {"label", translate:translate(Lang, "IRC server")}],
- [{xmlelement, "required", [], []}]}]},
- adhoc:produce_response(Request,
- #adhoc_response{status = executing,
- elements = [Form]});
- true ->
- case jlib:parse_xdata_submit(XData) of
- invalid ->
- {error, ?ERR_BAD_REQUEST};
- Fields ->
- Channel = case lists:keysearch("channel", 1, Fields) of
- {value, {"channel", C}} ->
- C;
- _ ->
- false
- end,
- Server = case lists:keysearch("server", 1, Fields) of
- {value, {"server", S}} ->
- S;
- _ ->
- false
- end,
- if Channel /= false,
- Server /= false ->
- RoomJID = Channel ++ "%" ++ Server ++ "@" ++ To#jid.server,
- Invite = {xmlelement, "message", [],
- [{xmlelement, "x",
- [{"xmlns", ?NS_MUC_USER}],
- [{xmlelement, "invite",
- [{"from", jlib:jid_to_string(From)}],
- [{xmlelement, "reason", [],
- [{xmlcdata,
- translate:translate(Lang,
- "Join the IRC channel here.")}]}]}]},
- {xmlelement, "x",
- [{"xmlns", ?NS_XCONFERENCE}],
- [{xmlcdata, translate:translate(Lang,
- "Join the IRC channel here.")}]},
- {xmlelement, "body", [],
- [{xmlcdata, io_lib:format(
- translate:translate(Lang,
- "Join the IRC channel in this Jabber ID: ~s"),
- [RoomJID])}]}]},
- ejabberd_router:route(jlib:string_to_jid(RoomJID), From, Invite),
- adhoc:produce_response(Request, #adhoc_response{status = completed});
- true ->
- {error, ?ERR_BAD_REQUEST}
- end
- end
- end.
-
-adhoc_register(_ServerHost, _From, _To, #adhoc_request{action = "cancel"} = Request) ->
- adhoc:produce_response(Request,
- #adhoc_response{status = canceled});
-adhoc_register(ServerHost, From, To, #adhoc_request{lang = Lang,
- node = _Node,
- xdata = XData,
- action = Action} = Request) ->
- #jid{user = User} = From,
- #jid{lserver = Host} = To,
- LServer = jlib:nameprep(ServerHost),
- SHost = ejabberd_odbc:escape(Host),
- SJID = ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From)))),
- %% Generate form for setting username and encodings. If the user
- %% hasn't begun to fill out the form, generate an initial form
- %% based on current values.
- if XData == false ->
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select data from irc_custom where "
- "jid='", SJID, "' and host='", SHost, "';"]) of
- {'EXIT', _Reason} ->
- Username = User,
- ConnectionsParams = [];
- {selected, ["data"], []} ->
- Username = User,
- ConnectionsParams = [];
- {selected, ["data"], [{Data1}]} ->
- Data = ejabberd_odbc:decode_term(Data1),
- Username = xml:get_attr_s(username, Data),
- ConnectionsParams = xml:get_attr_s(connections_params, Data)
- end,
- Error = false;
- true ->
- case jlib:parse_xdata_submit(XData) of
- invalid ->
- Error = {error, ?ERR_BAD_REQUEST},
- Username = false,
- ConnectionsParams = false;
- Fields ->
- Username = case lists:keysearch("username", 1, Fields) of
- {value, {"username", U}} ->
- U;
- _ ->
- User
- end,
- ConnectionsParams = parse_connections_params(Fields),
- Error = false
- end
- end,
-
- if Error /= false ->
- Error;
- Action == "complete" ->
- SData = ejabberd_odbc:encode_term(
- [{username, Username},
- {connections_params, ConnectionsParams}]),
- case catch ejabberd_odbc:sql_transaction(
- LServer,
- fun() ->
- odbc_queries:update_t(
- "irc_custom",
- ["jid", "host", "data"],
- [SJID, SHost, SData],
- ["jid='", SJID,
- "' and host='", SHost, "'"]),
- ok
- end) of
- {atomic, ok} ->
- adhoc:produce_response(Request, #adhoc_response{status = completed});
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
- true ->
- Form = generate_adhoc_register_form(Lang, Username, ConnectionsParams),
- adhoc:produce_response(Request,
- #adhoc_response{status = executing,
- elements = [Form],
- actions = ["next", "complete"]})
- end.
-
-generate_adhoc_register_form(Lang, Username, ConnectionsParams) ->
- {xmlelement, "x",
- [{"xmlns", ?NS_XDATA},
- {"type", "form"}],
- [{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "IRC settings")}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "Enter username and encodings you wish to use for "
- "connecting to IRC servers. Press 'Next' to get more fields "
- "to fill in. Press 'Complete' to save settings.")}]},
- {xmlelement, "field",
- [{"var", "username"},
- {"type", "text-single"},
- {"label", translate:translate(Lang, "IRC username")}],
- [{xmlelement, "required", [], []},
- {xmlelement, "value", [], [{xmlcdata, Username}]}]}] ++
- generate_connection_params_fields(Lang, ConnectionsParams, 1, [])}.
-
-generate_connection_params_fields(Lang, [], Number, Acc) ->
- Field = generate_connection_params_field(Lang, "", "", -1, "", Number),
- lists:reverse(Field ++ Acc);
-
-generate_connection_params_fields(Lang, [ConnectionParams | ConnectionsParams], Number, Acc) ->
- case ConnectionParams of
- {Server, Encoding, Port, Password} ->
- Field = generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number),
- generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
- {Server, Encoding, Port} ->
- Field = generate_connection_params_field(Lang, Server, Encoding, Port, [], Number),
- generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
- {Server, Encoding} ->
- Field = generate_connection_params_field(Lang, Server, Encoding, [], [], Number),
- generate_connection_params_fields(Lang, ConnectionsParams, Number + 1, Field ++ Acc);
- _ ->
- []
- end.
-
-generate_connection_params_field(Lang, Server, Encoding, Port, Password, Number) ->
- EncodingUsed = case Encoding of
- [] ->
- get_default_encoding(Server);
- _ ->
- Encoding
- end,
- PortUsedInt = if
- Port >= 0 andalso Port =< 65535 ->
- Port;
- true ->
- ?DEFAULT_IRC_PORT
- end,
- PortUsed = integer_to_list(PortUsedInt),
- PasswordUsed = case Password of
- [] ->
- "";
- _ ->
- Password
- end,
- NumberString = integer_to_list(Number),
- %% Fields are in reverse order, as they will be reversed again later.
- [{xmlelement, "field",
- [{"var", "password" ++ NumberString},
- {"type", "text-single"},
- {"label", io_lib:format(translate:translate(Lang, "Password ~b"), [Number])}],
- [{xmlelement, "value", [], [{xmlcdata, PasswordUsed}]}]},
- {xmlelement, "field",
- [{"var", "port" ++ NumberString},
- {"type", "text-single"},
- {"label", io_lib:format(translate:translate(Lang, "Port ~b"), [Number])}],
- [{xmlelement, "value", [], [{xmlcdata, PortUsed}]}]},
- {xmlelement, "field",
- [{"var", "encoding" ++ NumberString},
- {"type", "list-single"},
- {"label", io_lib:format(translate:translate(Lang, "Encoding for server ~b"), [Number])}],
- [{xmlelement, "value", [], [{xmlcdata, EncodingUsed}]} |
- lists:map(fun(E) ->
- {xmlelement, "option", [{"label", E}],
- [{xmlelement, "value", [], [{xmlcdata, E}]}]}
- end, ?POSSIBLE_ENCODINGS)]},
- {xmlelement, "field",
- [{"var", "server" ++ NumberString},
- {"type", "text-single"},
- {"label", io_lib:format(translate:translate(Lang, "Server ~b"), [Number])}],
- [{xmlelement, "value", [], [{xmlcdata, Server}]}]}].
-
-parse_connections_params(Fields) ->
- %% Find all fields staring with serverN, encodingN, portN and passwordN for any values
- %% of N, and generate lists of {"N", Value}.
- Servers = lists:sort(
- [{lists:nthtail(6, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
- lists:prefix("server", Var)]),
- Encodings = lists:sort(
- [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
- lists:prefix("encoding", Var)]),
-
- Ports = lists:sort(
- [{lists:nthtail(4, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
- lists:prefix("port", Var)]),
-
- Passwords = lists:sort(
- [{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
- lists:prefix("password", Var)]),
-
- %% Now sort the lists, and find the corresponding pairs.
- parse_connections_params(Servers, Encodings, Ports, Passwords).
-
-retrieve_connections_params(ConnectionParams, ServerN) ->
- case ConnectionParams of
- [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail] ->
- if
- ServerN == ConnectionParamN ->
- {ConnectionParam, ConnectionParamsTail};
- ServerN < ConnectionParamN ->
- {[], [{ConnectionParamN, ConnectionParam} | ConnectionParamsTail]};
- ServerN > ConnectionParamN ->
- {[], ConnectionParamsTail}
- end;
- _ ->
- {[], []}
- end.
-
-parse_connections_params([], _, _, _) ->
- [];
-parse_connections_params(_, [], [], []) ->
- [];
-
-parse_connections_params([{ServerN, Server} | Servers], Encodings, Ports, Passwords) ->
- %% Try to match matches of servers, ports, passwords and encodings, no matter what fields
- %% the client might have left out.
- {NewEncoding, NewEncodings} = retrieve_connections_params(Encodings, ServerN),
- {NewPort, NewPorts} = retrieve_connections_params(Ports, ServerN),
- {NewPassword, NewPasswords} = retrieve_connections_params(Passwords, ServerN),
- [{Server, NewEncoding, NewPort, NewPassword} | parse_connections_params(Servers, NewEncodings, NewPorts, NewPasswords)].
diff --git a/src/mod_last.erl b/src/mod_last.erl
index 31876d47c..4cc0c5cc3 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -47,10 +47,16 @@
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- mnesia:create_table(last_activity,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, last_activity)}]),
- update_table(),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(last_activity,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, last_activity)}]),
+ update_table();
+ _ ->
+ ok
+ end,
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
@@ -145,6 +151,9 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
%% @spec (LUser::string(), LServer::string()) ->
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
get_last(LUser, LServer) ->
+ get_last(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_last(LUser, LServer, mnesia) ->
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
{'EXIT', Reason} ->
{error, Reason};
@@ -152,6 +161,21 @@ get_last(LUser, LServer) ->
not_found;
[#last_activity{timestamp = TimeStamp, status = Status}] ->
{ok, TimeStamp, Status}
+ end;
+get_last(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:get_last(LServer, Username) of
+ {selected, ["seconds","state"], []} ->
+ not_found;
+ {selected, ["seconds","state"], [{STimeStamp, Status}]} ->
+ case catch list_to_integer(STimeStamp) of
+ TimeStamp when is_integer(TimeStamp) ->
+ {ok, TimeStamp, Status};
+ Reason ->
+ {error, {invalid_timestamp, Reason}}
+ end;
+ Reason ->
+ {error, {invalid_result, Reason}}
end.
get_last_iq(IQ, SubEl, LUser, LServer) ->
@@ -186,13 +210,22 @@ on_presence_update(User, Server, _Resource, Status) ->
store_last_info(User, Server, TimeStamp, Status) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ store_last_info(LUser, LServer, TimeStamp, Status, DBType).
+
+store_last_info(LUser, LServer, TimeStamp, Status, mnesia) ->
US = {LUser, LServer},
F = fun() ->
mnesia:write(#last_activity{us = US,
timestamp = TimeStamp,
status = Status})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+store_last_info(LUser, LServer, TimeStamp, Status, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
+ State = ejabberd_odbc:escape(Status),
+ odbc_queries:set_last_t(LServer, Username, Seconds, State).
%% @spec (LUser::string(), LServer::string()) ->
%% {ok, TimeStamp::integer(), Status::string()} | not_found
@@ -207,12 +240,18 @@ get_last_info(LUser, LServer) ->
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ remove_user(LUser, LServer, DBType).
+
+remove_user(LUser, LServer, mnesia) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete({last_activity, US})
end,
- mnesia:transaction(F).
-
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:del_last(LServer, Username).
update_table() ->
Fields = record_info(fields, last_activity),
diff --git a/src/mod_last_odbc.erl b/src/mod_last_odbc.erl
deleted file mode 100644
index 4466909d4..000000000
--- a/src/mod_last_odbc.erl
+++ /dev/null
@@ -1,204 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_last_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : jabber:iq:last support (XEP-0012)
-%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_last_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
- stop/1,
- process_local_iq/3,
- process_sm_iq/3,
- on_presence_update/4,
- store_last_info/4,
- get_last_info/2,
- remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
- ?MODULE, process_local_iq, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
- ?MODULE, process_sm_iq, IQDisc),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(unset_presence_hook, Host,
- ?MODULE, on_presence_update, 50).
-
-stop(Host) ->
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(unset_presence_hook, Host,
- ?MODULE, on_presence_update, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_LAST),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_LAST).
-
-%%%
-%%% Uptime of ejabberd node
-%%%
-
-process_local_iq(_From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
- get ->
- Sec = get_node_uptime(),
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_LAST},
- {"seconds", integer_to_list(Sec)}],
- []}]}
- end.
-
-%% @spec () -> integer()
-%% @doc Get the uptime of the ejabberd node, expressed in seconds.
-%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
-get_node_uptime() ->
- case ejabberd_config:get_local_option(node_start) of
- {_, _, _} = StartNow ->
- now_to_seconds(now()) - now_to_seconds(StartNow);
- _undefined ->
- trunc(element(1, erlang:statistics(wall_clock))/1000)
- end.
-
-now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
- MegaSecs * 1000000 + Secs.
-
-
-%%%
-%%% Serve queries about user last online
-%%%
-process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
- get ->
- User = To#jid.luser,
- Server = To#jid.lserver,
- {Subscription, _Groups} =
- ejabberd_hooks:run_fold(
- roster_get_jid_info, Server,
- {none, []}, [User, Server, From]),
- if
- (Subscription == both) or (Subscription == from) ->
- UserListRecord = ejabberd_hooks:run_fold(
- privacy_get_user_list, Server,
- #userlist{},
- [User, Server]),
- case ejabberd_hooks:run_fold(
- privacy_check_packet, Server,
- allow,
- [User, Server, UserListRecord,
- {To, From,
- {xmlelement, "presence", [], []}},
- out]) of
- allow ->
- get_last_iq(IQ, SubEl, User, Server);
- deny ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_FORBIDDEN]}
- end;
- true ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_FORBIDDEN]}
- end
- end.
-
-%% @spec (LUser::string(), LServer::string()) ->
-%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
-get_last(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_last(LServer, Username) of
- {selected, ["seconds","state"], []} ->
- not_found;
- {selected, ["seconds","state"], [{STimeStamp, Status}]} ->
- case catch list_to_integer(STimeStamp) of
- TimeStamp when is_integer(TimeStamp) ->
- {ok, TimeStamp, Status};
- Reason ->
- {error, {invalid_timestamp, Reason}}
- end;
- Reason ->
- {error, {invalid_result, Reason}}
- end.
-
-get_last_iq(IQ, SubEl, LUser, LServer) ->
- case ejabberd_sm:get_user_resources(LUser, LServer) of
- [] ->
- case get_last(LUser, LServer) of
- {error, _Reason} ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
- not_found ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
- {ok, TimeStamp, Status} ->
- TimeStamp2 = now_to_seconds(now()),
- Sec = TimeStamp2 - TimeStamp,
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_LAST},
- {"seconds", integer_to_list(Sec)}],
- [{xmlcdata, Status}]}]}
- end;
- _ ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_LAST},
- {"seconds", "0"}],
- []}]}
- end.
-
-on_presence_update(User, Server, _Resource, Status) ->
- TimeStamp = now_to_seconds(now()),
- store_last_info(User, Server, TimeStamp, Status).
-
-store_last_info(User, Server, TimeStamp, Status) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)),
- State = ejabberd_odbc:escape(Status),
- odbc_queries:set_last_t(LServer, Username, Seconds, State).
-
-%% @spec (LUser::string(), LServer::string()) ->
-%% {ok, TimeStamp::integer(), Status::string()} | not_found
-get_last_info(LUser, LServer) ->
- case get_last(LUser, LServer) of
- {error, _Reason} ->
- not_found;
- Res ->
- Res
- end.
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_last(LServer, Username).
diff --git a/src/mod_muc/Makefile.win32 b/src/mod_muc/Makefile.win32
index 27b81925e..5107b1069 100644
--- a/src/mod_muc/Makefile.win32
+++ b/src/mod_muc/Makefile.win32
@@ -4,7 +4,7 @@ include ..\Makefile.inc
EFLAGS = -I .. -pz ..
OUTDIR = ..
-BEAMS = ..\mod_muc.beam ..\mod_muc_odbc.beam ..\mod_muc_log.beam ..\mod_muc_room.beam
+BEAMS = ..\mod_muc.beam ..\mod_muc_log.beam ..\mod_muc_room.beam
ALL : $(BEAMS)
@@ -14,9 +14,6 @@ CLEAN :
$(OUTDIR)\mod_muc.beam : mod_muc.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc.erl
-$(OUTDIR)\mod_muc_odbc.beam : mod_muc_odbc.erl
- erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_odbc.erl
-
$(OUTDIR)\mod_muc_log.beam : mod_muc_log.erl
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_log.erl
diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl
index 81573d1a1..811e3c068 100644
--- a/src/mod_muc/mod_muc.erl
+++ b/src/mod_muc/mod_muc.erl
@@ -111,26 +111,70 @@ create_room(Host, Name, From, Nick, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, {create, Name, From, Nick, Opts}).
-store_room(_ServerHost, Host, Name, Opts) ->
+store_room(ServerHost, Host, Name, Opts) ->
+ LServer = jlib:nameprep(ServerHost),
+ store_room(LServer, Host, Name, Opts, gen_mod:db_type(LServer, ?MODULE)).
+
+store_room(_LServer, Host, Name, Opts, mnesia) ->
F = fun() ->
mnesia:write(#muc_room{name_host = {Name, Host},
opts = Opts})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+store_room(LServer, Host, Name, Opts, odbc) ->
+ SName = ejabberd_odbc:escape(Name),
+ SHost = ejabberd_odbc:escape(Host),
+ SOpts = ejabberd_odbc:encode_term(Opts),
+ F = fun() ->
+ odbc_queries:update_t(
+ "muc_room",
+ ["name", "host", "opts"],
+ [SName, SHost, SOpts],
+ ["name='", SName, "' and host='", SHost, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
-restore_room(_ServerHost, Host, Name) ->
+restore_room(ServerHost, Host, Name) ->
+ LServer = jlib:nameprep(ServerHost),
+ restore_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)).
+
+restore_room(_LServer, Host, Name, mnesia) ->
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
[#muc_room{opts = Opts}] ->
Opts;
_ ->
error
+ end;
+restore_room(LServer, Host, Name, odbc) ->
+ SName = ejabberd_odbc:escape(Name),
+ SHost = ejabberd_odbc:escape(Host),
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select opts from muc_room where name='",
+ SName, "' and host='", SHost, "';"]) of
+ {selected, ["opts"], [{Opts}]} ->
+ ejabberd_odbc:decode_term(Opts);
+ _ ->
+ error
end.
-forget_room(_ServerHost, Host, Name) ->
+forget_room(ServerHost, Host, Name) ->
+ LServer = jlib:nameprep(ServerHost),
+ forget_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)).
+
+forget_room(_LServer, Host, Name, mnesia) ->
F = fun() ->
mnesia:delete({muc_room, {Name, Host}})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+forget_room(LServer, Host, Name, odbc) ->
+ SName = ejabberd_odbc:escape(Name),
+ SHost = ejabberd_odbc:escape(Host),
+ F = fun() ->
+ ejabberd_odbc:sql_query_t(
+ ["delete from muc_room where name='",
+ SName, "' and host='", SHost, "';"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
Rsm = jlib:rsm_decode(IQ),
@@ -144,7 +188,11 @@ process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
can_use_nick(_ServerHost, _Host, _JID, "") ->
false;
-can_use_nick(_ServerHost, Host, JID, Nick) ->
+can_use_nick(ServerHost, Host, JID, Nick) ->
+ LServer = jlib:nameprep(ServerHost),
+ can_use_nick(LServer, Host, JID, Nick, gen_mod:db_type(LServer, ?MODULE)).
+
+can_use_nick(_LServer, Host, JID, Nick, mnesia) ->
{LUser, LServer, _} = jlib:jid_tolower(JID),
LUS = {LUser, LServer},
case catch mnesia:dirty_select(
@@ -160,6 +208,21 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
true;
[#muc_registered{us_host = {U, _Host}}] ->
U == LUS
+ end;
+can_use_nick(LServer, Host, JID, Nick, odbc) ->
+ SJID = jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:jid_remove_resource(JID))),
+ SNick = ejabberd_odbc:escape(Nick),
+ SHost = ejabberd_odbc:escape(Host),
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select jid from muc_registered ",
+ "where nick='", SNick, "' and host='",
+ SHost, "';"]) of
+ {selected, ["jid"], [{SJID1}]} ->
+ SJID == SJID1;
+ _ ->
+ true
end.
%%====================================================================
@@ -174,21 +237,28 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
- mnesia:create_table(muc_room,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, muc_room)}]),
- mnesia:create_table(muc_registered,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, muc_registered)}]),
+ MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ update_tables(MyHost),
+ mnesia:create_table(muc_room,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, muc_room)}]),
+ mnesia:create_table(muc_registered,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, muc_registered)}]),
+ mnesia:add_table_index(muc_registered, nick);
+ _ ->
+ ok
+ end,
mnesia:create_table(muc_online_room,
[{ram_copies, [node()]},
{attributes, record_info(fields, muc_online_room)}]),
mnesia:add_table_copy(muc_online_room, node(), ram_copies),
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
- MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
- update_tables(MyHost),
clean_table_from_bad_node(node(), MyHost),
- mnesia:add_table_index(muc_registered, nick),
mnesia:subscribe(system),
Access = gen_mod:get_opt(access, Opts, all),
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
@@ -238,7 +308,7 @@ handle_call({create, Room, From, Nick, Opts},
Host, ServerHost, Access,
Room, HistorySize,
RoomShaper, From,
- Nick, NewOpts, ?MODULE),
+ Nick, NewOpts),
register_room(Host, Room, Pid),
{reply, ok, State}.
@@ -379,7 +449,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
[{xmlelement, "query",
[{"xmlns", XMLNS}],
iq_get_register_info(
- Host, From, Lang)}]},
+ ServerHost, Host, From, Lang)}]},
ejabberd_router:route(To,
From,
jlib:iq_to_xml(Res));
@@ -387,7 +457,8 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
xmlns = ?NS_REGISTER = XMLNS,
lang = Lang,
sub_el = SubEl} = IQ ->
- case process_iq_register_set(Host, From, SubEl, Lang) of
+ case process_iq_register_set(
+ ServerHost, Host, From, SubEl, Lang) of
{result, IQRes} ->
Res = IQ#iq{type = result,
sub_el =
@@ -519,52 +590,72 @@ check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
false
end.
+get_rooms(ServerHost, Host) ->
+ LServer = jlib:nameprep(ServerHost),
+ get_rooms(LServer, Host, gen_mod:db_type(LServer, ?MODULE)).
-load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
+get_rooms(_LServer, Host, mnesia) ->
case catch mnesia:dirty_select(
muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'},
[],
['$_']}]) of
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]),
- ok;
+ [];
Rs ->
- lists:foreach(
- fun(R) ->
- {Room, Host} = R#muc_room.name_host,
- case mnesia:dirty_read(muc_online_room, {Room, Host}) of
- [] ->
- {ok, Pid} = mod_muc_room:start(
- Host,
- ServerHost,
- Access,
- Room,
- HistorySize,
- RoomShaper,
- R#muc_room.opts,
- ?MODULE),
- register_room(Host, Room, Pid);
- _ ->
- ok
- end
- end, Rs)
+ Rs
+ end;
+get_rooms(LServer, Host, odbc) ->
+ SHost = ejabberd_odbc:escape(Host),
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select name, opts from muc_room ",
+ "where host='", SHost, "';"]) of
+ {'EXIT', Reason} ->
+ ?ERROR_MSG("~p", [Reason]),
+ [];
+ {selected, ["name", "opts"], RoomOpts} ->
+ lists:map(
+ fun({Room, Opts}) ->
+ #muc_room{name_host = {Room, Host},
+ opts = ejabberd_odbc:decode_term(Opts)}
+ end, RoomOpts)
end.
+load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
+ lists:foreach(
+ fun(R) ->
+ {Room, Host} = R#muc_room.name_host,
+ case mnesia:dirty_read(muc_online_room, {Room, Host}) of
+ [] ->
+ {ok, Pid} = mod_muc_room:start(
+ Host,
+ ServerHost,
+ Access,
+ Room,
+ HistorySize,
+ RoomShaper,
+ R#muc_room.opts),
+ register_room(Host, Room, Pid);
+ _ ->
+ ok
+ end
+ end, get_rooms(ServerHost, Host)).
+
start_new_room(Host, ServerHost, Access, Room,
HistorySize, RoomShaper, From,
Nick, DefRoomOpts) ->
- case mnesia:dirty_read(muc_room, {Room, Host}) of
- [] ->
+ case restore_room(ServerHost, Room, Host) of
+ error ->
?DEBUG("MUC: open new room '~s'~n", [Room]),
mod_muc_room:start(Host, ServerHost, Access,
Room, HistorySize,
RoomShaper, From,
- Nick, DefRoomOpts, ?MODULE);
- [#muc_room{opts = Opts}|_] ->
+ Nick, DefRoomOpts);
+ Opts ->
?DEBUG("MUC: restore room '~s'~n", [Room]),
mod_muc_room:start(Host, ServerHost, Access,
Room, HistorySize,
- RoomShaper, Opts, ?MODULE)
+ RoomShaper, Opts)
end.
register_room(Host, Room, Pid) ->
@@ -693,18 +784,44 @@ flush() ->
iq_get_unique(From) ->
{xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}.
-iq_get_register_info(Host, From, Lang) ->
+get_nick(ServerHost, Host, From) ->
+ LServer = jlib:nameprep(ServerHost),
+ get_nick(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)).
+
+get_nick(_LServer, Host, From, mnesia) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
LUS = {LUser, LServer},
+ case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
+ {'EXIT', _Reason} ->
+ error;
+ [] ->
+ error;
+ [#muc_registered{nick = Nick}] ->
+ Nick
+ end;
+get_nick(LServer, Host, From, odbc) ->
+ SJID = ejabberd_odbc:escape(
+ jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:jid_remove_resource(From)))),
+ SHost = ejabberd_odbc:escape(Host),
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select nick from muc_registered where "
+ "jid='", SJID, "' and host='", SHost, "';"]) of
+ {selected, ["nick"], [{Nick}]} ->
+ Nick;
+ _ ->
+ error
+ end.
+
+iq_get_register_info(ServerHost, Host, From, Lang) ->
{Nick, Registered} =
- case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
- {'EXIT', _Reason} ->
- {"", []};
- [] ->
- {"", []};
- [#muc_registered{nick = N}] ->
- {N, [{xmlelement, "registered", [], []}]}
- end,
+ case get_nick(ServerHost, Host, From) of
+ error ->
+ {"", []};
+ N ->
+ {N, [{xmlelement, "registered", [], []}]}
+ end,
Registered ++
[{xmlelement, "instructions", [],
[{xmlcdata,
@@ -722,7 +839,11 @@ iq_get_register_info(Host, From, Lang) ->
Lang, "Enter nickname you want to register")}]},
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
-iq_set_register_info(Host, From, Nick, Lang) ->
+set_nick(ServerHost, Host, From, Nick) ->
+ LServer = jlib:nameprep(ServerHost),
+ set_nick(LServer, Host, From, Nick, gen_mod:db_type(LServer, ?MODULE)).
+
+set_nick(_LServer, Host, From, Nick, mnesia) ->
{LUser, LServer, _} = jlib:jid_tolower(From),
LUS = {LUser, LServer},
F = fun() ->
@@ -755,7 +876,48 @@ iq_set_register_info(Host, From, Nick, Lang) ->
end
end
end,
- case mnesia:transaction(F) of
+ mnesia:transaction(F);
+set_nick(LServer, Host, From, Nick, odbc) ->
+ JID = jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:jid_remove_resource(From))),
+ SJID = ejabberd_odbc:escape(JID),
+ SNick = ejabberd_odbc:escape(Nick),
+ SHost = ejabberd_odbc:escape(Host),
+ F = fun() ->
+ case Nick of
+ "" ->
+ ejabberd_odbc:sql_query_t(
+ ["delete from muc_registered where ",
+ "jid='", SJID, "' and host='", Host, "';"]),
+ ok;
+ _ ->
+ Allow =
+ case ejabberd_odbc:sql_query_t(
+ ["select jid from muc_registered ",
+ "where nick='", SNick, "' and host='",
+ SHost, "';"]) of
+ {selected, ["jid"], [{J}]} ->
+ J == JID;
+ _ ->
+ true
+ end,
+ if Allow ->
+ odbc_queries:update_t(
+ "muc_registered",
+ ["jid", "host", "nick"],
+ [SJID, SHost, SNick],
+ ["jid='", SJID, "' and host='", SHost, "'"]),
+ ok;
+ true ->
+ false
+ end
+ end
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
+
+iq_set_register_info(ServerHost, Host, From, Nick, Lang) ->
+ case set_nick(ServerHost, Host, From, Nick) of
{atomic, ok} ->
{result, []};
{atomic, false} ->
@@ -765,7 +927,7 @@ iq_set_register_info(Host, From, Nick, Lang) ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
-process_iq_register_set(Host, From, SubEl, Lang) ->
+process_iq_register_set(ServerHost, Host, From, SubEl, Lang) ->
{xmlelement, _Name, _Attrs, Els} = SubEl,
case xml:get_subtag(SubEl, "remove") of
false ->
@@ -783,7 +945,8 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
_ ->
case lists:keysearch("nick", 1, XData) of
{value, {_, [Nick]}} when Nick /= "" ->
- iq_set_register_info(Host, From, Nick, Lang);
+ iq_set_register_info(ServerHost, Host,
+ From, Nick, Lang);
_ ->
ErrText = "You must fill in field \"Nickname\" in the form",
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
@@ -796,7 +959,7 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
{error, ?ERR_BAD_REQUEST}
end;
_ ->
- iq_set_register_info(Host, From, "", Lang)
+ iq_set_register_info(ServerHost, Host, From, "", Lang)
end.
iq_get_vcard(Lang) ->
diff --git a/src/mod_muc/mod_muc_odbc.erl b/src/mod_muc/mod_muc_odbc.erl
deleted file mode 100644
index 47dc4c9ef..000000000
--- a/src/mod_muc/mod_muc_odbc.erl
+++ /dev/null
@@ -1,875 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_muc_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : MUC support (XEP-0045)
-%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_muc_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_server).
--behaviour(gen_mod).
-
-%% API
--export([start_link/2,
- start/2,
- stop/1,
- room_destroyed/4,
- store_room/4,
- restore_room/3,
- forget_room/3,
- create_room/5,
- process_iq_disco_items/4,
- broadcast_service_message/2,
- can_use_nick/4]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-
--record(muc_online_room, {name_host, pid}).
-
--record(state, {host,
- server_host,
- access,
- history_size,
- default_room_opts,
- room_shaper}).
-
--define(PROCNAME, ejabberd_mod_muc).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
-start_link(Host, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
-
-start(Host, Opts) ->
- start_supervisor(Host),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- ChildSpec =
- {Proc,
- {?MODULE, start_link, [Host, Opts]},
- temporary,
- 1000,
- worker,
- [?MODULE]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
- stop_supervisor(Host),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:call(Proc, stop),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-%% This function is called by a room in three situations:
-%% A) The owner of the room destroyed it
-%% B) The only participant of a temporary room leaves it
-%% C) mod_muc_odbc:stop was called, and each room is being terminated
-%% In this case, the mod_muc_odbc process died before the room processes
-%% So the message sending must be catched
-room_destroyed(Host, Room, Pid, ServerHost) ->
- catch gen_mod:get_module_proc(ServerHost, ?PROCNAME) !
- {room_destroyed, {Room, Host}, Pid},
- ok.
-
-%% @doc Create a room.
-%% If Opts = default, the default room options are used.
-%% Else use the passed options as defined in mod_muc_room.
-create_room(Host, Name, From, Nick, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:call(Proc, {create, Name, From, Nick, Opts}).
-
-store_room(ServerHost, Host, Name, Opts) ->
- SName = ejabberd_odbc:escape(Name),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- SOpts = ejabberd_odbc:encode_term(Opts),
- F = fun() ->
- odbc_queries:update_t(
- "muc_room",
- ["name", "host", "opts"],
- [SName, SHost, SOpts],
- ["name='", SName, "' and host='", SHost, "'"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-restore_room(ServerHost, Host, Name) ->
- SName = ejabberd_odbc:escape(Name),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- case catch ejabberd_odbc:sql_query(
- LServer, ["select opts from muc_room where name='",
- SName, "' and host='", SHost, "';"]) of
- {selected, ["opts"], [{Opts}]} ->
- ejabberd_odbc:decode_term(Opts);
- _ ->
- error
- end.
-
-forget_room(ServerHost, Host, Name) ->
- SName = ejabberd_odbc:escape(Name),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- F = fun() ->
- ejabberd_odbc:sql_query_t(
- ["delete from muc_room where name='",
- SName, "' and host='", SHost, "';"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
- Rsm = jlib:rsm_decode(IQ),
- Res = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_DISCO_ITEMS}],
- iq_disco_items(Host, From, Lang, Rsm)}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res)).
-
-can_use_nick(_ServerHost, _Host, _JID, "") ->
- false;
-can_use_nick(ServerHost, Host, JID, Nick) ->
- SJID = jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(JID))),
- SNick = ejabberd_odbc:escape(Nick),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- case catch ejabberd_odbc:sql_query(
- LServer, ["select jid from muc_registered ",
- "where nick='", SNick, "' and host='",
- SHost, "';"]) of
- {selected, ["jid"], [{SJID1}]} ->
- SJID == SJID1;
- _ ->
- true
- end.
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
-init([Host, Opts]) ->
- mnesia:create_table(muc_online_room,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, muc_online_room)}]),
- mnesia:add_table_copy(muc_online_room, node(), ram_copies),
- catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
- MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
- clean_table_from_bad_node(node(), MyHost),
- mnesia:subscribe(system),
- Access = gen_mod:get_opt(access, Opts, all),
- AccessCreate = gen_mod:get_opt(access_create, Opts, all),
- AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
- AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all),
- HistorySize = gen_mod:get_opt(history_size, Opts, 20),
- DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
- RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
- ejabberd_router:register_route(MyHost),
- load_permanent_rooms(MyHost, Host,
- {Access, AccessCreate, AccessAdmin, AccessPersistent},
- HistorySize,
- RoomShaper),
- {ok, #state{host = MyHost,
- server_host = Host,
- access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
- default_room_opts = DefRoomOpts,
- history_size = HistorySize,
- room_shaper = RoomShaper}}.
-
-%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
-%% Description: Handling call messages
-%%--------------------------------------------------------------------
-handle_call(stop, _From, State) ->
- {stop, normal, ok, State};
-
-handle_call({create, Room, From, Nick, Opts},
- _From,
- #state{host = Host,
- server_host = ServerHost,
- access = Access,
- default_room_opts = DefOpts,
- history_size = HistorySize,
- room_shaper = RoomShaper} = State) ->
- ?DEBUG("MUC: create new room '~s'~n", [Room]),
- NewOpts = case Opts of
- default -> DefOpts;
- _ -> Opts
- end,
- {ok, Pid} = mod_muc_room:start(
- Host, ServerHost, Access,
- Room, HistorySize,
- RoomShaper, From,
- Nick, NewOpts, ?MODULE),
- register_room(Host, Room, Pid),
- {reply, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-handle_info({route, From, To, Packet},
- #state{host = Host,
- server_host = ServerHost,
- access = Access,
- default_room_opts = DefRoomOpts,
- history_size = HistorySize,
- room_shaper = RoomShaper} = State) ->
- case catch do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
- From, To, Packet, DefRoomOpts) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p", [Reason]);
- _ ->
- ok
- end,
- {noreply, State};
-handle_info({room_destroyed, RoomHost, Pid}, State) ->
- F = fun() ->
- mnesia:delete_object(#muc_online_room{name_host = RoomHost,
- pid = Pid})
- end,
- mnesia:transaction(F),
- {noreply, State};
-handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
- clean_table_from_bad_node(Node),
- {noreply, State};
-handle_info(_Info, State) ->
- {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-terminate(_Reason, State) ->
- ejabberd_router:unregister_route(State#state.host),
- ok.
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-start_supervisor(Host) ->
- Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
- ChildSpec =
- {Proc,
- {ejabberd_tmp_sup, start_link,
- [Proc, mod_muc_room]},
- permanent,
- infinity,
- supervisor,
- [ejabberd_tmp_sup]},
- supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop_supervisor(Host) ->
- Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup),
- supervisor:terminate_child(ejabberd_sup, Proc),
- supervisor:delete_child(ejabberd_sup, Proc).
-
-do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
- From, To, Packet, DefRoomOpts) ->
- {AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent} = Access,
- case acl:match_rule(ServerHost, AccessRoute, From) of
- allow ->
- do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
- From, To, Packet, DefRoomOpts);
- _ ->
- {xmlelement, _Name, Attrs, _Els} = Packet,
- Lang = xml:get_attr_s("xml:lang", Attrs),
- ErrText = "Access denied by service policy",
- Err = jlib:make_error_reply(Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route_error(To, From, Err, Packet)
- end.
-
-
-do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
- From, To, Packet, DefRoomOpts) ->
- {_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access,
- {Room, _, Nick} = jlib:jid_tolower(To),
- {xmlelement, Name, Attrs, _Els} = Packet,
- case Room of
- "" ->
- case Nick of
- "" ->
- case Name of
- "iq" ->
- case jlib:iq_query_info(Packet) of
- #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
- sub_el = _SubEl, lang = Lang} = IQ ->
- Info = ejabberd_hooks:run_fold(
- disco_info, ServerHost, [],
- [ServerHost, ?MODULE, "", ""]),
- Res = IQ#iq{type = result,
- sub_el = [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- iq_disco_info(Lang)
- ++Info}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- #iq{type = get,
- xmlns = ?NS_DISCO_ITEMS} = IQ ->
- spawn(?MODULE,
- process_iq_disco_items,
- [Host, From, To, IQ]);
- #iq{type = get,
- xmlns = ?NS_REGISTER = XMLNS,
- lang = Lang,
- sub_el = _SubEl} = IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- iq_get_register_info(
- ServerHost, Host, From, Lang)}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- #iq{type = set,
- xmlns = ?NS_REGISTER = XMLNS,
- lang = Lang,
- sub_el = SubEl} = IQ ->
- case process_iq_register_set(
- ServerHost, Host, From, SubEl, Lang) of
- {result, IQRes} ->
- Res = IQ#iq{type = result,
- sub_el =
- [{xmlelement, "query",
- [{"xmlns", XMLNS}],
- IQRes}]},
- ejabberd_router:route(
- To, From, jlib:iq_to_xml(Res));
- {error, Error} ->
- Err = jlib:make_error_reply(
- Packet, Error),
- ejabberd_router:route(
- To, From, Err)
- end;
- #iq{type = get,
- xmlns = ?NS_VCARD = XMLNS,
- lang = Lang,
- sub_el = _SubEl} = IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [{xmlelement, "vCard",
- [{"xmlns", XMLNS}],
- iq_get_vcard(Lang)}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- #iq{type = get,
- xmlns = ?NS_MUC_UNIQUE
- } = IQ ->
- Res = IQ#iq{type = result,
- sub_el =
- [{xmlelement, "unique",
- [{"xmlns", ?NS_MUC_UNIQUE}],
- [iq_get_unique(From)]}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(Res));
- #iq{} ->
- Err = jlib:make_error_reply(
- Packet,
- ?ERR_FEATURE_NOT_IMPLEMENTED),
- ejabberd_router:route(To, From, Err);
- _ ->
- ok
- end;
- "message" ->
- case xml:get_attr_s("type", Attrs) of
- "error" ->
- ok;
- _ ->
- case acl:match_rule(ServerHost, AccessAdmin, From) of
- allow ->
- Msg = xml:get_path_s(
- Packet,
- [{elem, "body"}, cdata]),
- broadcast_service_message(Host, Msg);
- _ ->
- Lang = xml:get_attr_s("xml:lang", Attrs),
- ErrText = "Only service administrators "
- "are allowed to send service messages",
- Err = jlib:make_error_reply(
- Packet,
- ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(
- To, From, Err)
- end
- end;
- "presence" ->
- ok
- end;
- _ ->
- case xml:get_attr_s("type", Attrs) of
- "error" ->
- ok;
- "result" ->
- ok;
- _ ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_ITEM_NOT_FOUND),
- ejabberd_router:route(To, From, Err)
- end
- end;
- _ ->
- case mnesia:dirty_read(muc_online_room, {Room, Host}) of
- [] ->
- Type = xml:get_attr_s("type", Attrs),
- case {Name, Type} of
- {"presence", ""} ->
- case check_user_can_create_room(ServerHost,
- AccessCreate, From,
- Room) of
- true ->
- {ok, Pid} = start_new_room(
- Host, ServerHost, Access,
- Room, HistorySize,
- RoomShaper, From,
- Nick, DefRoomOpts),
- register_room(Host, Room, Pid),
- mod_muc_room:route(Pid, From, Nick, Packet),
- ok;
- false ->
- Lang = xml:get_attr_s("xml:lang", Attrs),
- ErrText = "Room creation is denied by service policy",
- Err = jlib:make_error_reply(
- Packet, ?ERRT_FORBIDDEN(Lang, ErrText)),
- ejabberd_router:route(To, From, Err)
- end;
- _ ->
- Lang = xml:get_attr_s("xml:lang", Attrs),
- ErrText = "Conference room does not exist",
- Err = jlib:make_error_reply(
- Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)),
- ejabberd_router:route(To, From, Err)
- end;
- [R] ->
- Pid = R#muc_online_room.pid,
- ?DEBUG("MUC: send to process ~p~n", [Pid]),
- mod_muc_room:route(Pid, From, Nick, Packet),
- ok
- end
- end.
-
-check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
- case acl:match_rule(ServerHost, AccessCreate, From) of
- allow ->
- (length(RoomID) =< gen_mod:get_module_opt(ServerHost, ?MODULE,
- max_room_id, infinite));
- _ ->
- false
- end.
-
-
-load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- case catch ejabberd_odbc:sql_query(
- LServer, ["select name, opts from muc_room ",
- "where host='", SHost, "';"]) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p", [Reason]),
- ok;
- {selected, ["name", "opts"], RoomOpts} ->
- lists:foreach(
- fun({Room, Opts}) ->
- case mnesia:dirty_read(muc_online_room, {Room, Host}) of
- [] ->
- {ok, Pid} = mod_muc_room:start(
- Host,
- ServerHost,
- Access,
- Room,
- HistorySize,
- RoomShaper,
- ejabberd_odbc:decode_term(Opts),
- ?MODULE),
- register_room(Host, Room, Pid);
- _ ->
- ok
- end
- end, RoomOpts)
- end.
-
-start_new_room(Host, ServerHost, Access, Room,
- HistorySize, RoomShaper, From,
- Nick, DefRoomOpts) ->
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- SRoom = ejabberd_odbc:escape(Room),
- case ejabberd_odbc:sql_query(
- LServer, ["select opts from muc_room where name='", SRoom,
- "' and host='", SHost, "';"]) of
- {selected, ["opts"], []} ->
- ?DEBUG("MUC: open new room '~s'~n", [Room]),
- mod_muc_room:start(Host, ServerHost, Access,
- Room, HistorySize,
- RoomShaper, From,
- Nick, DefRoomOpts, ?MODULE);
- {selected, ["opts"], [{Opts}|_]} ->
- ?DEBUG("MUC: restore room '~s'~n", [Room]),
- mod_muc_room:start(Host, ServerHost, Access,
- Room, HistorySize,
- RoomShaper, ejabberd_odbc:decode_term(Opts),
- ?MODULE)
- end.
-
-register_room(Host, Room, Pid) ->
- F = fun() ->
- mnesia:write(#muc_online_room{name_host = {Room, Host},
- pid = Pid})
- end,
- mnesia:transaction(F).
-
-
-iq_disco_info(Lang) ->
- [{xmlelement, "identity",
- [{"category", "conference"},
- {"type", "text"},
- {"name", translate:translate(Lang, "Chatrooms")}], []},
- {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
- {xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []},
- {xmlelement, "feature", [{"var", ?NS_MUC}], []},
- {xmlelement, "feature", [{"var", ?NS_MUC_UNIQUE}], []},
- {xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
- {xmlelement, "feature", [{"var", ?NS_RSM}], []},
- {xmlelement, "feature", [{"var", ?NS_VCARD}], []}].
-
-
-iq_disco_items(Host, From, Lang, none) ->
- lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
- case catch gen_fsm:sync_send_all_state_event(
- Pid, {get_disco_item, From, Lang}, 100) of
- {item, Desc} ->
- flush(),
- {true,
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string({Name, Host, ""})},
- {"name", Desc}], []}};
- _ ->
- false
- end
- end, get_vh_rooms(Host));
-
-iq_disco_items(Host, From, Lang, Rsm) ->
- {Rooms, RsmO} = get_vh_rooms(Host, Rsm),
- RsmOut = jlib:rsm_encode(RsmO),
- lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
- case catch gen_fsm:sync_send_all_state_event(
- Pid, {get_disco_item, From, Lang}, 100) of
- {item, Desc} ->
- flush(),
- {true,
- {xmlelement, "item",
- [{"jid", jlib:jid_to_string({Name, Host, ""})},
- {"name", Desc}], []}};
- _ ->
- false
- end
- end, Rooms) ++ RsmOut.
-
-get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})->
- AllRooms = lists:sort(get_vh_rooms(Host)),
- Count = erlang:length(AllRooms),
- Guard = case Direction of
- _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
- aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
- before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
- _ -> [{'==', {element, 2, '$1'}, Host}]
- end,
- L = lists:sort(
- mnesia:dirty_select(muc_online_room,
- [{#muc_online_room{name_host = '$1', _ = '_'},
- Guard,
- ['$_']}])),
- L2 = if
- Index == undefined andalso Direction == before ->
- lists:reverse(lists:sublist(lists:reverse(L), 1, M));
- Index == undefined ->
- lists:sublist(L, 1, M);
- Index > Count orelse Index < 0 ->
- [];
- true ->
- lists:sublist(L, Index+1, M)
- end,
- if
- L2 == [] ->
- {L2, #rsm_out{count=Count}};
- true ->
- H = hd(L2),
- NewIndex = get_room_pos(H, AllRooms),
- T=lists:last(L2),
- {F, _}=H#muc_online_room.name_host,
- {Last, _}=T#muc_online_room.name_host,
- {L2, #rsm_out{first=F, last=Last, count=Count, index=NewIndex}}
- end.
-
-%% @doc Return the position of desired room in the list of rooms.
-%% The room must exist in the list. The count starts in 0.
-%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
-get_room_pos(Desired, Rooms) ->
- get_room_pos(Desired, Rooms, 0).
-get_room_pos(Desired, [HeadRoom | _], HeadPosition)
- when (Desired#muc_online_room.name_host ==
- HeadRoom#muc_online_room.name_host) ->
- HeadPosition;
-get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
- get_room_pos(Desired, Rooms, HeadPosition + 1).
-
-flush() ->
- receive
- _ ->
- flush()
- after 0 ->
- ok
- end.
-
--define(XFIELD(Type, Label, Var, Val),
- {xmlelement, "field", [{"type", Type},
- {"label", translate:translate(Lang, Label)},
- {"var", Var}],
- [{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
-
-%% @doc Get a pseudo unique Room Name. The Room Name is generated as a hash of
-%% the requester JID, the local time and a random salt.
-%%
-%% "pseudo" because we don't verify that there is not a room
-%% with the returned Name already created, nor mark the generated Name
-%% as "already used". But in practice, it is unique enough. See
-%% http://xmpp.org/extensions/xep-0045.html#createroom-unique
-iq_get_unique(From) ->
- {xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}.
-
-iq_get_register_info(ServerHost, Host, From, Lang) ->
- SJID = ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From)))),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- {Nick, Registered} =
- case catch ejabberd_odbc:sql_query(
- LServer, ["select nick from muc_registered where "
- "jid='", SJID, "' and host='", SHost, "';"]) of
- {selected, ["nick"], [{N}]} ->
- {N, [{xmlelement, "registered", [], []}]};
- _ ->
- {"", []}
- end,
- Registered ++
- [{xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "You need a client that supports x:data to register the nickname")}]},
- {xmlelement, "x",
- [{"xmlns", ?NS_XDATA}],
- [{xmlelement, "title", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Nickname Registration at ") ++ Host}]},
- {xmlelement, "instructions", [],
- [{xmlcdata,
- translate:translate(
- Lang, "Enter nickname you want to register")}]},
- ?XFIELD("text-single", "Nickname", "nick", Nick)]}].
-
-iq_set_register_info(ServerHost, Host, From, Nick, Lang) ->
- JID = jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:jid_remove_resource(From))),
- SJID = ejabberd_odbc:escape(JID),
- SNick = ejabberd_odbc:escape(Nick),
- SHost = ejabberd_odbc:escape(Host),
- LServer = jlib:nameprep(ServerHost),
- F = fun() ->
- case Nick of
- "" ->
- ejabberd_odbc:sql_query_t(
- ["delete from muc_registered where ",
- "jid='", SJID, "' and host='", Host, "';"]),
- ok;
- _ ->
- Allow =
- case ejabberd_odbc:sql_query_t(
- ["select jid from muc_registered ",
- "where nick='", SNick, "' and host='",
- SHost, "';"]) of
- {selected, ["jid"], [{J}]} ->
- J == JID;
- _ ->
- true
- end,
- if Allow ->
- odbc_queries:update_t(
- "muc_registered",
- ["jid", "host", "nick"],
- [SJID, SHost, SNick],
- ["jid='", SJID, "' and host='", SHost, "'"]),
- ok;
- true ->
- false
- end
- end
- end,
- case catch ejabberd_odbc:sql_transaction(LServer, F) of
- {atomic, ok} ->
- {result, []};
- {atomic, false} ->
- ErrText = "That nickname is registered by another person",
- {error, ?ERRT_CONFLICT(Lang, ErrText)};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-process_iq_register_set(ServerHost, Host, From, SubEl, Lang) ->
- {xmlelement, _Name, _Attrs, Els} = SubEl,
- case xml:get_subtag(SubEl, "remove") of
- false ->
- case xml:remove_cdata(Els) of
- [{xmlelement, "x", _Attrs1, _Els1} = XEl] ->
- case {xml:get_tag_attr_s("xmlns", XEl),
- xml:get_tag_attr_s("type", XEl)} of
- {?NS_XDATA, "cancel"} ->
- {result, []};
- {?NS_XDATA, "submit"} ->
- XData = jlib:parse_xdata_submit(XEl),
- case XData of
- invalid ->
- {error, ?ERR_BAD_REQUEST};
- _ ->
- case lists:keysearch("nick", 1, XData) of
- {value, {_, [Nick]}} when Nick /= "" ->
- iq_set_register_info(ServerHost, Host,
- From, Nick, Lang);
- _ ->
- ErrText = "You must fill in field \"Nickname\" in the form",
- {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
- end
- end;
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end;
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end;
- _ ->
- iq_set_register_info(ServerHost, Host, From, "", Lang)
- end.
-
-iq_get_vcard(Lang) ->
- [{xmlelement, "FN", [],
- [{xmlcdata, "ejabberd/mod_muc"}]},
- {xmlelement, "URL", [],
- [{xmlcdata, ?EJABBERD_URI}]},
- {xmlelement, "DESC", [],
- [{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++
- "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-
-broadcast_service_message(Host, Msg) ->
- lists:foreach(
- fun(#muc_online_room{pid = Pid}) ->
- gen_fsm:send_all_state_event(
- Pid, {service_message, Msg})
- end, get_vh_rooms(Host)).
-
-get_vh_rooms(Host) ->
- mnesia:dirty_select(muc_online_room,
- [{#muc_online_room{name_host = '$1', _ = '_'},
- [{'==', {element, 2, '$1'}, Host}],
- ['$_']}]).
-
-
-clean_table_from_bad_node(Node) ->
- F = fun() ->
- Es = mnesia:select(
- muc_online_room,
- [{#muc_online_room{pid = '$1', _ = '_'},
- [{'==', {node, '$1'}, Node}],
- ['$_']}]),
- lists:foreach(fun(E) ->
- mnesia:delete_object(E)
- end, Es)
- end,
- mnesia:async_dirty(F).
-
-clean_table_from_bad_node(Node, Host) ->
- F = fun() ->
- Es = mnesia:select(
- muc_online_room,
- [{#muc_online_room{pid = '$1',
- name_host = {'_', Host},
- _ = '_'},
- [{'==', {node, '$1'}, Node}],
- ['$_']}]),
- lists:foreach(fun(E) ->
- mnesia:delete_object(E)
- end, Es)
- end,
- mnesia:async_dirty(F).
diff --git a/src/mod_muc/mod_muc_room.erl b/src/mod_muc/mod_muc_room.erl
index a5845175c..670460be7 100644
--- a/src/mod_muc/mod_muc_room.erl
+++ b/src/mod_muc/mod_muc_room.erl
@@ -31,10 +31,10 @@
%% External exports
--export([start_link/10,
- start_link/8,
- start/10,
- start/8,
+-export([start_link/9,
+ start_link/7,
+ start/9,
+ start/7,
route/4]).
%% gen_fsm callbacks
@@ -65,38 +65,38 @@
-ifdef(NO_TRANSIENT_SUPERVISORS).
-define(SUPERVISOR_START,
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
- RoomShaper, Creator, Nick, DefRoomOpts, Mod],
+ RoomShaper, Creator, Nick, DefRoomOpts],
?FSMOPTS)).
-else.
-define(SUPERVISOR_START,
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
supervisor:start_child(
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
- Creator, Nick, DefRoomOpts, Mod])).
+ Creator, Nick, DefRoomOpts])).
-endif.
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
start(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
- Creator, Nick, DefRoomOpts, Mod) ->
+ Creator, Nick, DefRoomOpts) ->
?SUPERVISOR_START.
-start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) ->
+start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
supervisor:start_child(
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
- Opts, Mod]).
+ Opts]).
start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
- Creator, Nick, DefRoomOpts, Mod) ->
+ Creator, Nick, DefRoomOpts) ->
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
- RoomShaper, Creator, Nick, DefRoomOpts, Mod],
+ RoomShaper, Creator, Nick, DefRoomOpts],
?FSMOPTS).
-start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) ->
+start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts) ->
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
- RoomShaper, Opts, Mod],
+ RoomShaper, Opts],
?FSMOPTS).
%%%----------------------------------------------------------------------
@@ -110,14 +110,12 @@ start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) -
%% ignore |
%% {stop, StopReason}
%%----------------------------------------------------------------------
-init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick,
- DefRoomOpts, Mod]) ->
+init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts]) ->
process_flag(trap_exit, true),
Shaper = shaper:new(RoomShaper),
State = set_affiliation(Creator, owner,
#state{host = Host,
server_host = ServerHost,
- mod = Mod,
access = Access,
room = Room,
history = lqueue_new(HistorySize),
@@ -130,12 +128,11 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick,
add_to_log(room_existence, created, State1),
add_to_log(room_existence, started, State1),
{ok, normal_state, State1};
-init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod]) ->
+init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts]) ->
process_flag(trap_exit, true),
Shaper = shaper:new(RoomShaper),
State = set_opts(Opts, #state{host = Host,
server_host = ServerHost,
- mod = Mod,
access = Access,
room = Room,
history = lqueue_new(HistorySize),
@@ -164,8 +161,7 @@ normal_state({route, From, "",
MinMessageInterval =
trunc(gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod,
- min_message_interval, 0) * 1000000),
+ mod_muc, min_message_interval, 0) * 1000000),
Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} =
shaper:update(Activity#activity.message_shaper, Size),
@@ -286,7 +282,7 @@ normal_state({route, From, "",
StateData),
case (NSD#state.config)#config.persistent of
true ->
- (NSD#state.mod):store_room(
+ mod_muc:store_room(
NSD#state.server_host,
NSD#state.host,
NSD#state.room,
@@ -485,7 +481,7 @@ normal_state({route, From, Nick,
MinPresenceInterval =
trunc(gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, min_presence_interval, 0) * 1000000),
+ mod_muc, min_presence_interval, 0) * 1000000),
if
(Now >= Activity#activity.presence_time + MinPresenceInterval) and
(Activity#activity.presence == undefined) ->
@@ -855,9 +851,8 @@ terminate(Reason, _StateName, StateData) ->
tab_remove_online_user(LJID, StateData)
end, [], StateData#state.users),
add_to_log(room_existence, stopped, StateData),
- (StateData#state.mod):room_destroyed(
- StateData#state.host, StateData#state.room, self(),
- StateData#state.server_host),
+ mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
+ StateData#state.server_host),
ok.
%%%----------------------------------------------------------------------
@@ -892,7 +887,7 @@ process_groupchat_message(From, {xmlelement, "message", Attrs, _Els} = Packet,
FromNick},
case (NSD#state.config)#config.persistent of
true ->
- (NSD#state.mod):store_room(
+ mod_muc:store_room(
NSD#state.server_host,
NSD#state.host,
NSD#state.room,
@@ -1031,9 +1026,9 @@ process_presence(From, Nick, {xmlelement, "presence", Attrs, _Els} = Packet,
case is_nick_change(From, Nick, StateData) of
true ->
case {nick_collision(From, Nick, StateData),
- (StateData#state.mod):can_use_nick(
- StateData#state.server_host,
- StateData#state.host, From, Nick),
+ mod_muc:can_use_nick(
+ StateData#state.server_host,
+ StateData#state.host, From, Nick),
{(StateData#state.config)#config.allow_visitor_nickchange,
is_visitor(From, StateData)}} of
{_, _, {false, true}} ->
@@ -1428,11 +1423,11 @@ get_max_users(StateData) ->
get_service_max_users(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
- StateData#state.mod, max_users, ?MAX_USERS_DEFAULT).
+ mod_muc, max_users, ?MAX_USERS_DEFAULT).
get_max_users_admin_threshold(StateData) ->
gen_mod:get_module_opt(StateData#state.server_host,
- StateData#state.mod, max_users_admin_threshold, 5).
+ mod_muc, max_users_admin_threshold, 5).
get_user_activity(JID, StateData) ->
case treap:lookup(jlib:jid_tolower(JID),
@@ -1442,11 +1437,11 @@ get_user_activity(JID, StateData) ->
MessageShaper =
shaper:new(gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, user_message_shaper, none)),
+ mod_muc, user_message_shaper, none)),
PresenceShaper =
shaper:new(gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, user_presence_shaper, none)),
+ mod_muc, user_presence_shaper, none)),
#activity{message_shaper = MessageShaper,
presence_shaper = PresenceShaper}
end.
@@ -1455,11 +1450,11 @@ store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, min_message_interval, 0),
+ mod_muc, min_message_interval, 0),
MinPresenceInterval =
gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, min_presence_interval, 0),
+ mod_muc, min_presence_interval, 0),
Key = jlib:jid_tolower(JID),
Now = now_to_usec(now()),
Activity1 = clean_treap(StateData#state.activity, {1, -Now}),
@@ -1740,7 +1735,7 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
NConferences = tab_count_user(From),
MaxConferences = gen_mod:get_module_opt(
StateData#state.server_host,
- StateData#state.mod, max_user_conferences, 10),
+ mod_muc, max_user_conferences, 10),
Collision = nick_collision(From, Nick, StateData),
case {(ServiceAffiliation == owner orelse
((Affiliation == admin orelse Affiliation == owner) andalso
@@ -1748,8 +1743,9 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
NUsers < MaxUsers) andalso
NConferences < MaxConferences,
Collision,
- (StateData#state.mod):can_use_nick(StateData#state.server_host,
- StateData#state.host, From, Nick),
+ mod_muc:can_use_nick(
+ StateData#state.server_host,
+ StateData#state.host, From, Nick),
get_default_role(Affiliation, StateData)} of
{false, _, _, _} ->
% max user reached and user is not admin or owner
@@ -2589,9 +2585,9 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
end, StateData, lists:flatten(Res)),
case (NSD#state.config)#config.persistent of
true ->
- (NSD#state.mod):store_room(NSD#state.server_host,
- NSD#state.host, NSD#state.room,
- make_opts(NSD));
+ mod_muc:store_room(NSD#state.server_host,
+ NSD#state.host, NSD#state.room,
+ make_opts(NSD));
_ ->
ok
end,
@@ -3091,8 +3087,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
jlib:parse_xdata_submit(XEl)) of
{value, {_, [N]}} ->
length(N) =< gen_mod:get_module_opt(StateData#state.server_host,
- StateData#state.mod,
- max_room_name, infinite);
+ mod_muc, max_room_name,
+ infinite);
_ ->
true
end,
@@ -3101,8 +3097,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
jlib:parse_xdata_submit(XEl)) of
{value, {_, [D]}} ->
length(D) =< gen_mod:get_module_opt(StateData#state.server_host,
- StateData#state.mod,
- max_room_desc, infinite);
+ mod_muc, max_room_desc,
+ infinite);
_ ->
true
end,
@@ -3173,9 +3169,7 @@ is_password_settings_correct(XEl, StateData) ->
|| JID <- JIDList]}).
get_default_room_maxusers(RoomState) ->
- DefRoomOpts = gen_mod:get_module_opt(
- RoomState#state.server_host,
- RoomState#state.mod, default_room_options, []),
+ DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
RoomState2 = set_opts(DefRoomOpts, RoomState),
(RoomState2#state.config)#config.max_users.
@@ -3487,14 +3481,14 @@ set_xoption([_ | _Opts], _Config) ->
change_config(Config, StateData) ->
NSD = StateData#state{config = Config},
- Mod = StateData#state.mod,
case {(StateData#state.config)#config.persistent,
Config#config.persistent} of
{_, true} ->
- Mod:store_room(NSD#state.server_host, NSD#state.host,
- NSD#state.room, make_opts(NSD));
+ mod_muc:store_room(NSD#state.server_host, NSD#state.host,
+ NSD#state.room, make_opts(NSD));
{true, false} ->
- Mod:forget_room(NSD#state.server_host, NSD#state.host, NSD#state.room);
+ mod_muc:forget_room(NSD#state.server_host, NSD#state.host,
+ NSD#state.room);
{false, false} ->
ok
end,
@@ -3625,7 +3619,7 @@ destroy_room(DEl, StateData) ->
end, ?DICT:to_list(StateData#state.users)),
case (StateData#state.config)#config.persistent of
true ->
- (StateData#state.mod):forget_room(
+ mod_muc:forget_room(
StateData#state.server_host,
StateData#state.host, StateData#state.room);
false ->
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 20874c833..75b1966b2 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : mod_offline.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Store and manage offline messages in Mnesia database.
+%%% Purpose : Store and manage offline messages.
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
@@ -29,15 +29,17 @@
-behaviour(gen_mod).
+-export([count_offline_messages/2]).
+
-export([start/2,
- loop/1,
+ loop/2,
stop/1,
store_packet/3,
resend_offline_messages/2,
pop_offline_messages/3,
get_sm_features/5,
- remove_expired_messages/0,
- remove_old_messages/1,
+ remove_expired_messages/1,
+ remove_old_messages/2,
remove_user/2,
get_queue_length/2,
webadmin_page/3,
@@ -58,11 +60,17 @@
-define(MAX_USER_MESSAGES, infinity).
start(Host, Opts) ->
- mnesia:create_table(offline_msg,
- [{disc_only_copies, [node()]},
- {type, bag},
- {attributes, record_info(fields, offline_msg)}]),
- update_table(),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(offline_msg,
+ [{disc_only_copies, [node()]},
+ {type, bag},
+ {attributes,
+ record_info(fields, offline_msg)}]),
+ update_table();
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, store_packet, 50),
ejabberd_hooks:add(resend_offline_messages_hook, Host,
@@ -83,44 +91,88 @@ start(Host, Opts) ->
?MODULE, webadmin_user_parse_query, 50),
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
- spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
+ spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
-loop(AccessMaxOfflineMsgs) ->
+loop(Host, AccessMaxOfflineMsgs) ->
receive
- #offline_msg{us=US} = Msg ->
- Msgs = receive_all(US, [Msg]),
+ #offline_msg{us = User} = Msg ->
+ DBType = gen_mod:db_type(Host, ?MODULE),
+ Msgs = receive_all(User, [Msg], DBType),
Len = length(Msgs),
- {User, Host} = US,
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
User, Host),
- F = fun() ->
- %% Only count messages if needed:
- Count = if MaxOfflineMsgs =/= infinity ->
- Len + p1_mnesia:count_records(
- offline_msg,
- #offline_msg{us=US, _='_'});
- true ->
- 0
- end,
- if
- Count > MaxOfflineMsgs ->
- discard_warn_sender(Msgs);
- true ->
- if
- Len >= ?OFFLINE_TABLE_LOCK_THRESHOLD ->
- mnesia:write_lock_table(offline_msg);
- true ->
- ok
- end,
- lists:foreach(fun(M) ->
- mnesia:write(M)
- end, Msgs)
- end
- end,
- mnesia:transaction(F),
- loop(AccessMaxOfflineMsgs);
- _ ->
- loop(AccessMaxOfflineMsgs)
+ store_offline_msg(Host, User, Msgs, Len, MaxOfflineMsgs, DBType),
+ loop(Host, AccessMaxOfflineMsgs);
+ _ ->
+ loop(Host, AccessMaxOfflineMsgs)
+ end.
+
+store_offline_msg(_Host, US, Msgs, Len, MaxOfflineMsgs, mnesia) ->
+ F = fun() ->
+ %% Only count messages if needed:
+ Count = if MaxOfflineMsgs =/= infinity ->
+ Len + p1_mnesia:count_records(
+ offline_msg,
+ #offline_msg{us=US, _='_'});
+ true ->
+ 0
+ end,
+ if
+ Count > MaxOfflineMsgs ->
+ discard_warn_sender(Msgs);
+ true ->
+ if
+ Len >= ?OFFLINE_TABLE_LOCK_THRESHOLD ->
+ mnesia:write_lock_table(offline_msg);
+ true ->
+ ok
+ end,
+ lists:foreach(fun(M) ->
+ mnesia:write(M)
+ end, Msgs)
+ end
+ end,
+ mnesia:transaction(F);
+store_offline_msg(Host, User, Msgs, Len, MaxOfflineMsgs, odbc) ->
+ Count = if MaxOfflineMsgs =/= infinity ->
+ Len + count_offline_messages(User, Host);
+ true -> 0
+ end,
+ if
+ Count > MaxOfflineMsgs ->
+ discard_warn_sender(Msgs);
+ true ->
+ Query = lists:map(
+ fun(M) ->
+ Username =
+ ejabberd_odbc:escape(
+ (M#offline_msg.to)#jid.luser),
+ From = M#offline_msg.from,
+ To = M#offline_msg.to,
+ {xmlelement, Name, Attrs, Els} =
+ M#offline_msg.packet,
+ Attrs2 = jlib:replace_from_to_attrs(
+ jlib:jid_to_string(From),
+ jlib:jid_to_string(To),
+ Attrs),
+ Packet = {xmlelement, Name, Attrs2,
+ Els ++
+ [jlib:timestamp_to_xml(
+ calendar:now_to_universal_time(
+ M#offline_msg.timestamp),
+ utc,
+ jlib:make_jid("", Host, ""),
+ "Offline Storage"),
+ %% TODO: Delete the next three lines once XEP-0091 is Obsolete
+ jlib:timestamp_to_xml(
+ calendar:now_to_universal_time(
+ M#offline_msg.timestamp))]},
+ XML =
+ ejabberd_odbc:escape(
+ xml:element_to_binary(Packet)),
+ odbc_queries:add_spool_sql(Username, XML)
+ end, Msgs),
+ odbc_queries:add_spool(Host, Query)
end.
%% Function copied from ejabberd_sm.erl:
@@ -132,15 +184,27 @@ get_max_user_messages(AccessRule, LUser, Host) ->
_ -> ?MAX_USER_MESSAGES
end.
-receive_all(US, Msgs) ->
+receive_all(US, Msgs, DBType) ->
receive
#offline_msg{us=US} = Msg ->
- receive_all(US, [Msg | Msgs])
+ receive_all(US, [Msg | Msgs], DBType)
after 0 ->
- Msgs
+ %% FIXME: the diff between mnesia and odbc version:
+ %%
+ %% after 0 ->
+ %% - Msgs
+ %% + lists:reverse(Msgs)
+ %% end.
+ %%
+ %% Is it a bug in mnesia version?
+ case DBType of
+ mnesia ->
+ Msgs;
+ odbc ->
+ lists:reverse(Msgs)
+ end
end.
-
stop(Host) ->
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, store_packet, 50),
@@ -325,6 +389,10 @@ resend_offline_messages(User, Server) ->
pop_offline_messages(Ls, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ pop_offline_messages(Ls, LUser, LServer,
+ gen_mod:db_type(LServer, ?MODULE)).
+
+pop_offline_messages(Ls, LUser, LServer, mnesia) ->
US = {LUser, LServer},
F = fun() ->
Rs = mnesia:wread({offline_msg, US}),
@@ -346,7 +414,7 @@ pop_offline_messages(Ls, User, Server) ->
calendar:now_to_universal_time(
R#offline_msg.timestamp),
utc,
- jlib:make_jid("", Server, ""),
+ jlib:make_jid("", LServer, ""),
"Offline Storage"),
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
jlib:timestamp_to_xml(
@@ -365,10 +433,39 @@ pop_offline_messages(Ls, User, Server) ->
lists:keysort(#offline_msg.timestamp, Rs)));
_ ->
Ls
+ end;
+pop_offline_messages(Ls, LUser, LServer, odbc) ->
+ EUser = ejabberd_odbc:escape(LUser),
+ case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of
+ {atomic, {selected, ["username","xml"], Rs}} ->
+ Ls ++ lists:flatmap(
+ fun({_, XML}) ->
+ case xml_stream:parse_element(XML) of
+ {error, _Reason} ->
+ [];
+ El ->
+ To = jlib:string_to_jid(
+ xml:get_tag_attr_s("to", El)),
+ From = jlib:string_to_jid(
+ xml:get_tag_attr_s("from", El)),
+ if
+ (To /= error) and
+ (From /= error) ->
+ [{route, From, To, El}];
+ true ->
+ []
+ end
+ end
+ end, Rs);
+ _ ->
+ Ls
end.
+remove_expired_messages(Server) ->
+ LServer = jlib:nameprep(Server),
+ remove_expired_messages(LServer, gen_mod:db_type(LServer, ?MODULE)).
-remove_expired_messages() ->
+remove_expired_messages(_LServer, mnesia) ->
TimeStamp = now(),
F = fun() ->
mnesia:write_lock_table(offline_msg),
@@ -387,9 +484,16 @@ remove_expired_messages() ->
end
end, ok, offline_msg)
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+remove_expired_messages(_LServer, odbc) ->
+ %% TODO
+ {atomic, ok}.
-remove_old_messages(Days) ->
+remove_old_messages(Days, Server) ->
+ LServer = jlib:nameprep(Server),
+ remove_old_messages(Days, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_old_messages(Days, _LServer, mnesia) ->
{MegaSecs, Secs, _MicroSecs} = now(),
S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
MegaSecs1 = S div 1000000,
@@ -404,16 +508,25 @@ remove_old_messages(Days) ->
(_Rec, _Acc) -> ok
end, ok, offline_msg)
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+remove_old_messages(_Days, _LServer, odbc) ->
+ %% TODO
+ {atomic, ok}.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete({offline_msg, US})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:del_spool_msg(LServer, Username).
update_table() ->
Fields = record_info(fields, offline_msg),
@@ -527,36 +640,76 @@ webadmin_page(_, Host,
webadmin_page(Acc, _, _) -> Acc.
+read_all_msgs(LUser, LServer, mnesia) ->
+ US = {LUser, LServer},
+ lists:keysort(#offline_msg.timestamp,
+ mnesia:dirty_read({offline_msg, US}));
+read_all_msgs(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select xml from spool"
+ " where username='", Username, "'"
+ " order by seq;"]) of
+ {selected, ["username", "xml"], Rs} ->
+ lists:flatmap(
+ fun({XML}) ->
+ case xml_stream:parse_element(XML) of
+ {error, _Reason} ->
+ [];
+ El ->
+ [El]
+ end
+ end, Rs);
+ _ ->
+ []
+ end.
+
+format_user_queue(Msgs, mnesia) ->
+ lists:map(
+ fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
+ packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
+ ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+ {{Year, Month, Day}, {Hour, Minute, Second}} =
+ calendar:now_to_local_time(TimeStamp),
+ Time = lists:flatten(
+ io_lib:format(
+ "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
+ [Year, Month, Day, Hour, Minute, Second])),
+ SFrom = jlib:jid_to_string(From),
+ STo = jlib:jid_to_string(To),
+ Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
+ Packet = {xmlelement, Name, Attrs2, Els},
+ FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+ ?XE("tr",
+ [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+ ?XAC("td", [{"class", "valign"}], Time),
+ ?XAC("td", [{"class", "valign"}], SFrom),
+ ?XAC("td", [{"class", "valign"}], STo),
+ ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+ )
+ end, Msgs);
+format_user_queue(Msgs, odbc) ->
+ lists:map(
+ fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
+ ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
+ Packet = Msg,
+ FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
+ ?XE("tr",
+ [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
+ ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
+ )
+ end, Msgs).
+
user_queue(User, Server, Query, Lang) ->
- US = {jlib:nodeprep(User), jlib:nameprep(Server)},
- Res = user_queue_parse_query(US, Query),
- MsgsAll = lists:keysort(#offline_msg.timestamp,
- mnesia:dirty_read({offline_msg, US})),
- Msgs = get_messages_subset(User, Server, MsgsAll),
- FMsgs =
- lists:map(
- fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
- packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
- ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
- {{Year, Month, Day}, {Hour, Minute, Second}} =
- calendar:now_to_local_time(TimeStamp),
- Time = lists:flatten(
- io_lib:format(
- "~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
- [Year, Month, Day, Hour, Minute, Second])),
- SFrom = jlib:jid_to_string(From),
- STo = jlib:jid_to_string(To),
- Attrs2 = jlib:replace_from_to_attrs(SFrom, STo, Attrs),
- Packet = {xmlelement, Name, Attrs2, Els},
- FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
- ?XE("tr",
- [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
- ?XAC("td", [{"class", "valign"}], Time),
- ?XAC("td", [{"class", "valign"}], SFrom),
- ?XAC("td", [{"class", "valign"}], STo),
- ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
- )
- end, Msgs),
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ Res = user_queue_parse_query(LUser, LServer, Query, DBType),
+ MsgsAll = read_all_msgs(LUser, LServer, DBType),
+ Msgs = get_messages_subset(User, Server, MsgsAll, DBType),
+ FMsgs = format_user_queue(Msgs, DBType),
[?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
[us_to_list(US)]))] ++
case Res of
@@ -587,7 +740,8 @@ user_queue(User, Server, Query, Lang) ->
?INPUTT("submit", "delete", "Delete Selected")
])].
-user_queue_parse_query(US, Query) ->
+user_queue_parse_query(LUser, LServer, Query, mnesia) ->
+ US = {LUser, LServer},
case lists:keysearch("delete", 1, Query) of
{value, _} ->
Msgs = lists:keysort(#offline_msg.timestamp,
@@ -609,15 +763,74 @@ user_queue_parse_query(US, Query) ->
ok;
false ->
nothing
+ end;
+user_queue_parse_query(LUser, LServer, Query, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case lists:keysearch("delete", 1, Query) of
+ {value, _} ->
+ Msgs = case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select xml, seq from spool"
+ " where username='", Username, "'"
+ " order by seq;"]) of
+ {selected, ["xml", "seq"], Rs} ->
+ lists:flatmap(
+ fun({XML, Seq}) ->
+ case xml_stream:parse_element(XML) of
+ {error, _Reason} ->
+ [];
+ El ->
+ [{El, Seq}]
+ end
+ end, Rs);
+ _ ->
+ []
+ end,
+ F = fun() ->
+ lists:foreach(
+ fun({Msg, Seq}) ->
+ ID = jlib:encode_base64(
+ binary_to_list(term_to_binary(Msg))),
+ case lists:member({"selected", ID}, Query) of
+ true ->
+ SSeq = ejabberd_odbc:escape(Seq),
+ catch ejabberd_odbc:sql_query(
+ LServer,
+ ["delete from spool"
+ " where username='", Username, "'"
+ " and seq='", SSeq, "';"]);
+ false ->
+ ok
+ end
+ end, Msgs)
+ end,
+ mnesia:transaction(F),
+ ok;
+ false ->
+ nothing
end.
us_to_list({User, Server}) ->
jlib:jid_to_string({User, Server, ""}).
-get_queue_length(User, Server) ->
- length(mnesia:dirty_read({offline_msg, {User, Server}})).
+get_queue_length(LUser, LServer) ->
+ get_queue_length(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_queue_length(LUser, LServer, mnesia) ->
+ length(mnesia:dirty_read({offline_msg, {LUser, LServer}}));
+get_queue_length(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select count(*) from spool"
+ " where username='", Username, "';"]) of
+ {selected, [_], [{SCount}]} ->
+ list_to_integer(SCount);
+ _ ->
+ 0
+ end.
-get_messages_subset(User, Host, MsgsAll) ->
+get_messages_subset(User, Host, MsgsAll, DBType) ->
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
max_user_offline_messages),
MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
@@ -625,17 +838,23 @@ get_messages_subset(User, Host, MsgsAll) ->
_ -> 100
end,
Length = length(MsgsAll),
- get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
+ get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll, DBType).
-get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
+get_messages_subset2(Max, Length, MsgsAll, _DBType) when Length =< Max*2 ->
MsgsAll;
-get_messages_subset2(Max, Length, MsgsAll) ->
+get_messages_subset2(Max, Length, MsgsAll, mnesia) ->
FirstN = Max,
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
NoJID = jlib:make_jid("...", "...", ""),
IntermediateMsg = #offline_msg{timestamp = now(), from = NoJID, to = NoJID,
packet = {xmlelement, "...", [], []}},
+ MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN;
+get_messages_subset2(Max, Length, MsgsAll, odbc) ->
+ FirstN = Max,
+ {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
+ MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
+ IntermediateMsg = {xmlelement, "...", [], []},
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
webadmin_user(Acc, User, Server, Lang) ->
@@ -644,16 +863,29 @@ webadmin_user(Acc, User, Server, Lang) ->
integer_to_list(QueueLen))],
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
-webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
- US = {User, Server},
+delete_all_msgs(User, Server) ->
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ delete_all_msgs(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+delete_all_msgs(LUser, LServer, mnesia) ->
+ US = {LUser, LServer},
F = fun() ->
- mnesia:write_lock_table(offline_msg),
- lists:foreach(
- fun(Msg) ->
- mnesia:delete_object(Msg)
- end, mnesia:dirty_read({offline_msg, US}))
+ mnesia:write_lock_table(offline_msg),
+ lists:foreach(
+ fun(Msg) ->
+ mnesia:delete_object(Msg)
+ end, mnesia:dirty_read({offline_msg, US}))
end,
- case mnesia:transaction(F) of
+ mnesia:transaction(F);
+delete_all_msgs(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:del_spool_msg(LServer, Username),
+ %% TODO: process the output
+ {atomic, ok}.
+
+webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
+ case delete_all_msgs(User, Server) of
{aborted, Reason} ->
?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
{stop, error};
@@ -663,3 +895,14 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
end;
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
Acc.
+
+%% Returns as integer the number of offline messages for a given user
+count_offline_messages(LUser, LServer) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:count_records_where(
+ LServer, "spool", "where username='" ++ Username ++ "'") of
+ {selected, [_], [{Res}]} ->
+ list_to_integer(Res);
+ _ ->
+ 0
+ end.
diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl
deleted file mode 100644
index 0d30d5745..000000000
--- a/src/mod_offline_odbc.erl
+++ /dev/null
@@ -1,548 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_offline_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Store and manage offline messages in relational database.
-%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_offline_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([count_offline_messages/2]).
-
--export([start/2,
- loop/2,
- stop/1,
- store_packet/3,
- pop_offline_messages/3,
- get_sm_features/5,
- remove_user/2,
- get_queue_length/2,
- webadmin_page/3,
- webadmin_user/4,
- webadmin_user_parse_query/5]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
--record(offline_msg, {user, timestamp, expire, from, to, packet}).
-
--define(PROCNAME, ejabberd_offline).
--define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
-
-%% default value for the maximum number of user messages
--define(MAX_USER_MESSAGES, infinity).
-
-start(Host, Opts) ->
- ejabberd_hooks:add(offline_message_hook, Host,
- ?MODULE, store_packet, 50),
- ejabberd_hooks:add(resend_offline_messages_hook, Host,
- ?MODULE, pop_offline_messages, 50),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(disco_sm_features, Host,
- ?MODULE, get_sm_features, 50),
- ejabberd_hooks:add(disco_local_features, Host,
- ?MODULE, get_sm_features, 50),
- ejabberd_hooks:add(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:add(webadmin_user, Host,
- ?MODULE, webadmin_user, 50),
- ejabberd_hooks:add(webadmin_user_parse_query, Host,
- ?MODULE, webadmin_user_parse_query, 50),
- AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
- register(gen_mod:get_module_proc(Host, ?PROCNAME),
- spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
-
-loop(Host, AccessMaxOfflineMsgs) ->
- receive
- #offline_msg{user = User} = Msg ->
- Msgs = receive_all(User, [Msg]),
- Len = length(Msgs),
- MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
- User, Host),
-
- %% Only count existing messages if needed:
- Count = if MaxOfflineMsgs =/= infinity ->
- Len + count_offline_messages(User, Host);
- true -> 0
- end,
- if
- Count > MaxOfflineMsgs ->
- discard_warn_sender(Msgs);
- true ->
- Query = lists:map(
- fun(M) ->
- Username =
- ejabberd_odbc:escape(
- (M#offline_msg.to)#jid.luser),
- From = M#offline_msg.from,
- To = M#offline_msg.to,
- {xmlelement, Name, Attrs, Els} =
- M#offline_msg.packet,
- Attrs2 = jlib:replace_from_to_attrs(
- jlib:jid_to_string(From),
- jlib:jid_to_string(To),
- Attrs),
- Packet = {xmlelement, Name, Attrs2,
- Els ++
- [jlib:timestamp_to_xml(
- calendar:now_to_universal_time(
- M#offline_msg.timestamp),
- utc,
- jlib:make_jid("", Host, ""),
- "Offline Storage"),
- %% TODO: Delete the next three lines once XEP-0091 is Obsolete
- jlib:timestamp_to_xml(
- calendar:now_to_universal_time(
- M#offline_msg.timestamp))]},
- XML =
- ejabberd_odbc:escape(
- xml:element_to_binary(Packet)),
- odbc_queries:add_spool_sql(Username, XML)
- end, Msgs),
- case catch odbc_queries:add_spool(Host, Query) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p~n", [Reason]);
- {error, Reason} ->
- ?ERROR_MSG("~p~n", [Reason]);
- _ ->
- ok
- end
- end,
- loop(Host, AccessMaxOfflineMsgs);
- _ ->
- loop(Host, AccessMaxOfflineMsgs)
- end.
-
-%% Function copied from ejabberd_sm.erl:
-get_max_user_messages(AccessRule, LUser, Host) ->
- case acl:match_rule(
- Host, AccessRule, jlib:make_jid(LUser, Host, "")) of
- Max when is_integer(Max) -> Max;
- infinity -> infinity;
- _ -> ?MAX_USER_MESSAGES
- end.
-
-receive_all(Username, Msgs) ->
- receive
- #offline_msg{user=Username} = Msg ->
- receive_all(Username, [Msg | Msgs])
- after 0 ->
- lists:reverse(Msgs)
- end.
-
-
-stop(Host) ->
- ejabberd_hooks:delete(offline_message_hook, Host,
- ?MODULE, store_packet, 50),
- ejabberd_hooks:delete(resend_offline_messages_hook, Host,
- ?MODULE, pop_offline_messages, 50),
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
- ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50),
- ejabberd_hooks:delete(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:delete(webadmin_user, Host,
- ?MODULE, webadmin_user, 50),
- ejabberd_hooks:delete(webadmin_user_parse_query, Host,
- ?MODULE, webadmin_user_parse_query, 50),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- exit(whereis(Proc), stop),
- ok.
-
-get_sm_features(Acc, _From, _To, "", _Lang) ->
- Feats = case Acc of
- {result, I} -> I;
- _ -> []
- end,
- {result, Feats ++ [?NS_FEATURE_MSGOFFLINE]};
-
-get_sm_features(_Acc, _From, _To, ?NS_FEATURE_MSGOFFLINE, _Lang) ->
- %% override all lesser features...
- {result, []};
-
-get_sm_features(Acc, _From, _To, _Node, _Lang) ->
- Acc.
-
-
-store_packet(From, To, Packet) ->
- Type = xml:get_tag_attr_s("type", Packet),
- if
- (Type /= "error") and (Type /= "groupchat") and
- (Type /= "headline") ->
- case check_event_chatstates(From, To, Packet) of
- true ->
- #jid{luser = LUser} = To,
- TimeStamp = now(),
- {xmlelement, _Name, _Attrs, Els} = Packet,
- Expire = find_x_expire(TimeStamp, Els),
- gen_mod:get_module_proc(To#jid.lserver, ?PROCNAME) !
- #offline_msg{user = LUser,
- timestamp = TimeStamp,
- expire = Expire,
- from = From,
- to = To,
- packet = Packet},
- stop;
- _ ->
- ok
- end;
- true ->
- ok
- end.
-
-%% Check if the packet has any content about XEP-0022 or XEP-0085
-check_event_chatstates(From, To, Packet) ->
- {xmlelement, Name, Attrs, Els} = Packet,
- case find_x_event_chatstates(Els, {false, false, false}) of
- %% There wasn't any x:event or chatstates subelements
- {false, false, _} ->
- true;
- %% There a chatstates subelement and other stuff, but no x:event
- {false, CEl, true} when CEl /= false ->
- true;
- %% There was only a subelement: a chatstates
- {false, CEl, false} when CEl /= false ->
- %% Don't allow offline storage
- false;
- %% There was an x:event element, and maybe also other stuff
- {El, _, _} when El /= false ->
- case xml:get_subtag(El, "id") of
- false ->
- case xml:get_subtag(El, "offline") of
- false ->
- true;
- _ ->
- ID = case xml:get_tag_attr_s("id", Packet) of
- "" ->
- {xmlelement, "id", [], []};
- S ->
- {xmlelement, "id", [],
- [{xmlcdata, S}]}
- end,
- ejabberd_router:route(
- To, From, {xmlelement, Name, Attrs,
- [{xmlelement, "x",
- [{"xmlns", ?NS_EVENT}],
- [ID,
- {xmlelement, "offline", [], []}]}]
- }),
- true
- end;
- _ ->
- false
- end
- end.
-
-%% Check if the packet has subelements about XEP-0022, XEP-0085 or other
-find_x_event_chatstates([], Res) ->
- Res;
-find_x_event_chatstates([{xmlcdata, _} | Els], Res) ->
- find_x_event_chatstates(Els, Res);
-find_x_event_chatstates([El | Els], {A, B, C}) ->
- case xml:get_tag_attr_s("xmlns", El) of
- ?NS_EVENT ->
- find_x_event_chatstates(Els, {El, B, C});
- ?NS_CHATSTATES ->
- find_x_event_chatstates(Els, {A, El, C});
- _ ->
- find_x_event_chatstates(Els, {A, B, true})
- end.
-
-find_x_expire(_, []) ->
- never;
-find_x_expire(TimeStamp, [{xmlcdata, _} | Els]) ->
- find_x_expire(TimeStamp, Els);
-find_x_expire(TimeStamp, [El | Els]) ->
- case xml:get_tag_attr_s("xmlns", El) of
- ?NS_EXPIRE ->
- Val = xml:get_tag_attr_s("seconds", El),
- case catch list_to_integer(Val) of
- {'EXIT', _} ->
- never;
- Int when Int > 0 ->
- {MegaSecs, Secs, MicroSecs} = TimeStamp,
- S = MegaSecs * 1000000 + Secs + Int,
- MegaSecs1 = S div 1000000,
- Secs1 = S rem 1000000,
- {MegaSecs1, Secs1, MicroSecs};
- _ ->
- never
- end;
- _ ->
- find_x_expire(TimeStamp, Els)
- end.
-
-
-pop_offline_messages(Ls, User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- EUser = ejabberd_odbc:escape(LUser),
- case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of
- {atomic, {selected, ["username","xml"], Rs}} ->
- Ls ++ lists:flatmap(
- fun({_, XML}) ->
- case xml_stream:parse_element(XML) of
- {error, _Reason} ->
- [];
- El ->
- To = jlib:string_to_jid(
- xml:get_tag_attr_s("to", El)),
- From = jlib:string_to_jid(
- xml:get_tag_attr_s("from", El)),
- if
- (To /= error) and
- (From /= error) ->
- [{route, From, To, El}];
- true ->
- []
- end
- end
- end, Rs);
- _ ->
- Ls
- end.
-
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_spool_msg(LServer, Username).
-
-
-%% Helper functions:
-
-%% TODO: Warning - This function is a duplicate from mod_offline.erl
-%% It is duplicate to stay consistent (many functions are duplicated
-%% in this module). It will be refactored later on.
-%% Warn senders that their messages have been discarded:
-discard_warn_sender(Msgs) ->
- lists:foreach(
- fun(#offline_msg{from=From, to=To, packet=Packet}) ->
- ErrText = "Your contact offline message queue is full. The message has been discarded.",
- Lang = xml:get_tag_attr_s("xml:lang", Packet),
- Err = jlib:make_error_reply(
- Packet, ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText)),
- ejabberd_router:route(
- To,
- From, Err)
- end, Msgs).
-
-
-webadmin_page(_, Host,
- #request{us = _US,
- path = ["user", U, "queue"],
- q = Query,
- lang = Lang} = _Request) ->
- Res = user_queue(U, Host, Query, Lang),
- {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-user_queue(User, Server, Query, Lang) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- US = {LUser, LServer},
- Res = user_queue_parse_query(Username, LServer, Query),
- MsgsAll = case catch ejabberd_odbc:sql_query(
- LServer,
- ["select username, xml from spool"
- " where username='", Username, "'"
- " order by seq;"]) of
- {selected, ["username", "xml"], Rs} ->
- lists:flatmap(
- fun({_, XML}) ->
- case xml_stream:parse_element(XML) of
- {error, _Reason} ->
- [];
- El ->
- [El]
- end
- end, Rs);
- _ ->
- []
- end,
- Msgs = get_messages_subset(User, Server, MsgsAll),
- FMsgs =
- lists:map(
- fun({xmlelement, _Name, _Attrs, _Els} = Msg) ->
- ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
- Packet = Msg,
- FPacket = ejabberd_web_admin:pretty_print_xml(Packet),
- ?XE("tr",
- [?XAE("td", [{"class", "valign"}], [?INPUT("checkbox", "selected", ID)]),
- ?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
- )
- end, Msgs),
- [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
- [us_to_list(US)]))] ++
- case Res of
- ok -> [?XREST("Submitted")];
- nothing -> []
- end ++
- [?XAE("form", [{"action", ""}, {"method", "post"}],
- [?XE("table",
- [?XE("thead",
- [?XE("tr",
- [?X("td"),
- ?XCT("td", "Packet")
- ])]),
- ?XE("tbody",
- if
- FMsgs == [] ->
- [?XE("tr",
- [?XAC("td", [{"colspan", "4"}], " ")]
- )];
- true ->
- FMsgs
- end
- )]),
- ?BR,
- ?INPUTT("submit", "delete", "Delete Selected")
- ])].
-
-user_queue_parse_query(Username, LServer, Query) ->
- case lists:keysearch("delete", 1, Query) of
- {value, _} ->
- Msgs = case catch ejabberd_odbc:sql_query(
- LServer,
- ["select xml, seq from spool"
- " where username='", Username, "'"
- " order by seq;"]) of
- {selected, ["xml", "seq"], Rs} ->
- lists:flatmap(
- fun({XML, Seq}) ->
- case xml_stream:parse_element(XML) of
- {error, _Reason} ->
- [];
- El ->
- [{El, Seq}]
- end
- end, Rs);
- _ ->
- []
- end,
- F = fun() ->
- lists:foreach(
- fun({Msg, Seq}) ->
- ID = jlib:encode_base64(
- binary_to_list(term_to_binary(Msg))),
- case lists:member({"selected", ID}, Query) of
- true ->
- SSeq = ejabberd_odbc:escape(Seq),
- catch ejabberd_odbc:sql_query(
- LServer,
- ["delete from spool"
- " where username='", Username, "'"
- " and seq='", SSeq, "';"]);
- false ->
- ok
- end
- end, Msgs)
- end,
- mnesia:transaction(F),
- ok;
- false ->
- nothing
- end.
-
-us_to_list({User, Server}) ->
- jlib:jid_to_string({User, Server, ""}).
-
-get_queue_length(Username, LServer) ->
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select count(*) from spool"
- " where username='", Username, "';"]) of
- {selected, [_], [{SCount}]} ->
- SCount;
- _ ->
- 0
- end.
-
-get_messages_subset(User, Host, MsgsAll) ->
- Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
- max_user_offline_messages),
- MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
- Number when is_integer(Number) -> Number;
- _ -> 100
- end,
- Length = length(MsgsAll),
- get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll).
-
-get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 ->
- MsgsAll;
-get_messages_subset2(Max, Length, MsgsAll) ->
- FirstN = Max,
- {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
- MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
- IntermediateMsg = {xmlelement, "...", [], []},
- MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
-
-webadmin_user(Acc, User, Server, Lang) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- QueueLen = get_queue_length(Username, LServer),
- FQueueLen = [?AC("queue/", QueueLen)],
- Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
-
-webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
- case catch odbc_queries:del_spool_msg(Server, User) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
- {stop, error};
- {error, Reason} ->
- ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
- {stop, error};
- _ ->
- ?INFO_MSG("Removed all offline messages for ~s@~s", [User, Server]),
- {stop, ok}
- end;
-webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
- Acc.
-
-%% ------------------------------------------------
-%% mod_offline: number of messages quota management
-
-%% Returns as integer the number of offline messages for a given user
-count_offline_messages(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:count_records_where(
- LServer, "spool", "where username='" ++ Username ++ "'") of
- {selected, [_], [{Res}]} ->
- list_to_integer(Res);
- _ ->
- 0
- end.
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 665b8866a..42809ef8b 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -36,8 +36,21 @@
get_user_list/3,
check_packet/6,
remove_user/2,
+ item_to_raw/1,
+ raw_to_item/1,
+ is_list_needdb/1,
updated_list/3]).
+%% For mod_blocking
+-export([sql_add_privacy_list/2,
+ sql_get_default_privacy_list/2,
+ sql_get_default_privacy_list_t/1,
+ sql_get_privacy_list_data/3,
+ sql_get_privacy_list_data_by_id_t/1,
+ sql_get_privacy_list_id_t/2,
+ sql_set_default_privacy_list/2,
+ sql_set_privacy_list/2]).
+
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
@@ -45,9 +58,15 @@
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- mnesia:create_table(privacy, [{disc_copies, [node()]},
- {attributes, record_info(fields, privacy)}]),
- update_table(),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(privacy,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, privacy)}]),
+ update_table();
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(privacy_iq_get, Host,
?MODULE, process_iq_get, 50),
ejabberd_hooks:add(privacy_iq_set, Host,
@@ -102,68 +121,116 @@ process_iq_get(_, From, _To, #iq{sub_el = SubEl},
{error, ?ERR_BAD_REQUEST}
end.
-
process_lists_get(LUser, LServer, Active) ->
+ case process_lists_get(LUser, LServer, Active,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ error ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ {_Default, []} ->
+ {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+ {Default, LItems} ->
+ DItems =
+ case Default of
+ none ->
+ LItems;
+ _ ->
+ [{xmlelement, "default",
+ [{"name", Default}], []} | LItems]
+ end,
+ ADItems =
+ case Active of
+ none ->
+ DItems;
+ _ ->
+ [{xmlelement, "active",
+ [{"name", Active}], []} | DItems]
+ end,
+ {result,
+ [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
+ ADItems}]}
+ end.
+
+process_lists_get(LUser, LServer, _Active, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
+ error;
[] ->
- {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+ {none, []};
[#privacy{default = Default, lists = Lists}] ->
- case Lists of
- [] ->
- {result, [{xmlelement, "query",
- [{"xmlns", ?NS_PRIVACY}], []}]};
- _ ->
- LItems = lists:map(
- fun({N, _}) ->
- {xmlelement, "list",
- [{"name", N}], []}
- end, Lists),
- DItems =
- case Default of
- none ->
- LItems;
- _ ->
- [{xmlelement, "default",
- [{"name", Default}], []} | LItems]
- end,
- ADItems =
- case Active of
- none ->
- DItems;
- _ ->
- [{xmlelement, "active",
- [{"name", Active}], []} | DItems]
- end,
- {result,
- [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
- ADItems}]}
- end
+ LItems = lists:map(
+ fun({N, _}) ->
+ {xmlelement, "list", [{"name", N}], []}
+ end, Lists),
+ {Default, LItems}
+ end;
+process_lists_get(LUser, LServer, _Active, odbc) ->
+ Default = case catch sql_get_default_privacy_list(LUser, LServer) of
+ {selected, ["name"], []} ->
+ none;
+ {selected, ["name"], [{DefName}]} ->
+ DefName;
+ _ ->
+ none
+ end,
+ case catch sql_get_privacy_list_names(LUser, LServer) of
+ {selected, ["name"], Names} ->
+ LItems = lists:map(
+ fun({N}) ->
+ {xmlelement, "list", [{"name", N}], []}
+ end, Names),
+ {Default, LItems};
+ _ ->
+ error
end.
process_list_get(LUser, LServer, {value, Name}) ->
+ case process_list_get(LUser, LServer, Name,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ error ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ not_found ->
+ {error, ?ERR_ITEM_NOT_FOUND};
+ Items ->
+ LItems = lists:map(fun item_to_xml/1, Items),
+ {result,
+ [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
+ [{xmlelement, "list",
+ [{"name", Name}], LItems}]}]}
+ end;
+process_list_get(_LUser, _LServer, false) ->
+ {error, ?ERR_BAD_REQUEST}.
+
+process_list_get(LUser, LServer, Name, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
+ error;
[] ->
- {error, ?ERR_ITEM_NOT_FOUND};
- %{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
+ not_found;
[#privacy{lists = Lists}] ->
case lists:keysearch(Name, 1, Lists) of
{value, {_, List}} ->
- LItems = lists:map(fun item_to_xml/1, List),
- {result,
- [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
- [{xmlelement, "list",
- [{"name", Name}], LItems}]}]};
+ List;
_ ->
- {error, ?ERR_ITEM_NOT_FOUND}
+ not_found
end
end;
-
-process_list_get(_LUser, _LServer, false) ->
- {error, ?ERR_BAD_REQUEST}.
+process_list_get(LUser, LServer, Name, odbc) ->
+ case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+ {selected, ["id"], []} ->
+ not_found;
+ {selected, ["id"], [{ID}]} ->
+ case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+ {selected, ["t", "value", "action", "ord", "match_all",
+ "match_iq", "match_message",
+ "match_presence_in", "match_presence_out"],
+ RItems} ->
+ lists:map(fun raw_to_item/1, RItems);
+ _ ->
+ error
+ end;
+ _ ->
+ error
+ end.
item_to_xml(Item) ->
Attrs1 = [{"action", action_to_list(Item#listitem.action)},
@@ -269,98 +336,194 @@ process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
{error, ?ERR_BAD_REQUEST}
end.
+process_default_set(LUser, LServer, Value) ->
+ case process_default_set(LUser, LServer, Value,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ {atomic, error} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ {atomic, not_found} ->
+ {error, ?ERR_ITEM_NOT_FOUND};
+ {atomic, ok} ->
+ {result, []};
+ _ ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR}
+ end.
-process_default_set(LUser, LServer, {value, Name}) ->
+process_default_set(LUser, LServer, {value, Name}, mnesia) ->
F = fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
- {error, ?ERR_ITEM_NOT_FOUND};
+ not_found;
[#privacy{lists = Lists} = P] ->
case lists:keymember(Name, 1, Lists) of
true ->
mnesia:write(P#privacy{default = Name,
lists = Lists}),
- {result, []};
+ ok;
false ->
- {error, ?ERR_ITEM_NOT_FOUND}
+ not_found
end
end
end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
- Res;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
-
-process_default_set(LUser, LServer, false) ->
+ mnesia:transaction(F);
+process_default_set(LUser, LServer, {value, Name}, odbc) ->
+ F = fun() ->
+ case sql_get_privacy_list_names_t(LUser) of
+ {selected, ["name"], []} ->
+ not_found;
+ {selected, ["name"], Names} ->
+ case lists:member({Name}, Names) of
+ true ->
+ sql_set_default_privacy_list(LUser, Name),
+ ok;
+ false ->
+ not_found
+ end
+ end
+ end,
+ odbc_queries:sql_transaction(LServer, F);
+process_default_set(LUser, LServer, false, mnesia) ->
F = fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
- {result, []};
+ ok;
[R] ->
- mnesia:write(R#privacy{default = none}),
- {result, []}
+ mnesia:write(R#privacy{default = none})
end
end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
- Res;
+ mnesia:transaction(F);
+process_default_set(LUser, LServer, false, odbc) ->
+ case catch sql_unset_default_privacy_list(LUser, LServer) of
+ {'EXIT', _Reason} ->
+ {atomic, error};
+ {error, _Reason} ->
+ {atomic, error};
_ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
+ {atomic, ok}
end.
-
process_active_set(LUser, LServer, {value, Name}) ->
+ case process_active_set(LUser, LServer, Name,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ error ->
+ {error, ?ERR_ITEM_NOT_FOUND};
+ Items ->
+ NeedDb = is_list_needdb(Items),
+ {result, [], #userlist{name = Name, list = Items, needdb = NeedDb}}
+ end;
+process_active_set(_LUser, _LServer, false) ->
+ {result, [], #userlist{}}.
+
+process_active_set(LUser, LServer, Name, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
[] ->
- {error, ?ERR_ITEM_NOT_FOUND};
+ error;
[#privacy{lists = Lists}] ->
case lists:keysearch(Name, 1, Lists) of
{value, {_, List}} ->
- NeedDb = is_list_needdb(List),
- {result, [], #userlist{name = Name, list = List, needdb = NeedDb}};
+ List;
false ->
- {error, ?ERR_ITEM_NOT_FOUND}
+ error
end
end;
+process_active_set(LUser, LServer, Name, odbc) ->
+ case catch sql_get_privacy_list_id(LUser, LServer, Name) of
+ {selected, ["id"], []} ->
+ error;
+ {selected, ["id"], [{ID}]} ->
+ case catch sql_get_privacy_list_data_by_id(ID, LServer) of
+ {selected, ["t", "value", "action", "ord", "match_all",
+ "match_iq", "match_message",
+ "match_presence_in", "match_presence_out"],
+ RItems} ->
+ lists:map(fun raw_to_item/1, RItems);
+ _ ->
+ error
+ end;
+ _ ->
+ error
+ end.
-process_active_set(_LUser, _LServer, false) ->
- {result, [], #userlist{}}.
-
+remove_privacy_list(LUser, LServer, Name, mnesia) ->
+ F = fun() ->
+ case mnesia:read({privacy, {LUser, LServer}}) of
+ [] ->
+ ok;
+ [#privacy{default = Default, lists = Lists} = P] ->
+ %% TODO: check active
+ if
+ Name == Default ->
+ conflict;
+ true ->
+ NewLists =
+ lists:keydelete(Name, 1, Lists),
+ mnesia:write(
+ P#privacy{lists = NewLists})
+ end
+ end
+ end,
+ mnesia:transaction(F);
+remove_privacy_list(LUser, LServer, Name, odbc) ->
+ F = fun() ->
+ case sql_get_default_privacy_list_t(LUser) of
+ {selected, ["name"], []} ->
+ sql_remove_privacy_list(LUser, Name),
+ ok;
+ {selected, ["name"], [{Default}]} ->
+ %% TODO: check active
+ if
+ Name == Default ->
+ conflict;
+ true ->
+ sql_remove_privacy_list(LUser, Name),
+ ok
+ end
+ end
+ end,
+ odbc_queries:sql_transaction(LServer, F).
+
+set_privacy_list(LUser, LServer, Name, List, mnesia) ->
+ F = fun() ->
+ case mnesia:wread({privacy, {LUser, LServer}}) of
+ [] ->
+ NewLists = [{Name, List}],
+ mnesia:write(#privacy{us = {LUser, LServer},
+ lists = NewLists});
+ [#privacy{lists = Lists} = P] ->
+ NewLists1 = lists:keydelete(Name, 1, Lists),
+ NewLists = [{Name, List} | NewLists1],
+ mnesia:write(P#privacy{lists = NewLists})
+ end
+ end,
+ mnesia:transaction(F);
+set_privacy_list(LUser, LServer, Name, List, odbc) ->
+ RItems = lists:map(fun item_to_raw/1, List),
+ F = fun() ->
+ ID =
+ case sql_get_privacy_list_id_t(LUser, Name) of
+ {selected, ["id"], []} ->
+ sql_add_privacy_list(LUser, Name),
+ {selected, ["id"], [{I}]} =
+ sql_get_privacy_list_id_t(LUser, Name),
+ I;
+ {selected, ["id"], [{I}]} ->
+ I
+ end,
+ sql_set_privacy_list(ID, RItems),
+ ok
+ end,
+ odbc_queries:sql_transaction(LServer, F).
process_list_set(LUser, LServer, {value, Name}, Els) ->
case parse_items(Els) of
false ->
{error, ?ERR_BAD_REQUEST};
remove ->
- F =
- fun() ->
- case mnesia:read({privacy, {LUser, LServer}}) of
- [] ->
- {result, []};
- [#privacy{default = Default, lists = Lists} = P] ->
- % TODO: check active
- if
- Name == Default ->
- {error, ?ERR_CONFLICT};
- true ->
- NewLists =
- lists:keydelete(Name, 1, Lists),
- mnesia:write(
- P#privacy{lists = NewLists}),
- {result, []}
- end
- end
- end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
+ case remove_privacy_list(LUser, LServer, Name,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ {atomic, conflict} ->
+ {error, ?ERR_CONFLICT};
+ {atomic, ok} ->
ejabberd_router:route(
jlib:make_jid(LUser, LServer, ""),
jlib:make_jid(LUser, LServer, ""),
@@ -368,30 +531,14 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
[{privacy_list,
#userlist{name = Name, list = []},
Name}]}),
- Res;
+ {result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end;
List ->
- F =
- fun() ->
- case mnesia:wread({privacy, {LUser, LServer}}) of
- [] ->
- NewLists = [{Name, List}],
- mnesia:write(#privacy{us = {LUser, LServer},
- lists = NewLists}),
- {result, []};
- [#privacy{lists = Lists} = P] ->
- NewLists1 = lists:keydelete(Name, 1, Lists),
- NewLists = [{Name, List} | NewLists1],
- mnesia:write(P#privacy{lists = NewLists}),
- {result, []}
- end
- end,
- case mnesia:transaction(F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
+ case set_privacy_list(LUser, LServer, Name, List,
+ gen_mod:db_type(LServer, ?MODULE)) of
+ {atomic, ok} ->
NeedDb = is_list_needdb(List),
ejabberd_router:route(
jlib:make_jid(LUser, LServer, ""),
@@ -400,7 +547,7 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
[{privacy_list,
#userlist{name = Name, list = List, needdb = NeedDb},
Name}]}),
- Res;
+ {result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end
@@ -532,30 +679,51 @@ is_list_needdb(Items) ->
end
end, Items).
-get_user_list(_, User, Server) ->
+get_user_list(Acc, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ {Default, Items} = get_user_list(Acc, LUser, LServer,
+ gen_mod:db_type(LServer, ?MODULE)),
+ NeedDb = is_list_needdb(Items),
+ #userlist{name = Default, list = Items, needdb = NeedDb}.
+
+get_user_list(_, LUser, LServer, mnesia) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
[] ->
- #userlist{};
+ {none, []};
[#privacy{default = Default, lists = Lists}] ->
case Default of
none ->
- #userlist{};
+ {none, []};
_ ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
- NeedDb = is_list_needdb(List),
- #userlist{name = Default, list = List, needdb = NeedDb};
+ {Default, List};
_ ->
- #userlist{}
+ {none, []}
end
end;
_ ->
- #userlist{}
+ {none, []}
+ end;
+get_user_list(_, LUser, LServer, odbc) ->
+ case catch sql_get_default_privacy_list(LUser, LServer) of
+ {selected, ["name"], []} ->
+ {none, []};
+ {selected, ["name"], [{Default}]} ->
+ case catch sql_get_privacy_list_data(LUser, LServer, Default) of
+ {selected, ["t", "value", "action", "ord", "match_all",
+ "match_iq", "match_message",
+ "match_presence_in", "match_presence_out"],
+ RItems} ->
+ {Default, lists:map(fun raw_to_item/1, RItems)};
+ _ ->
+ {none, []}
+ end;
+ _ ->
+ {none, []}
end.
-
%% From is the sender, To is the destination.
%% If Dir = out, User@Server is the sender account (From).
%% If Dir = in, User@Server is the destination account (To).
@@ -699,12 +867,16 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
F = fun() ->
mnesia:delete({privacy,
{LUser, LServer}})
end,
- mnesia:transaction(F).
-
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ sql_del_privacy_lists(LUser, LServer).
updated_list(_,
#userlist{name = OldName} = Old,
@@ -716,6 +888,160 @@ updated_list(_,
Old
end.
+raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
+ SMatchMessage, SMatchPresenceIn, SMatchPresenceOut}) ->
+ {Type, Value} =
+ case SType of
+ "n" ->
+ {none, none};
+ "j" ->
+ case jlib:string_to_jid(SValue) of
+ #jid{} = JID ->
+ {jid, jlib:jid_tolower(JID)}
+ end;
+ "g" ->
+ {group, SValue};
+ "s" ->
+ case SValue of
+ "none" ->
+ {subscription, none};
+ "both" ->
+ {subscription, both};
+ "from" ->
+ {subscription, from};
+ "to" ->
+ {subscription, to}
+ end
+ end,
+ Action =
+ case SAction of
+ "a" -> allow;
+ "d" -> deny
+ end,
+ Order = list_to_integer(SOrder),
+ MatchAll = ejabberd_odbc:to_bool(SMatchAll),
+ MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
+ MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
+ MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
+ MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
+ #listitem{type = Type,
+ value = Value,
+ action = Action,
+ order = Order,
+ match_all = MatchAll,
+ match_iq = MatchIQ,
+ match_message = MatchMessage,
+ match_presence_in = MatchPresenceIn,
+ match_presence_out = MatchPresenceOut
+ }.
+
+item_to_raw(#listitem{type = Type,
+ value = Value,
+ action = Action,
+ order = Order,
+ match_all = MatchAll,
+ match_iq = MatchIQ,
+ match_message = MatchMessage,
+ match_presence_in = MatchPresenceIn,
+ match_presence_out = MatchPresenceOut
+ }) ->
+ {SType, SValue} =
+ case Type of
+ none ->
+ {"n", ""};
+ jid ->
+ {"j", ejabberd_odbc:escape(jlib:jid_to_string(Value))};
+ group ->
+ {"g", ejabberd_odbc:escape(Value)};
+ subscription ->
+ case Value of
+ none ->
+ {"s", "none"};
+ both ->
+ {"s", "both"};
+ from ->
+ {"s", "from"};
+ to ->
+ {"s", "to"}
+ end
+ end,
+ SAction =
+ case Action of
+ allow -> "a";
+ deny -> "d"
+ end,
+ SOrder = integer_to_list(Order),
+ SMatchAll = if MatchAll -> "1"; true -> "0" end,
+ SMatchIQ = if MatchIQ -> "1"; true -> "0" end,
+ SMatchMessage = if MatchMessage -> "1"; true -> "0" end,
+ SMatchPresenceIn = if MatchPresenceIn -> "1"; true -> "0" end,
+ SMatchPresenceOut = if MatchPresenceOut -> "1"; true -> "0" end,
+ [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
+ SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
+
+sql_get_default_privacy_list(LUser, LServer) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:get_default_privacy_list(LServer, Username).
+
+sql_get_default_privacy_list_t(LUser) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:get_default_privacy_list_t(Username).
+
+sql_get_privacy_list_names(LUser, LServer) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:get_privacy_list_names(LServer, Username).
+
+sql_get_privacy_list_names_t(LUser) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:get_privacy_list_names_t(Username).
+
+sql_get_privacy_list_id(LUser, LServer, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:get_privacy_list_id(LServer, Username, SName).
+
+sql_get_privacy_list_id_t(LUser, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:get_privacy_list_id_t(Username, SName).
+
+sql_get_privacy_list_data(LUser, LServer, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:get_privacy_list_data(LServer, Username, SName).
+
+sql_get_privacy_list_data_by_id(ID, LServer) ->
+ odbc_queries:get_privacy_list_data_by_id(LServer, ID).
+
+sql_get_privacy_list_data_by_id_t(ID) ->
+ odbc_queries:get_privacy_list_data_by_id_t(ID).
+
+sql_set_default_privacy_list(LUser, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:set_default_privacy_list(Username, SName).
+
+sql_unset_default_privacy_list(LUser, LServer) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:unset_default_privacy_list(LServer, Username).
+
+sql_remove_privacy_list(LUser, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:remove_privacy_list(Username, SName).
+
+sql_add_privacy_list(LUser, Name) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SName = ejabberd_odbc:escape(Name),
+ odbc_queries:add_privacy_list(Username, SName).
+
+sql_set_privacy_list(ID, RItems) ->
+ odbc_queries:set_privacy_list(ID, RItems).
+
+sql_del_privacy_lists(LUser, LServer) ->
+ Username = ejabberd_odbc:escape(LUser),
+ Server = ejabberd_odbc:escape(LServer),
+ odbc_queries:del_privacy_lists(LServer, Server, Username).
update_table() ->
Fields = record_info(fields, privacy),
diff --git a/src/mod_privacy_odbc.erl b/src/mod_privacy_odbc.erl
deleted file mode 100644
index 87303b57a..000000000
--- a/src/mod_privacy_odbc.erl
+++ /dev/null
@@ -1,878 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_privacy_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : jabber:iq:privacy support
-%%% Created : 5 Oct 2006 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_privacy_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
- process_iq/3,
- process_iq_set/4,
- process_iq_get/5,
- get_user_list/3,
- check_packet/6,
- remove_user/2,
- item_to_raw/1,
- raw_to_item/1,
- updated_list/3]).
-
-%% For mod_blocking_odbc
--export([sql_add_privacy_list/2,
- sql_get_default_privacy_list/2,
- sql_get_default_privacy_list_t/1,
- sql_get_privacy_list_data/3,
- sql_get_privacy_list_data_by_id_t/1,
- sql_get_privacy_list_id_t/2,
- sql_set_default_privacy_list/2,
- sql_set_privacy_list/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_privacy.hrl").
-
-start(Host, Opts) ->
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- ejabberd_hooks:add(privacy_iq_get, Host,
- ?MODULE, process_iq_get, 50),
- ejabberd_hooks:add(privacy_iq_set, Host,
- ?MODULE, process_iq_set, 50),
- ejabberd_hooks:add(privacy_get_user_list, Host,
- ?MODULE, get_user_list, 50),
- ejabberd_hooks:add(privacy_check_packet, Host,
- ?MODULE, check_packet, 50),
- ejabberd_hooks:add(privacy_updated_list, Host,
- ?MODULE, updated_list, 50),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY,
- ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
- ejabberd_hooks:delete(privacy_iq_get, Host,
- ?MODULE, process_iq_get, 50),
- ejabberd_hooks:delete(privacy_iq_set, Host,
- ?MODULE, process_iq_set, 50),
- ejabberd_hooks:delete(privacy_get_user_list, Host,
- ?MODULE, get_user_list, 50),
- ejabberd_hooks:delete(privacy_check_packet, Host,
- ?MODULE, check_packet, 50),
- ejabberd_hooks:delete(privacy_updated_list, Host,
- ?MODULE, updated_list, 50),
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY).
-
-process_iq(_From, _To, IQ) ->
- SubEl = IQ#iq.sub_el,
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
-
-
-process_iq_get(_, From, _To, #iq{sub_el = SubEl},
- #userlist{name = Active}) ->
- #jid{luser = LUser, lserver = LServer} = From,
- {xmlelement, _, _, Els} = SubEl,
- case xml:remove_cdata(Els) of
- [] ->
- process_lists_get(LUser, LServer, Active);
- [{xmlelement, Name, Attrs, _SubEls}] ->
- case Name of
- "list" ->
- ListName = xml:get_attr("name", Attrs),
- process_list_get(LUser, LServer, ListName);
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end;
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end.
-
-
-process_lists_get(LUser, LServer, Active) ->
- Default = case catch sql_get_default_privacy_list(LUser, LServer) of
- {selected, ["name"], []} ->
- none;
- {selected, ["name"], [{DefName}]} ->
- DefName;
- _ ->
- none
- end,
- case catch sql_get_privacy_list_names(LUser, LServer) of
- {selected, ["name"], []} ->
- {result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
- {selected, ["name"], Names} ->
- LItems = lists:map(
- fun({N}) ->
- {xmlelement, "list",
- [{"name", N}], []}
- end, Names),
- DItems =
- case Default of
- none ->
- LItems;
- _ ->
- [{xmlelement, "default",
- [{"name", Default}], []} | LItems]
- end,
- ADItems =
- case Active of
- none ->
- DItems;
- _ ->
- [{xmlelement, "active",
- [{"name", Active}], []} | DItems]
- end,
- {result,
- [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
- ADItems}]};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end.
-
-process_list_get(LUser, LServer, {value, Name}) ->
- case catch sql_get_privacy_list_id(LUser, LServer, Name) of
- {selected, ["id"], []} ->
- {error, ?ERR_ITEM_NOT_FOUND};
- {selected, ["id"], [{ID}]} ->
- case catch sql_get_privacy_list_data_by_id(ID, LServer) of
- {selected, ["t", "value", "action", "ord", "match_all",
- "match_iq", "match_message",
- "match_presence_in", "match_presence_out"],
- RItems} ->
- Items = lists:map(fun raw_to_item/1, RItems),
- LItems = lists:map(fun item_to_xml/1, Items),
- {result,
- [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
- [{xmlelement, "list",
- [{"name", Name}], LItems}]}]};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
-
-process_list_get(_LUser, _LServer, false) ->
- {error, ?ERR_BAD_REQUEST}.
-
-item_to_xml(Item) ->
- Attrs1 = [{"action", action_to_list(Item#listitem.action)},
- {"order", order_to_list(Item#listitem.order)}],
- Attrs2 = case Item#listitem.type of
- none ->
- Attrs1;
- Type ->
- [{"type", type_to_list(Item#listitem.type)},
- {"value", value_to_list(Type, Item#listitem.value)} |
- Attrs1]
- end,
- SubEls = case Item#listitem.match_all of
- true ->
- [];
- false ->
- SE1 = case Item#listitem.match_iq of
- true ->
- [{xmlelement, "iq", [], []}];
- false ->
- []
- end,
- SE2 = case Item#listitem.match_message of
- true ->
- [{xmlelement, "message", [], []} | SE1];
- false ->
- SE1
- end,
- SE3 = case Item#listitem.match_presence_in of
- true ->
- [{xmlelement, "presence-in", [], []} | SE2];
- false ->
- SE2
- end,
- SE4 = case Item#listitem.match_presence_out of
- true ->
- [{xmlelement, "presence-out", [], []} | SE3];
- false ->
- SE3
- end,
- SE4
- end,
- {xmlelement, "item", Attrs2, SubEls}.
-
-
-action_to_list(Action) ->
- case Action of
- allow -> "allow";
- deny -> "deny"
- end.
-
-order_to_list(Order) ->
- integer_to_list(Order).
-
-type_to_list(Type) ->
- case Type of
- jid -> "jid";
- group -> "group";
- subscription -> "subscription"
- end.
-
-value_to_list(Type, Val) ->
- case Type of
- jid -> jlib:jid_to_string(Val);
- group -> Val;
- subscription ->
- case Val of
- both -> "both";
- to -> "to";
- from -> "from";
- none -> "none"
- end
- end.
-
-
-
-list_to_action(S) ->
- case S of
- "allow" -> allow;
- "deny" -> deny
- end.
-
-
-
-process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
- #jid{luser = LUser, lserver = LServer} = From,
- {xmlelement, _, _, Els} = SubEl,
- case xml:remove_cdata(Els) of
- [{xmlelement, Name, Attrs, SubEls}] ->
- ListName = xml:get_attr("name", Attrs),
- case Name of
- "list" ->
- process_list_set(LUser, LServer, ListName,
- xml:remove_cdata(SubEls));
- "active" ->
- process_active_set(LUser, LServer, ListName);
- "default" ->
- process_default_set(LUser, LServer, ListName);
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end;
- _ ->
- {error, ?ERR_BAD_REQUEST}
- end.
-
-
-process_default_set(LUser, LServer, {value, Name}) ->
- F = fun() ->
- case sql_get_privacy_list_names_t(LUser) of
- {selected, ["name"], []} ->
- {error, ?ERR_ITEM_NOT_FOUND};
- {selected, ["name"], Names} ->
- case lists:member({Name}, Names) of
- true ->
- sql_set_default_privacy_list(LUser, Name),
- {result, []};
- false ->
- {error, ?ERR_ITEM_NOT_FOUND}
- end
- end
- end,
- case odbc_queries:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
- Res;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
-
-process_default_set(LUser, LServer, false) ->
- case catch sql_unset_default_privacy_list(LUser, LServer) of
- {'EXIT', _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
- {error, _Reason} ->
- {error, ?ERR_INTERNAL_SERVER_ERROR};
- _ ->
- {result, []}
- end.
-
-
-process_active_set(LUser, LServer, {value, Name}) ->
- case catch sql_get_privacy_list_id(LUser, LServer, Name) of
- {selected, ["id"], []} ->
- {error, ?ERR_ITEM_NOT_FOUND};
- {selected, ["id"], [{ID}]} ->
- case catch sql_get_privacy_list_data_by_id(ID, LServer) of
- {selected, ["t", "value", "action", "ord", "match_all",
- "match_iq", "match_message",
- "match_presence_in", "match_presence_out"],
- RItems} ->
- Items = lists:map(fun raw_to_item/1, RItems),
- NeedDb = is_list_needdb(Items),
- {result, [], #userlist{name = Name, list = Items, needdb = NeedDb}};
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
-
-process_active_set(_LUser, _LServer, false) ->
- {result, [], #userlist{}}.
-
-
-
-
-
-
-process_list_set(LUser, LServer, {value, Name}, Els) ->
- case parse_items(Els) of
- false ->
- {error, ?ERR_BAD_REQUEST};
- remove ->
- F =
- fun() ->
- case sql_get_default_privacy_list_t(LUser) of
- {selected, ["name"], []} ->
- sql_remove_privacy_list(LUser, Name),
- {result, []};
- {selected, ["name"], [{Default}]} ->
- %% TODO: check active
- if
- Name == Default ->
- {error, ?ERR_CONFLICT};
- true ->
- sql_remove_privacy_list(LUser, Name),
- {result, []}
- end
- end
- end,
- case odbc_queries:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
- ejabberd_router:route(
- jlib:make_jid(LUser, LServer, ""),
- jlib:make_jid(LUser, LServer, ""),
- {xmlelement, "broadcast", [],
- [{privacy_list,
- #userlist{name = Name, list = []},
- Name}]}),
- Res;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end;
- List ->
- RItems = lists:map(fun item_to_raw/1, List),
- F =
- fun() ->
- ID =
- case sql_get_privacy_list_id_t(LUser, Name) of
- {selected, ["id"], []} ->
- sql_add_privacy_list(LUser, Name),
- {selected, ["id"], [{I}]} =
- sql_get_privacy_list_id_t(LUser, Name),
- I;
- {selected, ["id"], [{I}]} ->
- I
- end,
- sql_set_privacy_list(ID, RItems),
- {result, []}
- end,
- case odbc_queries:sql_transaction(LServer, F) of
- {atomic, {error, _} = Error} ->
- Error;
- {atomic, {result, _} = Res} ->
- NeedDb = is_list_needdb(List),
- ejabberd_router:route(
- jlib:make_jid(LUser, LServer, ""),
- jlib:make_jid(LUser, LServer, ""),
- {xmlelement, "broadcast", [],
- [{privacy_list,
- #userlist{name = Name, list = List, needdb = NeedDb},
- Name}]}),
- Res;
- _ ->
- {error, ?ERR_INTERNAL_SERVER_ERROR}
- end
- end;
-
-process_list_set(_LUser, _LServer, false, _Els) ->
- {error, ?ERR_BAD_REQUEST}.
-
-
-parse_items([]) ->
- remove;
-parse_items(Els) ->
- parse_items(Els, []).
-
-parse_items([], Res) ->
- %% Sort the items by their 'order' attribute
- lists:keysort(#listitem.order, Res);
-parse_items([{xmlelement, "item", Attrs, SubEls} | Els], Res) ->
- Type = xml:get_attr("type", Attrs),
- Value = xml:get_attr("value", Attrs),
- SAction = xml:get_attr("action", Attrs),
- SOrder = xml:get_attr("order", Attrs),
- Action = case catch list_to_action(element(2, SAction)) of
- {'EXIT', _} -> false;
- Val -> Val
- end,
- Order = case catch list_to_integer(element(2, SOrder)) of
- {'EXIT', _} ->
- false;
- IntVal ->
- if
- IntVal >= 0 ->
- IntVal;
- true ->
- false
- end
- end,
- if
- (Action /= false) and (Order /= false) ->
- I1 = #listitem{action = Action, order = Order},
- I2 = case {Type, Value} of
- {{value, T}, {value, V}} ->
- case T of
- "jid" ->
- case jlib:string_to_jid(V) of
- error ->
- false;
- JID ->
- I1#listitem{
- type = jid,
- value = jlib:jid_tolower(JID)}
- end;
- "group" ->
- I1#listitem{type = group,
- value = V};
- "subscription" ->
- case V of
- "none" ->
- I1#listitem{type = subscription,
- value = none};
- "both" ->
- I1#listitem{type = subscription,
- value = both};
- "from" ->
- I1#listitem{type = subscription,
- value = from};
- "to" ->
- I1#listitem{type = subscription,
- value = to};
- _ ->
- false
- end
- end;
- {{value, _}, false} ->
- false;
- _ ->
- I1
- end,
- case I2 of
- false ->
- false;
- _ ->
- case parse_matches(I2, xml:remove_cdata(SubEls)) of
- false ->
- false;
- I3 ->
- parse_items(Els, [I3 | Res])
- end
- end;
- true ->
- false
- end;
-
-parse_items(_, _Res) ->
- false.
-
-
-parse_matches(Item, []) ->
- Item#listitem{match_all = true};
-parse_matches(Item, Els) ->
- parse_matches1(Item, Els).
-
-parse_matches1(Item, []) ->
- Item;
-parse_matches1(Item, [{xmlelement, "message", _, _} | Els]) ->
- parse_matches1(Item#listitem{match_message = true}, Els);
-parse_matches1(Item, [{xmlelement, "iq", _, _} | Els]) ->
- parse_matches1(Item#listitem{match_iq = true}, Els);
-parse_matches1(Item, [{xmlelement, "presence-in", _, _} | Els]) ->
- parse_matches1(Item#listitem{match_presence_in = true}, Els);
-parse_matches1(Item, [{xmlelement, "presence-out", _, _} | Els]) ->
- parse_matches1(Item#listitem{match_presence_out = true}, Els);
-parse_matches1(_Item, [{xmlelement, _, _, _} | _Els]) ->
- false.
-
-
-
-
-
-is_list_needdb(Items) ->
- lists:any(
- fun(X) ->
- case X#listitem.type of
- subscription -> true;
- group -> true;
- _ -> false
- end
- end, Items).
-
-get_user_list(_, User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
-
- case catch sql_get_default_privacy_list(LUser, LServer) of
- {selected, ["name"], []} ->
- #userlist{};
- {selected, ["name"], [{Default}]} ->
- case catch sql_get_privacy_list_data(LUser, LServer, Default) of
- {selected, ["t", "value", "action", "ord", "match_all",
- "match_iq", "match_message",
- "match_presence_in", "match_presence_out"],
- RItems} ->
- Items = lists:map(fun raw_to_item/1, RItems),
- NeedDb = is_list_needdb(Items),
- #userlist{name = Default, list = Items, needdb = NeedDb};
- _ ->
- #userlist{}
- end;
- _ ->
- #userlist{}
- end.
-
-
-%% From is the sender, To is the destination.
-%% If Dir = out, User@Server is the sender account (From).
-%% If Dir = in, User@Server is the destination account (To).
-check_packet(_, _User, _Server,
- _UserList,
- {#jid{luser = "", lserver = Server} = _From,
- #jid{lserver = Server} = _To,
- _},
- in) ->
- allow;
-check_packet(_, _User, _Server,
- _UserList,
- {#jid{lserver = Server} = _From,
- #jid{luser = "", lserver = Server} = _To,
- _},
- out) ->
- allow;
-check_packet(_, _User, _Server,
- _UserList,
- {#jid{luser = User, lserver = Server} = _From,
- #jid{luser = User, lserver = Server} = _To,
- _},
- _Dir) ->
- allow;
-check_packet(_, User, Server,
- #userlist{list = List, needdb = NeedDb},
- {From, To, {xmlelement, PName, Attrs, _}},
- Dir) ->
- case List of
- [] ->
- allow;
- _ ->
- PType = case PName of
- "message" -> message;
- "iq" -> iq;
- "presence" ->
- case xml:get_attr_s("type", Attrs) of
- %% notification
- "" -> presence;
- "unavailable" -> presence;
- %% subscribe, subscribed, unsubscribe,
- %% unsubscribed, error, probe, or other
- _ -> other
- end
- end,
- PType2 = case {PType, Dir} of
- {message, in} -> message;
- {iq, in} -> iq;
- {presence, in} -> presence_in;
- {presence, out} -> presence_out;
- {_, _} -> other
- end,
- LJID = case Dir of
- in -> jlib:jid_tolower(From);
- out -> jlib:jid_tolower(To)
- end,
- {Subscription, Groups} =
- case NeedDb of
- true -> ejabberd_hooks:run_fold(roster_get_jid_info,
- jlib:nameprep(Server),
- {none, []},
- [User, Server, LJID]);
- false -> {[], []}
- end,
- check_packet_aux(List, PType2, LJID, Subscription, Groups)
- end.
-
-check_packet_aux([], _PType, _JID, _Subscription, _Groups) ->
- allow;
-check_packet_aux([Item | List], PType, JID, Subscription, Groups) ->
- #listitem{type = Type, value = Value, action = Action} = Item,
- case is_ptype_match(Item, PType) of
- true ->
- case Type of
- none ->
- Action;
- _ ->
- case is_type_match(Type, Value,
- JID, Subscription, Groups) of
- true ->
- Action;
- false ->
- check_packet_aux(List, PType,
- JID, Subscription, Groups)
- end
- end;
- false ->
- check_packet_aux(List, PType, JID, Subscription, Groups)
- end.
-
-
-is_ptype_match(Item, PType) ->
- case Item#listitem.match_all of
- true ->
- true;
- false ->
- case PType of
- message ->
- Item#listitem.match_message;
- iq ->
- Item#listitem.match_iq;
- presence_in ->
- Item#listitem.match_presence_in;
- presence_out ->
- Item#listitem.match_presence_out;
- other ->
- false
- end
- end.
-
-
-is_type_match(Type, Value, JID, Subscription, Groups) ->
- case Type of
- jid ->
- case Value of
- {"", Server, ""} ->
- case JID of
- {_, Server, _} ->
- true;
- _ ->
- false
- end;
- {User, Server, ""} ->
- case JID of
- {User, Server, _} ->
- true;
- _ ->
- false
- end;
- _ ->
- Value == JID
- end;
- subscription ->
- Value == Subscription;
- group ->
- lists:member(Value, Groups)
- end.
-
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- sql_del_privacy_lists(LUser, LServer).
-
-
-updated_list(_,
- #userlist{name = OldName} = Old,
- #userlist{name = NewName} = New) ->
- if
- OldName == NewName ->
- New;
- true ->
- Old
- end.
-
-
-raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
- SMatchMessage, SMatchPresenceIn, SMatchPresenceOut}) ->
- {Type, Value} =
- case SType of
- "n" ->
- {none, none};
- "j" ->
- case jlib:string_to_jid(SValue) of
- #jid{} = JID ->
- {jid, jlib:jid_tolower(JID)}
- end;
- "g" ->
- {group, SValue};
- "s" ->
- case SValue of
- "none" ->
- {subscription, none};
- "both" ->
- {subscription, both};
- "from" ->
- {subscription, from};
- "to" ->
- {subscription, to}
- end
- end,
- Action =
- case SAction of
- "a" -> allow;
- "d" -> deny
- end,
- Order = list_to_integer(SOrder),
- MatchAll = ejabberd_odbc:to_bool(SMatchAll),
- MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
- MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
- MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
- MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
- #listitem{type = Type,
- value = Value,
- action = Action,
- order = Order,
- match_all = MatchAll,
- match_iq = MatchIQ,
- match_message = MatchMessage,
- match_presence_in = MatchPresenceIn,
- match_presence_out = MatchPresenceOut
- }.
-
-item_to_raw(#listitem{type = Type,
- value = Value,
- action = Action,
- order = Order,
- match_all = MatchAll,
- match_iq = MatchIQ,
- match_message = MatchMessage,
- match_presence_in = MatchPresenceIn,
- match_presence_out = MatchPresenceOut
- }) ->
- {SType, SValue} =
- case Type of
- none ->
- {"n", ""};
- jid ->
- {"j", ejabberd_odbc:escape(jlib:jid_to_string(Value))};
- group ->
- {"g", ejabberd_odbc:escape(Value)};
- subscription ->
- case Value of
- none ->
- {"s", "none"};
- both ->
- {"s", "both"};
- from ->
- {"s", "from"};
- to ->
- {"s", "to"}
- end
- end,
- SAction =
- case Action of
- allow -> "a";
- deny -> "d"
- end,
- SOrder = integer_to_list(Order),
- SMatchAll = if MatchAll -> "1"; true -> "0" end,
- SMatchIQ = if MatchIQ -> "1"; true -> "0" end,
- SMatchMessage = if MatchMessage -> "1"; true -> "0" end,
- SMatchPresenceIn = if MatchPresenceIn -> "1"; true -> "0" end,
- SMatchPresenceOut = if MatchPresenceOut -> "1"; true -> "0" end,
- [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
- SMatchMessage, SMatchPresenceIn, SMatchPresenceOut].
-
-sql_get_default_privacy_list(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_default_privacy_list(LServer, Username).
-
-sql_get_default_privacy_list_t(LUser) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_default_privacy_list_t(Username).
-
-sql_get_privacy_list_names(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_privacy_list_names(LServer, Username).
-
-sql_get_privacy_list_names_t(LUser) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:get_privacy_list_names_t(Username).
-
-sql_get_privacy_list_id(LUser, LServer, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_id(LServer, Username, SName).
-
-sql_get_privacy_list_id_t(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_id_t(Username, SName).
-
-sql_get_privacy_list_data(LUser, LServer, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:get_privacy_list_data(LServer, Username, SName).
-
-sql_get_privacy_list_data_by_id(ID, LServer) ->
- odbc_queries:get_privacy_list_data_by_id(LServer, ID).
-
-sql_get_privacy_list_data_by_id_t(ID) ->
- odbc_queries:get_privacy_list_data_by_id_t(ID).
-
-sql_set_default_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:set_default_privacy_list(Username, SName).
-
-sql_unset_default_privacy_list(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:unset_default_privacy_list(LServer, Username).
-
-sql_remove_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:remove_privacy_list(Username, SName).
-
-sql_add_privacy_list(LUser, Name) ->
- Username = ejabberd_odbc:escape(LUser),
- SName = ejabberd_odbc:escape(Name),
- odbc_queries:add_privacy_list(Username, SName).
-
-sql_set_privacy_list(ID, RItems) ->
- odbc_queries:set_privacy_list(ID, RItems).
-
-sql_del_privacy_lists(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- Server = ejabberd_odbc:escape(LServer),
- odbc_queries:del_privacy_lists(LServer, Server, Username).
diff --git a/src/mod_private.erl b/src/mod_private.erl
index ba5c00a8e..5ca6204f3 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -46,10 +46,16 @@
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- mnesia:create_table(private_storage,
- [{disc_only_copies, [node()]},
- {attributes, record_info(fields, private_storage)}]),
- update_table(),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(private_storage,
+ [{disc_only_copies, [node()]},
+ {attributes,
+ record_info(fields, private_storage)}]),
+ update_table();
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE,
@@ -73,12 +79,21 @@ process_sm_iq(#jid{luser = LUser, lserver = LServer},
sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]
};
Data ->
- mnesia:transaction(fun() ->
- lists:foreach(fun
- (Datum) ->
- set_data(LUser, LServer, Datum)
- end, Data)
- end),
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ F = fun() ->
+ lists:foreach(
+ fun
+ (Datum) ->
+ set_data(LUser, LServer,
+ Datum, DBType)
+ end, Data)
+ end,
+ case DBType of
+ odbc ->
+ ejabberd_odbc:sql_transaction(LServer, F);
+ mnesia ->
+ mnesia:transaction(F)
+ end,
IQ#iq{type = result, sub_el = []}
end;
_ ->
@@ -132,30 +147,53 @@ filter_xmlels([_ | Xmlels], Data) ->
filter_xmlels(Xmlels, Data).
-set_data(LUser, LServer, {XmlNS, Xmlel}) ->
+set_data(LUser, LServer, {XmlNS, Xmlel}, mnesia) ->
mnesia:write(#private_storage{
- usns = {LUser, LServer, XmlNS},
- xml = Xmlel
- }).
-
+ usns = {LUser, LServer, XmlNS},
+ xml = Xmlel});
+set_data(LUser, LServer, {XMLNS, El}, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ LXMLNS = ejabberd_odbc:escape(XMLNS),
+ SData = ejabberd_odbc:escape(
+ xml:element_to_binary(El)),
+ odbc_queries:set_private_data(LServer, Username, LXMLNS, SData).
get_data(LUser, LServer, Data) ->
- get_data(LUser, LServer, Data, []).
+ get_data(LUser, LServer, gen_mod:db_type(LServer, ?MODULE), Data, []).
-get_data(_LUser, _LServer, [], Storage_Xmlels) ->
+get_data(_LUser, _LServer, _DBType, [], Storage_Xmlels) ->
lists:reverse(Storage_Xmlels);
-get_data(LUser, LServer, [{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
+get_data(LUser, LServer, mnesia, [{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
case mnesia:dirty_read(private_storage, {LUser, LServer, XmlNS}) of
[#private_storage{xml = Storage_Xmlel}] ->
- get_data(LUser, LServer, Data, [Storage_Xmlel | Storage_Xmlels]);
+ get_data(LUser, LServer, mnesia, Data,
+ [Storage_Xmlel | Storage_Xmlels]);
_ ->
- get_data(LUser, LServer, Data, [Xmlel | Storage_Xmlels])
+ get_data(LUser, LServer, mnesia, Data,
+ [Xmlel | Storage_Xmlels])
+ end;
+get_data(LUser, LServer, odbc, [{XMLNS, El} | Els], Res) ->
+ Username = ejabberd_odbc:escape(LUser),
+ LXMLNS = ejabberd_odbc:escape(XMLNS),
+ case catch odbc_queries:get_private_data(LServer, Username, LXMLNS) of
+ {selected, ["data"], [{SData}]} ->
+ case xml_stream:parse_element(SData) of
+ Data when element(1, Data) == xmlelement ->
+ get_data(LUser, LServer, odbc, Els, [Data | Res])
+ end;
+ %% MREMOND: I wonder when the query could return a vcard ?
+ {selected, ["vcard"], []} ->
+ get_data(LUser, LServer, odbc, Els, [El | Res]);
+ _ ->
+ get_data(LUser, LServer, odbc, Els, [El | Res])
end.
-
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ remove_user(LUser, LServer, gen_mod:db_type(Server, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
F = fun() ->
Namespaces = mnesia:select(
private_storage,
@@ -169,8 +207,10 @@ remove_user(User, Server) ->
{LUser, LServer, Namespace}})
end, Namespaces)
end,
- mnesia:transaction(F).
-
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ odbc_queries:del_user_private_storage(LServer, Username).
update_table() ->
Fields = record_info(fields, private_storage),
diff --git a/src/mod_private_odbc.erl b/src/mod_private_odbc.erl
deleted file mode 100644
index 45fee4cb2..000000000
--- a/src/mod_private_odbc.erl
+++ /dev/null
@@ -1,136 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_private_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Private storage support
-%%% Created : 5 Oct 2006 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_private_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2,
- stop/1,
- process_sm_iq/3,
- remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-start(Host, Opts) ->
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE,
- ?MODULE, process_sm_iq, IQDisc).
-
-stop(Host) ->
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE).
-
-
-process_sm_iq(From, _To, #iq{type = Type, sub_el = SubEl} = IQ) ->
- #jid{luser = LUser, lserver = LServer} = From,
- case lists:member(LServer, ?MYHOSTS) of
- true ->
- {xmlelement, Name, Attrs, Els} = SubEl,
- case Type of
- set ->
- F = fun() ->
- lists:foreach(
- fun(El) ->
- set_data(LUser, LServer, El)
- end, Els)
- end,
- odbc_queries:sql_transaction(LServer, F),
- IQ#iq{type = result,
- sub_el = [{xmlelement, Name, Attrs, []}]};
- get ->
- case catch get_data(LUser, LServer, Els) of
- {'EXIT', _Reason} ->
- IQ#iq{type = error,
- sub_el = [SubEl,
- ?ERR_INTERNAL_SERVER_ERROR]};
- Res ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, Name, Attrs, Res}]}
- end
- end;
- false ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
- end.
-
-set_data(LUser, LServer, El) ->
- case El of
- {xmlelement, _Name, Attrs, _Els} ->
- XMLNS = xml:get_attr_s("xmlns", Attrs),
- case XMLNS of
- "" ->
- ignore;
- _ ->
- Username = ejabberd_odbc:escape(LUser),
- LXMLNS = ejabberd_odbc:escape(XMLNS),
- SData = ejabberd_odbc:escape(
- xml:element_to_binary(El)),
- odbc_queries:set_private_data(LServer, Username, LXMLNS, SData)
- end;
- _ ->
- ignore
- end.
-
-get_data(LUser, LServer, Els) ->
- get_data(LUser, LServer, Els, []).
-
-get_data(_LUser, _LServer, [], Res) ->
- lists:reverse(Res);
-get_data(LUser, LServer, [El | Els], Res) ->
- case El of
- {xmlelement, _Name, Attrs, _} ->
- XMLNS = xml:get_attr_s("xmlns", Attrs),
- Username = ejabberd_odbc:escape(LUser),
- LXMLNS = ejabberd_odbc:escape(XMLNS),
- case catch odbc_queries:get_private_data(LServer, Username, LXMLNS) of
- {selected, ["data"], [{SData}]} ->
- case xml_stream:parse_element(SData) of
- Data when element(1, Data) == xmlelement ->
- get_data(LUser, LServer, Els,
- [Data | Res])
- end;
- %% MREMOND: I wonder when the query could return a vcard ?
- {selected, ["vcard"], []} ->
- get_data(LUser, LServer, Els,
- [El | Res]);
- _ ->
- get_data(LUser, LServer, Els,[El | Res])
- end;
- _ ->
- get_data(LUser, LServer, Els, Res)
- end.
-
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- odbc_queries:del_user_private_storage(LServer, Username).
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 3383148fc..5e6b4fd52 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -65,14 +65,21 @@
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- mnesia:create_table(roster,[{disc_copies, [node()]},
- {attributes, record_info(fields, roster)}]),
- mnesia:create_table(roster_version, [{disc_copies, [node()]},
- {attributes, record_info(fields, roster_version)}]),
-
- update_table(),
- mnesia:add_table_index(roster, us),
- mnesia:add_table_index(roster_version, us),
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(roster,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, roster)}]),
+ mnesia:create_table(roster_version,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, roster_version)}]),
+ update_table(),
+ mnesia:add_table_index(roster, us),
+ mnesia:add_table_index(roster_version, us);
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(roster_get, Host,
?MODULE, get_user_roster, 50),
ejabberd_hooks:add(roster_in_subscription, Host,
@@ -166,17 +173,69 @@ get_versioning_feature(Acc, Host) ->
false -> []
end.
-roster_version(LServer ,LUser) ->
- US = {LUser, LServer},
- case roster_version_on_db(LServer) of
- true ->
- case mnesia:dirty_read(roster_version, US) of
- [#roster_version{version = V}] -> V;
- [] -> not_found
- end;
- false ->
- roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
- end.
+roster_version(LServer, LUser) ->
+ US = {LUser, LServer},
+ case roster_version_on_db(LServer) of
+ true ->
+ case read_roster_version(LUser, LServer) of
+ error ->
+ not_found;
+ V ->
+ V
+ end;
+ false ->
+ roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
+ end.
+
+read_roster_version(LUser, LServer) ->
+ read_roster_version(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+read_roster_version(LUser, LServer, mnesia) ->
+ US = {LUser, LServer},
+ case mnesia:dirty_read(roster_version, US) of
+ [#roster_version{version = V}] -> V;
+ [] -> error
+ end;
+read_roster_version(LServer, LUser, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case odbc_queries:get_roster_version(LServer, Username) of
+ {selected, ["version"], [{Version}]} ->
+ Version;
+ {selected, ["version"], []} ->
+ error
+ end.
+
+write_roster_version(LUser, LServer) ->
+ write_roster_version(LUser, LServer, false).
+
+write_roster_version_t(LUser, LServer) ->
+ write_roster_version(LUser, LServer, true).
+
+write_roster_version(LUser, LServer, InTransaction) ->
+ Ver = sha:sha(term_to_binary(now())),
+ write_roster_version(LUser, LServer, InTransaction, Ver,
+ gen_mod:db_type(LServer, ?MODULE)),
+ Ver.
+
+write_roster_version(LUser, LServer, InTransaction, Ver, mnesia) ->
+ US = {LUser, LServer},
+ if InTransaction ->
+ mnesia:write(#roster_version{us = US, version = Ver});
+ true ->
+ mnesia:dirty_write(#roster_version{us = US, version = Ver})
+ end;
+write_roster_version(LUser, LServer, InTransaction, Ver, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ EVer = ejabberd_odbc:escape(Ver),
+ if InTransaction ->
+ odbc_queries:set_roster_version(Username, EVer);
+ true ->
+ odbc_queries:sql_transaction(
+ LServer,
+ fun() ->
+ odbc_queries:set_roster_version(Username, EVer)
+ end)
+ end.
%% Load roster from DB only if neccesary.
%% It is neccesary if
@@ -189,60 +248,119 @@ process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
LServer = From#jid.lserver,
US = {LUser, LServer},
try
- {ItemsToSend, VersionToSend} =
- case {xml:get_tag_attr("ver", SubEl),
- roster_versioning_enabled(LServer),
- roster_version_on_db(LServer)} of
+ {ItemsToSend, VersionToSend} =
+ case {xml:get_tag_attr("ver", SubEl),
+ roster_versioning_enabled(LServer),
+ roster_version_on_db(LServer)} of
{{value, RequestedVersion}, true, true} ->
- %% Retrieve version from DB. Only load entire roster
- %% when neccesary.
- case mnesia:dirty_read(roster_version, US) of
- [#roster_version{version = RequestedVersion}] ->
- {false, false};
- [#roster_version{version = NewVersion}] ->
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion};
- [] ->
- RosterVersion = sha:sha(term_to_binary(now())),
- mnesia:dirty_write(#roster_version{us = US, version = RosterVersion}),
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion}
- end;
-
+ %% Retrieve version from DB. Only load entire roster
+ %% when neccesary.
+ case read_roster_version(LUser, LServer) of
+ error ->
+ RosterVersion = write_roster_version(LUser, LServer),
+ {lists:map(
+ fun item_to_xml/1,
+ ejabberd_hooks:run_fold(
+ roster_get, To#jid.lserver, [], [US])),
+ RosterVersion};
+ RequestedVersion ->
+ {false, false};
+ NewVersion ->
+ {lists:map(
+ fun item_to_xml/1,
+ ejabberd_hooks:run_fold(
+ roster_get, To#jid.lserver, [], [US])),
+ NewVersion}
+ end;
{{value, RequestedVersion}, true, false} ->
- RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]),
- case roster_hash(RosterItems) of
- RequestedVersion ->
- {false, false};
- New ->
- {lists:map(fun item_to_xml/1, RosterItems), New}
- end;
-
+ RosterItems = ejabberd_hooks:run_fold(
+ roster_get, To#jid.lserver, [] , [US]),
+ case roster_hash(RosterItems) of
+ RequestedVersion ->
+ {false, false};
+ New ->
+ {lists:map(fun item_to_xml/1, RosterItems), New}
+ end;
_ ->
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false}
- end,
- IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of
- {false, false} -> [];
- {Items, false} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], Items}];
- {Items, Version} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}, {"ver", Version}], Items}]
- end}
- catch
+ {lists:map(
+ fun item_to_xml/1,
+ ejabberd_hooks:run_fold(
+ roster_get, To#jid.lserver, [], [US])),
+ false}
+ end,
+ IQ#iq{type = result,
+ sub_el = case {ItemsToSend, VersionToSend} of
+ {false, false} ->
+ [];
+ {Items, false} ->
+ [{xmlelement, "query",
+ [{"xmlns", ?NS_ROSTER}], Items}];
+ {Items, Version} ->
+ [{xmlelement, "query",
+ [{"xmlns", ?NS_ROSTER}, {"ver", Version}],
+ Items}]
+ end}
+ catch
_:_ ->
- IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
+ IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
end.
+get_user_roster(Acc, {LUser, LServer}) ->
+ Items = get_roster(LUser, LServer),
+ lists:filter(fun(#roster{subscription = none, ask = in}) ->
+ false;
+ (_) ->
+ true
+ end, Items) ++ Acc.
+
+get_roster(LUser, LServer) ->
+ get_roster(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
-get_user_roster(Acc, US) ->
+get_roster(LUser, LServer, mnesia) ->
+ US = {LUser, LServer},
case catch mnesia:dirty_index_read(roster, US, #roster.us) of
Items when is_list(Items) ->
- lists:filter(fun(#roster{subscription = none, ask = in}) ->
- false;
- (_) ->
- true
- end, Items) ++ Acc;
+ Items;
+ _ ->
+ []
+ end;
+get_roster(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:get_roster(LServer, Username) of
+ {selected, ["username", "jid", "nick", "subscription", "ask",
+ "askmessage", "server", "subscribe", "type"],
+ Items} when is_list(Items) ->
+ JIDGroups = case catch odbc_queries:get_roster_jid_groups(LServer, Username) of
+ {selected, ["jid","grp"], JGrps}
+ when is_list(JGrps) ->
+ JGrps;
+ _ ->
+ []
+ end,
+ GroupsDict =
+ lists:foldl(
+ fun({J, G}, Acc) ->
+ dict:append(J, G, Acc)
+ end, dict:new(), JIDGroups),
+ RItems = lists:flatmap(
+ fun(I) ->
+ case raw_to_record(LServer, I) of
+ %% Bad JID in database:
+ error ->
+ [];
+ R ->
+ SJID = jlib:jid_to_string(R#roster.jid),
+ Groups =
+ case dict:find(SJID, GroupsDict) of
+ {ok, Gs} -> Gs;
+ error -> []
+ end,
+ [R#roster{groups = Groups}]
+ end
+ end, Items),
+ RItems;
_ ->
- Acc
+ []
end.
@@ -280,6 +398,50 @@ item_to_xml(Item) ->
SubEls = SubEls1 ++ Item#roster.xs,
{xmlelement, "item", Attrs4, SubEls}.
+get_roster_by_jid_t(LUser, LServer, LJID) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ get_roster_by_jid_t(LUser, LServer, LJID, DBType).
+
+get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
+ case mnesia:read({roster, {LUser, LServer, LJID}}) of
+ [] ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID};
+ [I] ->
+ I#roster{jid = LJID,
+ name = "",
+ groups = [],
+ xs = []}
+ end;
+get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ {selected,
+ ["username", "jid", "nick", "subscription",
+ "ask", "askmessage", "server", "subscribe", "type"],
+ Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID),
+ case Res of
+ [] ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID};
+ [I] ->
+ R = raw_to_record(LServer, I),
+ case R of
+ %% Bad JID in database:
+ error ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID};
+ _ ->
+ R#roster{
+ usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID,
+ name = ""}
+ end
+ end.
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
{xmlelement, _Name, _Attrs, Els} = SubEl,
@@ -293,40 +455,28 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
error ->
ok;
_ ->
- JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
LJID = jlib:jid_tolower(JID1),
F = fun() ->
- Res = mnesia:read({roster, {LUser, LServer, LJID}}),
- Item = case Res of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = JID};
- [I] ->
- I#roster{jid = JID,
- name = "",
- groups = [],
- xs = []}
- end,
+ Item = get_roster_by_jid_t(LUser, LServer, LJID),
Item1 = process_item_attrs(Item, Attrs),
Item2 = process_item_els(Item1, Els),
case Item2#roster.subscription of
remove ->
- mnesia:delete({roster, {LUser, LServer, LJID}});
+ del_roster_t(LUser, LServer, LJID);
_ ->
- mnesia:write(Item2)
+ update_roster_t(LUser, LServer, LJID, Item2)
end,
%% If the item exist in shared roster, take the
%% subscription information from there:
Item3 = ejabberd_hooks:run_fold(roster_process_item,
LServer, Item2, [LServer]),
case roster_version_on_db(LServer) of
- true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
- false -> ok
+ true -> write_roster_version_t(LUser, LServer);
+ false -> ok
end,
{Item, Item3}
end,
- case mnesia:transaction(F) of
+ case transaction(LServer, F) of
{atomic, {OldItem, Item}} ->
push_item(User, LServer, To, Item),
case Item#roster.subscription of
@@ -351,7 +501,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
error ->
process_item_attrs(Item, Attrs);
JID1 ->
- JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
+ JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
process_item_attrs(Item#roster{jid = JID}, Attrs)
end;
"name" ->
@@ -435,30 +585,54 @@ push_item_version(Server, User, From, Item, RosterVersion) ->
push_item(User, Server, Resource, From, Item, RosterVersion)
end, ejabberd_sm:get_user_resources(User, Server)).
-get_subscription_lists(_, User, Server) ->
+get_subscription_lists(Acc, User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ Items = get_subscription_lists(Acc, LUser, LServer, DBType),
+ fill_subscription_lists(LServer, Items, [], []).
+
+get_subscription_lists(_, LUser, LServer, mnesia) ->
US = {LUser, LServer},
case mnesia:dirty_index_read(roster, US, #roster.us) of
Items when is_list(Items) ->
- fill_subscription_lists(Items, [], []);
+ Items;
_ ->
{[], []}
+ end;
+get_subscription_lists(_, LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:get_roster(LServer, Username) of
+ {selected, ["username", "jid", "nick", "subscription", "ask",
+ "askmessage", "server", "subscribe", "type"],
+ Items} when is_list(Items) ->
+ Items;
+ _ ->
+ []
end.
-fill_subscription_lists([I | Is], F, T) ->
+fill_subscription_lists(LServer, [#roster{} = I | Is], F, T) ->
J = element(3, I#roster.usj),
case I#roster.subscription of
both ->
- fill_subscription_lists(Is, [J | F], [J | T]);
+ fill_subscription_lists(LServer, Is, [J | F], [J | T]);
from ->
- fill_subscription_lists(Is, [J | F], T);
+ fill_subscription_lists(LServer, Is, [J | F], T);
to ->
- fill_subscription_lists(Is, F, [J | T]);
+ fill_subscription_lists(LServer, Is, F, [J | T]);
_ ->
- fill_subscription_lists(Is, F, T)
+ fill_subscription_lists(LServer, Is, F, T)
end;
-fill_subscription_lists([], F, T) ->
+fill_subscription_lists(LServer, [RawI | Is], F, T) ->
+ I = raw_to_record(LServer, RawI),
+ case I of
+ %% Bad JID in database:
+ error ->
+ fill_subscription_lists(LServer, Is, F, T);
+ _ ->
+ fill_subscription_lists(LServer, [I | Is], F, T)
+ end;
+fill_subscription_lists(_LServer, [], F, T) ->
{F, T}.
ask_to_pending(subscribe) -> out;
@@ -466,6 +640,25 @@ ask_to_pending(unsubscribe) -> none;
ask_to_pending(Ask) -> Ask.
+roster_subscribe_t(LUser, LServer, LJID, Item) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ roster_subscribe_t(LUser, LServer, LJID, Item, DBType).
+
+roster_subscribe_t(_LUser, _LServer, _LJID, Item, mnesia) ->
+ mnesia:write(Item);
+roster_subscribe_t(LUser, LServer, LJID, Item, odbc) ->
+ ItemVals = record_to_string(Item),
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals).
+
+transaction(LServer, F) ->
+ case gen_mod:db_type(LServer, ?MODULE) of
+ mnesia ->
+ mnesia:transaction(F);
+ odbc ->
+ ejabberd_odbc:sql_transaction(LServer, F)
+ end.
in_subscription(_, User, Server, JID, Type, Reason) ->
process_subscription(in, User, Server, JID, Type, Reason).
@@ -473,23 +666,54 @@ in_subscription(_, User, Server, JID, Type, Reason) ->
out_subscription(User, Server, JID, Type) ->
process_subscription(out, User, Server, JID, Type, []).
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ get_roster_by_jid_with_groups_t(LUser, LServer, LJID, DBType).
+
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID, mnesia) ->
+ case mnesia:read({roster, {LUser, LServer, LJID}}) of
+ [] ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID};
+ [I] ->
+ I
+ end;
+get_roster_by_jid_with_groups_t(LUser, LServer, LJID, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of
+ {selected,
+ ["username", "jid", "nick", "subscription", "ask",
+ "askmessage", "server", "subscribe", "type"],
+ [I]} ->
+ %% raw_to_record can return error, but
+ %% jlib_to_string would fail before this point
+ R = raw_to_record(LServer, I),
+ Groups =
+ case odbc_queries:get_roster_groups(LServer, Username, SJID) of
+ {selected, ["grp"], JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
+ _ ->
+ []
+ end,
+ R#roster{groups = Groups};
+ {selected,
+ ["username", "jid", "nick", "subscription", "ask",
+ "askmessage", "server", "subscribe", "type"],
+ []} ->
+ #roster{usj = {LUser, LServer, LJID},
+ us = {LUser, LServer},
+ jid = LJID}
+ end.
+
process_subscription(Direction, User, Server, JID1, Type, Reason) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
- US = {LUser, LServer},
LJID = jlib:jid_tolower(JID1),
F = fun() ->
- Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [] ->
- JID = {JID1#jid.user,
- JID1#jid.server,
- JID1#jid.resource},
- #roster{usj = {LUser, LServer, LJID},
- us = US,
- jid = JID};
- [I] ->
- I
- end,
+ Item = get_roster_by_jid_with_groups_t(
+ LUser, LServer, LJID),
NewState = case Direction of
out ->
out_state_change(Item#roster.subscription,
@@ -518,21 +742,21 @@ process_subscription(Direction, User, Server, JID1, Type, Reason) ->
{none, AutoReply};
{none, none} when Item#roster.subscription == none,
Item#roster.ask == in ->
- mnesia:delete({roster, {LUser, LServer, LJID}}),
+ del_roster_t(LUser, LServer, LJID),
{none, AutoReply};
{Subscription, Pending} ->
NewItem = Item#roster{subscription = Subscription,
ask = Pending,
askmessage = list_to_binary(AskMessage)},
- mnesia:write(NewItem),
+ roster_subscribe_t(LUser, LServer, LJID, NewItem),
case roster_version_on_db(LServer) of
- true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
- false -> ok
+ true -> write_roster_version_t(LUser, LServer);
+ false -> ok
end,
{{push, NewItem}, AutoReply}
end
end,
- case mnesia:transaction(F) of
+ case transaction(LServer, F) of
{atomic, {Push, AutoReply}} ->
case AutoReply of
none ->
@@ -663,6 +887,9 @@ in_auto_reply(_, _, _) -> none.
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
US = {LUser, LServer},
send_unsubscription_to_rosteritems(LUser, LServer),
F = fun() ->
@@ -671,7 +898,12 @@ remove_user(User, Server) ->
end,
mnesia:index_read(roster, US, #roster.us))
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ send_unsubscription_to_rosteritems(LUser, LServer),
+ odbc_queries:del_user_roster_t(LServer, Username),
+ ok.
%% For each contact with Subscription:
%% Both or From, send a "unsubscribed" presence stanza;
@@ -725,11 +957,36 @@ set_items(User, Server, SubEl) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
F = fun() ->
- lists:foreach(fun(El) ->
- process_item_set_t(LUser, LServer, El)
- end, Els)
- end,
- mnesia:transaction(F).
+ lists:foreach(
+ fun(El) ->
+ process_item_set_t(LUser, LServer, El)
+ end, Els)
+ end,
+ transaction(LServer, F).
+
+update_roster_t(LUser, LServer, LJID, Item) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ update_roster_t(LUser, LServer, LJID, Item, DBType).
+
+update_roster_t(_LUser, _LServer,_LJID, Item, mnesia) ->
+ mnesia:write(Item);
+update_roster_t(LUser, _LServer, LJID, Item, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ ItemVals = record_to_string(Item),
+ ItemGroups = groups_to_string(Item),
+ odbc_queries:update_roster_sql(Username, SJID, ItemVals, ItemGroups).
+
+del_roster_t(LUser, LServer, LJID) ->
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ del_roster_t(LUser, LServer, LJID, DBType).
+
+del_roster_t(LUser, LServer, LJID, mnesia) ->
+ mnesia:delete({roster, {LUser, LServer, LJID}});
+del_roster_t(LUser, _LServer, LJID, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ odbc_queries:del_roster_sql(Username, SJID).
process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
@@ -744,12 +1001,12 @@ process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
jid = JID},
Item1 = process_item_attrs_ws(Item, Attrs),
Item2 = process_item_els(Item1, Els),
- case Item2#roster.subscription of
- remove ->
- mnesia:delete({roster, {LUser, LServer, LJID}});
- _ ->
- mnesia:write(Item2)
- end
+ case Item2#roster.subscription of
+ remove ->
+ del_roster_t(LUser, LServer, LJID);
+ _ ->
+ update_roster_t(LUser, LServer, LJID, Item2)
+ end
end;
process_item_set_t(_LUser, _LServer, _) ->
ok.
@@ -761,7 +1018,7 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
error ->
process_item_attrs_ws(Item, Attrs);
JID1 ->
- JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
+ JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
end;
"name" ->
@@ -795,6 +1052,11 @@ process_item_attrs_ws(Item, []) ->
Item.
get_in_pending_subscriptions(Ls, User, Server) ->
+ LServer = jlib:nameprep(Server),
+ get_in_pending_subscriptions(Ls, User, Server,
+ gen_mod:db_type(LServer, ?MODULE)).
+
+get_in_pending_subscriptions(Ls, User, Server, mnesia) ->
JID = jlib:make_jid(User, Server, ""),
US = {JID#jid.luser, JID#jid.lserver},
case mnesia:dirty_index_read(roster, US, #roster.us) of
@@ -825,30 +1087,99 @@ get_in_pending_subscriptions(Ls, User, Server) ->
Result));
_ ->
Ls
+ end;
+get_in_pending_subscriptions(Ls, User, Server, odbc) ->
+ JID = jlib:make_jid(User, Server, ""),
+ LUser = JID#jid.luser,
+ LServer = JID#jid.lserver,
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:get_roster(LServer, Username) of
+ {selected, ["username", "jid", "nick", "subscription", "ask",
+ "askmessage", "server", "subscribe", "type"],
+ Items} when is_list(Items) ->
+ Ls ++ lists:map(
+ fun(R) ->
+ Message = R#roster.askmessage,
+ {xmlelement, "presence",
+ [{"from", jlib:jid_to_string(R#roster.jid)},
+ {"to", jlib:jid_to_string(JID)},
+ {"type", "subscribe"}],
+ [{xmlelement, "status", [],
+ [{xmlcdata, Message}]}]}
+ end,
+ lists:flatmap(
+ fun(I) ->
+ case raw_to_record(LServer, I) of
+ %% Bad JID in database:
+ error ->
+ [];
+ R ->
+ case R#roster.ask of
+ in -> [R];
+ both -> [R];
+ _ -> []
+ end
+ end
+ end,
+ Items));
+ _ ->
+ Ls
end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-get_jid_info(_, User, Server, JID) ->
+read_subscription_and_groups(User, Server, LJID) ->
LUser = jlib:nodeprep(User),
- LJID = jlib:jid_tolower(JID),
LServer = jlib:nameprep(Server),
+ read_subscription_and_groups(LUser, LServer, LJID,
+ gen_mod:db_type(LServer, ?MODULE)).
+
+read_subscription_and_groups(LUser, LServer, LJID, mnesia) ->
case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
[#roster{subscription = Subscription, groups = Groups}] ->
{Subscription, Groups};
- _ ->
+ _ ->
+ error
+ end;
+read_subscription_and_groups(LUser, LServer, LJID, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
+ case catch odbc_queries:get_subscription(LServer, Username, SJID) of
+ {selected, ["subscription"], [{SSubscription}]} ->
+ Subscription = case SSubscription of
+ "B" -> both;
+ "T" -> to;
+ "F" -> from;
+ _ -> none
+ end,
+ Groups = case catch odbc_queries:get_rostergroup_by_jid(
+ LServer, Username, SJID) of
+ {selected, ["grp"], JGrps} when is_list(JGrps) ->
+ [JGrp || {JGrp} <- JGrps];
+ _ ->
+ []
+ end,
+ {Subscription, Groups};
+ _ ->
+ error
+ end.
+
+get_jid_info(_, User, Server, JID) ->
+ LJID = jlib:jid_tolower(JID),
+ case read_subscription_and_groups(User, Server, LJID) of
+ {Subscription, Groups} ->
+ {Subscription, Groups};
+ error ->
LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
if
LRJID == LJID ->
{none, []};
true ->
- case catch mnesia:dirty_read(
- roster, {LUser, LServer, LRJID}) of
- [#roster{subscription = Subscription,
- groups = Groups}] ->
+ case read_subscription_and_groups(
+ User, Server, LRJID) of
+ {Subscription, Groups} ->
{Subscription, Groups};
- _ ->
+ error ->
{none, []}
end
end
@@ -856,6 +1187,75 @@ get_jid_info(_, User, Server, JID) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
+ _SServer, _SSubscribe, _SType}) ->
+ case jlib:string_to_jid(SJID) of
+ error ->
+ error;
+ JID ->
+ LJID = jlib:jid_tolower(JID),
+ Subscription = case SSubscription of
+ "B" -> both;
+ "T" -> to;
+ "F" -> from;
+ _ -> none
+ end,
+ Ask = case SAsk of
+ "S" -> subscribe;
+ "U" -> unsubscribe;
+ "B" -> both;
+ "O" -> out;
+ "I" -> in;
+ _ -> none
+ end,
+ #roster{usj = {User, LServer, LJID},
+ us = {User, LServer},
+ jid = LJID,
+ name = Nick,
+ subscription = Subscription,
+ ask = Ask,
+ askmessage = SAskMessage}
+ end.
+
+record_to_string(#roster{us = {User, _Server},
+ jid = JID,
+ name = Name,
+ subscription = Subscription,
+ ask = Ask,
+ askmessage = AskMessage}) ->
+ Username = ejabberd_odbc:escape(User),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
+ Nick = ejabberd_odbc:escape(Name),
+ SSubscription = case Subscription of
+ both -> "B";
+ to -> "T";
+ from -> "F";
+ none -> "N"
+ end,
+ SAsk = case Ask of
+ subscribe -> "S";
+ unsubscribe -> "U";
+ both -> "B";
+ out -> "O";
+ in -> "I";
+ none -> "N"
+ end,
+ SAskMessage = ejabberd_odbc:escape(AskMessage),
+ [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"].
+
+groups_to_string(#roster{us = {User, _Server},
+ jid = JID,
+ groups = Groups}) ->
+ Username = ejabberd_odbc:escape(User),
+ SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
+
+ %% Empty groups do not need to be converted to string to be inserted in
+ %% the database
+ lists:foldl(
+ fun([], Acc) -> Acc;
+ (Group, Acc) ->
+ G = ejabberd_odbc:escape(Group),
+ [[Username, SJID, G]|Acc] end, [], Groups).
update_table() ->
Fields = record_info(fields, roster),
@@ -927,10 +1327,12 @@ webadmin_page(_, Host,
webadmin_page(Acc, _, _) -> Acc.
user_roster(User, Server, Query, Lang) ->
- US = {jlib:nodeprep(User), jlib:nameprep(Server)},
- Items1 = mnesia:dirty_index_read(roster, US, #roster.us),
+ LUser = jlib:nodeprep(User),
+ LServer = jlib:nameprep(Server),
+ US = {LUser, LServer},
+ Items1 = get_roster(LUser, LServer),
Res = user_roster_parse_query(User, Server, Items1, Query),
- Items = mnesia:dirty_index_read(roster, US, #roster.us),
+ Items = get_roster(LUser, LServer),
SItems = lists:sort(Items),
FItems =
case SItems of
diff --git a/src/mod_roster_odbc.erl b/src/mod_roster_odbc.erl
deleted file mode 100644
index b4162ca50..000000000
--- a/src/mod_roster_odbc.erl
+++ /dev/null
@@ -1,1211 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_roster_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Roster management
-%%% Created : 15 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
-%%% @doc Roster management (Mnesia storage).
-%%%
-%%% Includes support for XEP-0237: Roster Versioning.
-%%% The roster versioning follows an all-or-nothing strategy:
-%%% - If the version supplied by the client is the latest, return an empty response.
-%%% - If not, return the entire new roster (with updated version string).
-%%% Roster version is a hash digest of the entire roster.
-%%% No additional data is stored in DB.
-
--module(mod_roster_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
- process_iq/3,
- process_local_iq/3,
- get_user_roster/2,
- get_subscription_lists/3,
- get_in_pending_subscriptions/3,
- in_subscription/6,
- out_subscription/4,
- set_items/3,
- remove_user/2,
- get_jid_info/4,
- webadmin_page/3,
- webadmin_user/4,
- get_versioning_feature/2,
- roster_versioning_enabled/1]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_roster.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
-
-start(Host, Opts) ->
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- ejabberd_hooks:add(roster_get, Host,
- ?MODULE, get_user_roster, 50),
- ejabberd_hooks:add(roster_in_subscription, Host,
- ?MODULE, in_subscription, 50),
- ejabberd_hooks:add(roster_out_subscription, Host,
- ?MODULE, out_subscription, 50),
- ejabberd_hooks:add(roster_get_subscription_lists, Host,
- ?MODULE, get_subscription_lists, 50),
- ejabberd_hooks:add(roster_get_jid_info, Host,
- ?MODULE, get_jid_info, 50),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(resend_subscription_requests_hook, Host,
- ?MODULE, get_in_pending_subscriptions, 50),
- ejabberd_hooks:add(roster_get_versioning_feature, Host,
- ?MODULE, get_versioning_feature, 50),
- ejabberd_hooks:add(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:add(webadmin_user, Host,
- ?MODULE, webadmin_user, 50),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,
- ?MODULE, process_iq, IQDisc).
-
-stop(Host) ->
- ejabberd_hooks:delete(roster_get, Host,
- ?MODULE, get_user_roster, 50),
- ejabberd_hooks:delete(roster_in_subscription, Host,
- ?MODULE, in_subscription, 50),
- ejabberd_hooks:delete(roster_out_subscription, Host,
- ?MODULE, out_subscription, 50),
- ejabberd_hooks:delete(roster_get_subscription_lists, Host,
- ?MODULE, get_subscription_lists, 50),
- ejabberd_hooks:delete(roster_get_jid_info, Host,
- ?MODULE, get_jid_info, 50),
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(resend_subscription_requests_hook, Host,
- ?MODULE, get_in_pending_subscriptions, 50),
- ejabberd_hooks:delete(roster_get_versioning_feature, Host,
- ?MODULE, get_versioning_feature, 50),
- ejabberd_hooks:delete(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:delete(webadmin_user, Host,
- ?MODULE, webadmin_user, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ROSTER).
-
-
-process_iq(From, To, IQ) ->
- #iq{sub_el = SubEl} = IQ,
- #jid{lserver = LServer} = From,
- case lists:member(LServer, ?MYHOSTS) of
- true ->
- process_local_iq(From, To, IQ);
- _ ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]}
- end.
-
-process_local_iq(From, To, #iq{type = Type} = IQ) ->
- case Type of
- set ->
- process_iq_set(From, To, IQ);
- get ->
- process_iq_get(From, To, IQ)
- end.
-
-
-roster_hash(Items) ->
- sha:sha(term_to_binary(
- lists:sort(
- [R#roster{groups = lists:sort(Grs)} ||
- R = #roster{groups = Grs} <- Items]))).
-
-roster_versioning_enabled(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, versioning, false).
-
-roster_version_on_db(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false).
-
-%% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled.
-get_versioning_feature(Acc, Host) ->
- case roster_versioning_enabled(Host) of
- true ->
- Feature = {xmlelement,
- "ver",
- [{"xmlns", ?NS_ROSTER_VER}],
- [{xmlelement, "optional", [], []}]},
- [Feature | Acc];
- false -> []
- end.
-
-roster_version(LServer ,LUser) ->
- US = {LUser, LServer},
- case roster_version_on_db(LServer) of
- true ->
- case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of
- {selected, ["version"], [{Version}]} -> Version;
- {selected, ["version"], []} ->
- %% If for some reason we don't had it on DB. Create a version Id and store it.
- %% (we did the same on process_iq_get, that is called when client get roster,
- %% not sure why it can still not be on DB at this point)
- RosterVersion = sha:sha(term_to_binary(now())),
- {atomic, {updated,1}} = odbc_queries:sql_transaction(LServer, fun() ->
- odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion)
- end),
- RosterVersion
- end;
- false ->
- roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
- end.
-
-%% Load roster from DB only if neccesary.
-%% It is neccesary if
-%% - roster versioning is disabled in server OR
-%% - roster versioning is not used by the client OR
-%% - roster versioning is used by server and client, BUT the server isn't storing versions on db OR
-%% - the roster version from client don't match current version.
-process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
- LUser = From#jid.luser,
- LServer = From#jid.lserver,
- US = {LUser, LServer},
-
- try
- {ItemsToSend, VersionToSend} =
- case {xml:get_tag_attr("ver", SubEl),
- roster_versioning_enabled(LServer),
- roster_version_on_db(LServer)} of
- {{value, RequestedVersion}, true, true} ->
- %% Retrieve version from DB. Only load entire roster
- %% when neccesary.
- case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of
- {selected, ["version"], [{RequestedVersion}]} ->
- {false, false};
- {selected, ["version"], [{NewVersion}]} ->
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion};
- {selected, ["version"], []} ->
- RosterVersion = sha:sha(term_to_binary(now())),
- {atomic, {updated,1}} = odbc_queries:sql_transaction(LServer, fun() ->
- odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion)
- end),
-
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion}
- end;
-
- {{value, RequestedVersion}, true, false} ->
- RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]),
- case roster_hash(RosterItems) of
- RequestedVersion ->
- {false, false};
- New ->
- {lists:map(fun item_to_xml/1, RosterItems), New}
- end;
-
- _ ->
- {lists:map(fun item_to_xml/1,
- ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false}
- end,
- IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of
- {false, false} -> [];
- {Items, false} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], Items}];
- {Items, Version} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}, {"ver", Version}], Items}]
- end}
- catch
- _:_ ->
- IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
- end.
-
-
-get_user_roster(Acc, {LUser, LServer}) ->
- Items = get_roster(LUser, LServer),
- lists:filter(fun(#roster{subscription = none, ask = in}) ->
- false;
- (_) ->
- true
- end, Items) ++ Acc.
-
-get_roster(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected, ["username", "jid", "nick", "subscription", "ask",
- "askmessage", "server", "subscribe", "type"],
- Items} when is_list(Items) ->
- JIDGroups = case catch odbc_queries:get_roster_jid_groups(LServer, Username) of
- {selected, ["jid","grp"], JGrps}
- when is_list(JGrps) ->
- JGrps;
- _ ->
- []
- end,
- GroupsDict =
- lists:foldl(
- fun({J, G}, Acc) ->
- dict:append(J, G, Acc)
- end, dict:new(), JIDGroups),
- RItems = lists:flatmap(
- fun(I) ->
- case raw_to_record(LServer, I) of
- %% Bad JID in database:
- error ->
- [];
- R ->
- SJID = jlib:jid_to_string(R#roster.jid),
- Groups =
- case dict:find(SJID, GroupsDict) of
- {ok, Gs} -> Gs;
- error -> []
- end,
- [R#roster{groups = Groups}]
- end
- end, Items),
- RItems;
- _ ->
- []
- end.
-
-
-item_to_xml(Item) ->
- Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
- Attrs2 = case Item#roster.name of
- "" ->
- Attrs1;
- Name ->
- [{"name", Name} | Attrs1]
- end,
- Attrs3 = case Item#roster.subscription of
- none ->
- [{"subscription", "none"} | Attrs2];
- from ->
- [{"subscription", "from"} | Attrs2];
- to ->
- [{"subscription", "to"} | Attrs2];
- both ->
- [{"subscription", "both"} | Attrs2];
- remove ->
- [{"subscription", "remove"} | Attrs2]
- end,
- Attrs = case ask_to_pending(Item#roster.ask) of
- out ->
- [{"ask", "subscribe"} | Attrs3];
- both ->
- [{"ask", "subscribe"} | Attrs3];
- _ ->
- Attrs3
- end,
- SubEls = lists:map(fun(G) ->
- {xmlelement, "group", [], [{xmlcdata, G}]}
- end, Item#roster.groups),
- {xmlelement, "item", Attrs, SubEls}.
-
-
-process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
- {xmlelement, _Name, _Attrs, Els} = SubEl,
- lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els),
- IQ#iq{type = result, sub_el = []}.
-
-process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
- JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
- #jid{user = User, luser = LUser, lserver = LServer} = From,
- case JID1 of
- error ->
- ok;
- _ ->
- LJID = jlib:jid_tolower(JID1),
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- F = fun() ->
- {selected,
- ["username", "jid", "nick", "subscription",
- "ask", "askmessage", "server", "subscribe", "type"],
- Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID),
- Item = case Res of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = LJID};
- [I] ->
- R = raw_to_record(LServer, I),
- case R of
- %% Bad JID in database:
- error ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = LJID};
- _ ->
- R#roster{
- usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = LJID,
- name = ""}
- end
- end,
- Item1 = process_item_attrs(Item, Attrs),
- Item2 = process_item_els(Item1, Els),
- case Item2#roster.subscription of
- remove ->
- odbc_queries:del_roster(LServer, Username, SJID);
- _ ->
- ItemVals = record_to_string(Item2),
- ItemGroups = groups_to_string(Item2),
- odbc_queries:update_roster(LServer, Username, SJID, ItemVals, ItemGroups)
- end,
- %% If the item exist in shared roster, take the
- %% subscription information from there:
- Item3 = ejabberd_hooks:run_fold(roster_process_item,
- LServer, Item2, [LServer]),
- case roster_version_on_db(LServer) of
- true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha(term_to_binary(now())));
- false -> ok
- end,
- {Item, Item3}
- end,
- case odbc_queries:sql_transaction(LServer, F) of
- {atomic, {OldItem, Item}} ->
- push_item(User, LServer, To, Item),
- case Item#roster.subscription of
- remove ->
- send_unsubscribing_presence(From, OldItem),
- ok;
- _ ->
- ok
- end;
- E ->
- ?DEBUG("ROSTER: roster item set error: ~p~n", [E]),
- ok
- end
- end;
-process_item_set(_From, _To, _) ->
- ok.
-
-process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
- case Attr of
- "jid" ->
- case jlib:string_to_jid(Val) of
- error ->
- process_item_attrs(Item, Attrs);
- JID1 ->
- JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
- process_item_attrs(Item#roster{jid = JID}, Attrs)
- end;
- "name" ->
- process_item_attrs(Item#roster{name = Val}, Attrs);
- "subscription" ->
- case Val of
- "remove" ->
- process_item_attrs(Item#roster{subscription = remove},
- Attrs);
- _ ->
- process_item_attrs(Item, Attrs)
- end;
- "ask" ->
- process_item_attrs(Item, Attrs);
- _ ->
- process_item_attrs(Item, Attrs)
- end;
-process_item_attrs(Item, []) ->
- Item.
-
-
-process_item_els(Item, [{xmlelement, Name, _Attrs, SEls} | Els]) ->
- case Name of
- "group" ->
- Groups = [xml:get_cdata(SEls) | Item#roster.groups],
- process_item_els(Item#roster{groups = Groups}, Els);
- _ ->
- process_item_els(Item, Els)
- end;
-process_item_els(Item, [{xmlcdata, _} | Els]) ->
- process_item_els(Item, Els);
-process_item_els(Item, []) ->
- Item.
-
-
-push_item(User, Server, From, Item) ->
- ejabberd_sm:route(jlib:make_jid("", "", ""),
- jlib:make_jid(User, Server, ""),
- {xmlelement, "broadcast", [],
- [{item,
- Item#roster.jid,
- Item#roster.subscription}]}),
- case roster_versioning_enabled(Server) of
- true ->
- push_item_version(Server, User, From, Item, roster_version(Server, User));
- false ->
- lists:foreach(fun(Resource) ->
- push_item(User, Server, Resource, From, Item)
- end, ejabberd_sm:get_user_resources(User, Server))
- end.
-
-% TODO: don't push to those who not load roster
-push_item(User, Server, Resource, From, Item) ->
- ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
- id = "push" ++ randoms:get_string(),
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_ROSTER}],
- [item_to_xml(Item)]}]},
- ejabberd_router:route(
- From,
- jlib:make_jid(User, Server, Resource),
- jlib:iq_to_xml(ResIQ)).
-
-%% @doc Roster push, calculate and include the version attribute.
-%% TODO: don't push to those who didn't load roster
-push_item_version(Server, User, From, Item, RosterVersion) ->
- lists:foreach(fun(Resource) ->
- push_item_version(User, Server, Resource, From, Item, RosterVersion)
- end, ejabberd_sm:get_user_resources(User, Server)).
-
-push_item_version(User, Server, Resource, From, Item, RosterVersion) ->
- IQPush = #iq{type = 'set', xmlns = ?NS_ROSTER,
- id = "push" ++ randoms:get_string(),
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_ROSTER},
- {"ver", RosterVersion}],
- [item_to_xml(Item)]}]},
- ejabberd_router:route(
- From,
- jlib:make_jid(User, Server, Resource),
- jlib:iq_to_xml(IQPush)).
-
-get_subscription_lists(_, User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected, ["username", "jid", "nick", "subscription", "ask",
- "askmessage", "server", "subscribe", "type"],
- Items} when is_list(Items) ->
- fill_subscription_lists(LServer, Items, [], []);
- _ ->
- {[], []}
- end.
-
-fill_subscription_lists(LServer, [RawI | Is], F, T) ->
- I = raw_to_record(LServer, RawI),
- case I of
- %% Bad JID in database:
- error ->
- fill_subscription_lists(LServer, Is, F, T);
- _ ->
- J = I#roster.jid,
- case I#roster.subscription of
- both ->
- fill_subscription_lists(LServer, Is, [J | F], [J | T]);
- from ->
- fill_subscription_lists(LServer, Is, [J | F], T);
- to ->
- fill_subscription_lists(LServer, Is, F, [J | T]);
- _ ->
- fill_subscription_lists(LServer, Is, F, T)
- end
- end;
-fill_subscription_lists(_LServer, [], F, T) ->
- {F, T}.
-
-ask_to_pending(subscribe) -> out;
-ask_to_pending(unsubscribe) -> none;
-ask_to_pending(Ask) -> Ask.
-
-
-
-in_subscription(_, User, Server, JID, Type, Reason) ->
- process_subscription(in, User, Server, JID, Type, Reason).
-
-out_subscription(User, Server, JID, Type) ->
- process_subscription(out, User, Server, JID, Type, []).
-
-process_subscription(Direction, User, Server, JID1, Type, Reason) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- LJID = jlib:jid_tolower(JID1),
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- F = fun() ->
- Item =
- case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of
- {selected,
- ["username", "jid", "nick", "subscription", "ask",
- "askmessage", "server", "subscribe", "type"],
- [I]} ->
- %% raw_to_record can return error, but
- %% jlib_to_string would fail before this point
- R = raw_to_record(LServer, I),
- Groups =
- case odbc_queries:get_roster_groups(LServer, Username, SJID) of
- {selected, ["grp"], JGrps} when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ ->
- []
- end,
- R#roster{groups = Groups};
- {selected,
- ["username", "jid", "nick", "subscription", "ask",
- "askmessage", "server", "subscribe", "type"],
- []} ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = LJID}
- end,
- NewState = case Direction of
- out ->
- out_state_change(Item#roster.subscription,
- Item#roster.ask,
- Type);
- in ->
- in_state_change(Item#roster.subscription,
- Item#roster.ask,
- Type)
- end,
- AutoReply = case Direction of
- out ->
- none;
- in ->
- in_auto_reply(Item#roster.subscription,
- Item#roster.ask,
- Type)
- end,
- AskMessage = case NewState of
- {_, both} -> Reason;
- {_, in} -> Reason;
- _ -> ""
- end,
- case NewState of
- none ->
- {none, AutoReply};
- {none, none} when Item#roster.subscription == none,
- Item#roster.ask == in ->
- odbc_queries:del_roster(LServer, Username, SJID),
- {none, AutoReply};
- {Subscription, Pending} ->
- NewItem = Item#roster{subscription = Subscription,
- ask = Pending,
- askmessage = AskMessage},
- ItemVals = record_to_string(NewItem),
- odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals),
- case roster_version_on_db(LServer) of
- true -> odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), sha:sha(term_to_binary(now())));
- false -> ok
- end,
- {{push, NewItem}, AutoReply}
- end
- end,
- case odbc_queries:sql_transaction(LServer, F) of
- {atomic, {Push, AutoReply}} ->
- case AutoReply of
- none ->
- ok;
- _ ->
- T = case AutoReply of
- subscribed -> "subscribed";
- unsubscribed -> "unsubscribed"
- end,
- ejabberd_router:route(
- jlib:make_jid(User, Server, ""), JID1,
- {xmlelement, "presence", [{"type", T}], []})
- end,
- case Push of
- {push, Item} ->
- if
- Item#roster.subscription == none,
- Item#roster.ask == in ->
- ok;
- true ->
- push_item(User, Server,
- jlib:make_jid(User, Server, ""), Item)
- end,
- true;
- none ->
- false
- end;
- _ ->
- false
- end.
-
-
-%% in_state_change(Subscription, Pending, Type) -> NewState
-%% NewState = none | {NewSubscription, NewPending}
--ifdef(ROSTER_GATEWAY_WORKAROUND).
--define(NNSD, {to, none}).
--define(NISD, {to, in}).
--else.
--define(NNSD, none).
--define(NISD, none).
--endif.
-
-in_state_change(none, none, subscribe) -> {none, in};
-in_state_change(none, none, subscribed) -> ?NNSD;
-in_state_change(none, none, unsubscribe) -> none;
-in_state_change(none, none, unsubscribed) -> none;
-in_state_change(none, out, subscribe) -> {none, both};
-in_state_change(none, out, subscribed) -> {to, none};
-in_state_change(none, out, unsubscribe) -> none;
-in_state_change(none, out, unsubscribed) -> {none, none};
-in_state_change(none, in, subscribe) -> none;
-in_state_change(none, in, subscribed) -> ?NISD;
-in_state_change(none, in, unsubscribe) -> {none, none};
-in_state_change(none, in, unsubscribed) -> none;
-in_state_change(none, both, subscribe) -> none;
-in_state_change(none, both, subscribed) -> {to, in};
-in_state_change(none, both, unsubscribe) -> {none, out};
-in_state_change(none, both, unsubscribed) -> {none, in};
-in_state_change(to, none, subscribe) -> {to, in};
-in_state_change(to, none, subscribed) -> none;
-in_state_change(to, none, unsubscribe) -> none;
-in_state_change(to, none, unsubscribed) -> {none, none};
-in_state_change(to, in, subscribe) -> none;
-in_state_change(to, in, subscribed) -> none;
-in_state_change(to, in, unsubscribe) -> {to, none};
-in_state_change(to, in, unsubscribed) -> {none, in};
-in_state_change(from, none, subscribe) -> none;
-in_state_change(from, none, subscribed) -> {both, none};
-in_state_change(from, none, unsubscribe) -> {none, none};
-in_state_change(from, none, unsubscribed) -> none;
-in_state_change(from, out, subscribe) -> none;
-in_state_change(from, out, subscribed) -> {both, none};
-in_state_change(from, out, unsubscribe) -> {none, out};
-in_state_change(from, out, unsubscribed) -> {from, none};
-in_state_change(both, none, subscribe) -> none;
-in_state_change(both, none, subscribed) -> none;
-in_state_change(both, none, unsubscribe) -> {to, none};
-in_state_change(both, none, unsubscribed) -> {from, none}.
-
-out_state_change(none, none, subscribe) -> {none, out};
-out_state_change(none, none, subscribed) -> none;
-out_state_change(none, none, unsubscribe) -> none;
-out_state_change(none, none, unsubscribed) -> none;
-out_state_change(none, out, subscribe) -> {none, out}; %% We need to resend query (RFC3921, section 9.2)
-out_state_change(none, out, subscribed) -> none;
-out_state_change(none, out, unsubscribe) -> {none, none};
-out_state_change(none, out, unsubscribed) -> none;
-out_state_change(none, in, subscribe) -> {none, both};
-out_state_change(none, in, subscribed) -> {from, none};
-out_state_change(none, in, unsubscribe) -> none;
-out_state_change(none, in, unsubscribed) -> {none, none};
-out_state_change(none, both, subscribe) -> none;
-out_state_change(none, both, subscribed) -> {from, out};
-out_state_change(none, both, unsubscribe) -> {none, in};
-out_state_change(none, both, unsubscribed) -> {none, out};
-out_state_change(to, none, subscribe) -> none;
-out_state_change(to, none, subscribed) -> {both, none};
-out_state_change(to, none, unsubscribe) -> {none, none};
-out_state_change(to, none, unsubscribed) -> none;
-out_state_change(to, in, subscribe) -> none;
-out_state_change(to, in, subscribed) -> {both, none};
-out_state_change(to, in, unsubscribe) -> {none, in};
-out_state_change(to, in, unsubscribed) -> {to, none};
-out_state_change(from, none, subscribe) -> {from, out};
-out_state_change(from, none, subscribed) -> none;
-out_state_change(from, none, unsubscribe) -> none;
-out_state_change(from, none, unsubscribed) -> {none, none};
-out_state_change(from, out, subscribe) -> none;
-out_state_change(from, out, subscribed) -> none;
-out_state_change(from, out, unsubscribe) -> {from, none};
-out_state_change(from, out, unsubscribed) -> {none, out};
-out_state_change(both, none, subscribe) -> none;
-out_state_change(both, none, subscribed) -> none;
-out_state_change(both, none, unsubscribe) -> {from, none};
-out_state_change(both, none, unsubscribed) -> {to, none}.
-
-in_auto_reply(from, none, subscribe) -> subscribed;
-in_auto_reply(from, out, subscribe) -> subscribed;
-in_auto_reply(both, none, subscribe) -> subscribed;
-in_auto_reply(none, in, unsubscribe) -> unsubscribed;
-in_auto_reply(none, both, unsubscribe) -> unsubscribed;
-in_auto_reply(to, in, unsubscribe) -> unsubscribed;
-in_auto_reply(from, none, unsubscribe) -> unsubscribed;
-in_auto_reply(from, out, unsubscribe) -> unsubscribed;
-in_auto_reply(both, none, unsubscribe) -> unsubscribed;
-in_auto_reply(_, _, _) -> none.
-
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- send_unsubscription_to_rosteritems(LUser, LServer),
- odbc_queries:del_user_roster_t(LServer, Username),
- ok.
-
-%% For each contact with Subscription:
-%% Both or From, send a "unsubscribed" presence stanza;
-%% Both or To, send a "unsubscribe" presence stanza.
-send_unsubscription_to_rosteritems(LUser, LServer) ->
- RosterItems = get_user_roster([], {LUser, LServer}),
- From = jlib:make_jid({LUser, LServer, ""}),
- lists:foreach(fun(RosterItem) ->
- send_unsubscribing_presence(From, RosterItem)
- end,
- RosterItems).
-
-%% @spec (From::jid(), Item::roster()) -> ok
-send_unsubscribing_presence(From, Item) ->
- IsTo = case Item#roster.subscription of
- both -> true;
- to -> true;
- _ -> false
- end,
- IsFrom = case Item#roster.subscription of
- both -> true;
- from -> true;
- _ -> false
- end,
- if IsTo ->
- send_presence_type(
- jlib:jid_remove_resource(From),
- jlib:make_jid(Item#roster.jid), "unsubscribe");
- true -> ok
- end,
- if IsFrom ->
- send_presence_type(
- jlib:jid_remove_resource(From),
- jlib:make_jid(Item#roster.jid), "unsubscribed");
- true -> ok
- end,
- ok.
-
-send_presence_type(From, To, Type) ->
- ejabberd_router:route(
- From, To,
- {xmlelement, "presence",
- [{"type", Type}],
- []}).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-set_items(User, Server, SubEl) ->
- {xmlelement, _Name, _Attrs, Els} = SubEl,
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- catch odbc_queries:sql_transaction(
- LServer,
- lists:flatmap(fun(El) ->
- process_item_set_t(LUser, LServer, El)
- end, Els)).
-
-process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
- JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
- case JID1 of
- error ->
- [];
- _ ->
- LJID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- Item = #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer},
- jid = LJID},
- Item1 = process_item_attrs_ws(Item, Attrs),
- Item2 = process_item_els(Item1, Els),
- case Item2#roster.subscription of
- remove ->
- odbc_queries:del_roster_sql(Username, SJID);
- _ ->
- ItemVals = record_to_string(Item1),
- ItemGroups = groups_to_string(Item2),
- odbc_queries:update_roster_sql(Username, SJID, ItemVals, ItemGroups)
- end
- end;
-process_item_set_t(_LUser, _LServer, _) ->
- [].
-
-process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
- case Attr of
- "jid" ->
- case jlib:string_to_jid(Val) of
- error ->
- process_item_attrs_ws(Item, Attrs);
- JID1 ->
- JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource},
- process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
- end;
- "name" ->
- process_item_attrs_ws(Item#roster{name = Val}, Attrs);
- "subscription" ->
- case Val of
- "remove" ->
- process_item_attrs_ws(Item#roster{subscription = remove},
- Attrs);
- "none" ->
- process_item_attrs_ws(Item#roster{subscription = none},
- Attrs);
- "both" ->
- process_item_attrs_ws(Item#roster{subscription = both},
- Attrs);
- "from" ->
- process_item_attrs_ws(Item#roster{subscription = from},
- Attrs);
- "to" ->
- process_item_attrs_ws(Item#roster{subscription = to},
- Attrs);
- _ ->
- process_item_attrs_ws(Item, Attrs)
- end;
- "ask" ->
- process_item_attrs_ws(Item, Attrs);
- _ ->
- process_item_attrs_ws(Item, Attrs)
- end;
-process_item_attrs_ws(Item, []) ->
- Item.
-
-get_in_pending_subscriptions(Ls, User, Server) ->
- JID = jlib:make_jid(User, Server, ""),
- LUser = JID#jid.luser,
- LServer = JID#jid.lserver,
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_roster(LServer, Username) of
- {selected, ["username", "jid", "nick", "subscription", "ask",
- "askmessage", "server", "subscribe", "type"],
- Items} when is_list(Items) ->
- Ls ++ lists:map(
- fun(R) ->
- Message = R#roster.askmessage,
- {xmlelement, "presence",
- [{"from", jlib:jid_to_string(R#roster.jid)},
- {"to", jlib:jid_to_string(JID)},
- {"type", "subscribe"}],
- [{xmlelement, "status", [],
- [{xmlcdata, Message}]}]}
- end,
- lists:flatmap(
- fun(I) ->
- case raw_to_record(LServer, I) of
- %% Bad JID in database:
- error ->
- [];
- R ->
- case R#roster.ask of
- in -> [R];
- both -> [R];
- _ -> []
- end
- end
- end,
- Items));
- _ ->
- Ls
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-get_jid_info(_, User, Server, JID) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- LJID = jlib:jid_tolower(JID),
- Username = ejabberd_odbc:escape(LUser),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
- case catch odbc_queries:get_subscription(LServer, Username, SJID) of
- {selected, ["subscription"], [{SSubscription}]} ->
- Subscription = case SSubscription of
- "B" -> both;
- "T" -> to;
- "F" -> from;
- _ -> none
- end,
- Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SJID) of
- {selected, ["grp"], JGrps} when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ ->
- []
- end,
- {Subscription, Groups};
- _ ->
- LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
- if
- LRJID == LJID ->
- {none, []};
- true ->
- SRJID = ejabberd_odbc:escape(jlib:jid_to_string(LRJID)),
- case catch odbc_queries:get_subscription(LServer, Username, SRJID) of
- {selected, ["subscription"], [{SSubscription}]} ->
- Subscription = case SSubscription of
- "B" -> both;
- "T" -> to;
- "F" -> from;
- _ -> none
- end,
- Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SRJID) of
- {selected, ["grp"], JGrps} when is_list(JGrps) ->
- [JGrp || {JGrp} <- JGrps];
- _ ->
- []
- end,
- {Subscription, Groups};
- _ ->
- {none, []}
- end
- end
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage,
- _SServer, _SSubscribe, _SType}) ->
- case jlib:string_to_jid(SJID) of
- error ->
- error;
- JID ->
- LJID = jlib:jid_tolower(JID),
- Subscription = case SSubscription of
- "B" -> both;
- "T" -> to;
- "F" -> from;
- _ -> none
- end,
- Ask = case SAsk of
- "S" -> subscribe;
- "U" -> unsubscribe;
- "B" -> both;
- "O" -> out;
- "I" -> in;
- _ -> none
- end,
- #roster{usj = {User, LServer, LJID},
- us = {User, LServer},
- jid = LJID,
- name = Nick,
- subscription = Subscription,
- ask = Ask,
- askmessage = SAskMessage}
- end.
-
-record_to_string(#roster{us = {User, _Server},
- jid = JID,
- name = Name,
- subscription = Subscription,
- ask = Ask,
- askmessage = AskMessage}) ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
- Nick = ejabberd_odbc:escape(Name),
- SSubscription = case Subscription of
- both -> "B";
- to -> "T";
- from -> "F";
- none -> "N"
- end,
- SAsk = case Ask of
- subscribe -> "S";
- unsubscribe -> "U";
- both -> "B";
- out -> "O";
- in -> "I";
- none -> "N"
- end,
- SAskMessage = ejabberd_odbc:escape(AskMessage),
- [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"].
-
-groups_to_string(#roster{us = {User, _Server},
- jid = JID,
- groups = Groups}) ->
- Username = ejabberd_odbc:escape(User),
- SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
-
- %% Empty groups do not need to be converted to string to be inserted in
- %% the database
- lists:foldl(
- fun([], Acc) -> Acc;
- (Group, Acc) ->
- G = ejabberd_odbc:escape(Group),
- [[Username, SJID, G]|Acc] end, [], Groups).
-
-webadmin_page(_, Host,
- #request{us = _US,
- path = ["user", U, "roster"],
- q = Query,
- lang = Lang} = _Request) ->
- Res = user_roster(U, Host, Query, Lang),
- {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-user_roster(User, Server, Query, Lang) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- US = {LUser, LServer},
- Items1 = get_roster(LUser, LServer),
- Res = user_roster_parse_query(User, Server, Items1, Query),
- Items = get_roster(LUser, LServer),
- SItems = lists:sort(Items),
- FItems =
- case SItems of
- [] ->
- [?CT("None")];
- _ ->
- [?XE("table",
- [?XE("thead",
- [?XE("tr",
- [?XCT("td", "Jabber ID"),
- ?XCT("td", "Nickname"),
- ?XCT("td", "Subscription"),
- ?XCT("td", "Pending"),
- ?XCT("td", "Groups")
- ])]),
- ?XE("tbody",
- lists:map(
- fun(R) ->
- Groups =
- lists:flatmap(
- fun(Group) ->
- [?C(Group), ?BR]
- end, R#roster.groups),
- Pending = ask_to_pending(R#roster.ask),
- TDJID = build_contact_jid_td(R#roster.jid),
- ?XE("tr",
- [TDJID,
- ?XAC("td", [{"class", "valign"}],
- R#roster.name),
- ?XAC("td", [{"class", "valign"}],
- atom_to_list(R#roster.subscription)),
- ?XAC("td", [{"class", "valign"}],
- atom_to_list(Pending)),
- ?XAE("td", [{"class", "valign"}], Groups),
- if
- Pending == in ->
- ?XAE("td", [{"class", "valign"}],
- [?INPUTT("submit",
- "validate" ++
- ejabberd_web_admin:term_to_id(R#roster.jid),
- "Validate")]);
- true ->
- ?X("td")
- end,
- ?XAE("td", [{"class", "valign"}],
- [?INPUTT("submit",
- "remove" ++
- ejabberd_web_admin:term_to_id(R#roster.jid),
- "Remove")])])
- end, SItems))])]
- end,
- [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++
- case Res of
- ok -> [?XREST("Submitted")];
- error -> [?XREST("Bad format")];
- nothing -> []
- end ++
- [?XAE("form", [{"action", ""}, {"method", "post"}],
- FItems ++
- [?P,
- ?INPUT("text", "newjid", ""), ?C(" "),
- ?INPUTT("submit", "addjid", "Add Jabber ID")
- ])].
-
-build_contact_jid_td(RosterJID) ->
- %% Convert {U, S, R} into {jid, U, S, R, U, S, R}:
- ContactJID = jlib:make_jid(RosterJID),
- JIDURI = case {ContactJID#jid.luser, ContactJID#jid.lserver} of
- {"", _} -> "";
- {CUser, CServer} ->
- case lists:member(CServer, ?MYHOSTS) of
- false -> "";
- true -> "/admin/server/" ++ CServer ++ "/user/" ++ CUser ++ "/"
- end
- end,
- case JIDURI of
- [] ->
- ?XAC("td", [{"class", "valign"}], jlib:jid_to_string(RosterJID));
- URI when is_list(URI) ->
- ?XAE("td", [{"class", "valign"}], [?AC(JIDURI, jlib:jid_to_string(RosterJID))])
- end.
-
-user_roster_parse_query(User, Server, Items, Query) ->
- case lists:keysearch("addjid", 1, Query) of
- {value, _} ->
- case lists:keysearch("newjid", 1, Query) of
- {value, {_, undefined}} ->
- error;
- {value, {_, SJID}} ->
- case jlib:string_to_jid(SJID) of
- JID when is_record(JID, jid) ->
- user_roster_subscribe_jid(User, Server, JID),
- ok;
- error ->
- error
- end;
- false ->
- error
- end;
- false ->
- case catch user_roster_item_parse_query(
- User, Server, Items, Query) of
- submitted ->
- ok;
- {'EXIT', _Reason} ->
- error;
- _ ->
- nothing
- end
- end.
-
-
-user_roster_subscribe_jid(User, Server, JID) ->
- out_subscription(User, Server, JID, subscribe),
- UJID = jlib:make_jid(User, Server, ""),
- ejabberd_router:route(
- UJID, JID, {xmlelement, "presence", [{"type", "subscribe"}], []}).
-
-user_roster_item_parse_query(User, Server, Items, Query) ->
- lists:foreach(
- fun(R) ->
- JID = R#roster.jid,
- case lists:keysearch(
- "validate" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
- {value, _} ->
- JID1 = jlib:make_jid(JID),
- out_subscription(
- User, Server, JID1, subscribed),
- UJID = jlib:make_jid(User, Server, ""),
- ejabberd_router:route(
- UJID, JID1, {xmlelement, "presence",
- [{"type", "subscribed"}], []}),
- throw(submitted);
- false ->
- case lists:keysearch(
- "remove" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of
- {value, _} ->
- UJID = jlib:make_jid(User, Server, ""),
- process_iq(
- UJID, UJID,
- #iq{type = set,
- sub_el = {xmlelement, "query",
- [{"xmlns", ?NS_ROSTER}],
- [{xmlelement, "item",
- [{"jid", jlib:jid_to_string(JID)},
- {"subscription", "remove"}],
- []}]}}),
- throw(submitted);
- false ->
- ok
- end
-
- end
- end, Items),
- nothing.
-
-us_to_list({User, Server}) ->
- jlib:jid_to_string({User, Server, ""}).
-
-webadmin_user(Acc, _User, _Server, Lang) ->
- Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])].
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index a8dd977eb..d5fa1e3b2 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -63,15 +63,20 @@
-record(sr_group, {group_host, opts}).
-record(sr_user, {us, group_host}).
-start(Host, _Opts) ->
- mnesia:create_table(sr_group,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, sr_group)}]),
- mnesia:create_table(sr_user,
- [{disc_copies, [node()]},
- {type, bag},
- {attributes, record_info(fields, sr_user)}]),
- mnesia:add_table_index(sr_user, group_host),
+start(Host, Opts) ->
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(sr_group,
+ [{disc_copies, [node()]},
+ {attributes, record_info(fields, sr_group)}]),
+ mnesia:create_table(sr_user,
+ [{disc_copies, [node()]},
+ {type, bag},
+ {attributes, record_info(fields, sr_user)}]),
+ mnesia:add_table_index(sr_user, group_host);
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(webadmin_menu_host, Host,
?MODULE, webadmin_menu, 70),
ejabberd_hooks:add(webadmin_page_host, Host,
@@ -181,7 +186,7 @@ get_user_roster(Items, US) ->
get_vcard_module(Server) ->
Modules = gen_mod:loaded_modules(Server),
[M || M <- Modules,
- (M == mod_vcard) or (M == mod_vcard_odbc) or (M == mod_vcard_ldap)].
+ (M == mod_vcard) or (M == mod_vcard_ldap)].
get_rosteritem_name([], _, _) ->
"";
@@ -237,15 +242,14 @@ process_item(RosterItem, Host) ->
[] ->
%% Remove pending subscription by setting it
%% unsubscribed.
- Mod = get_roster_mod(ServerFrom),
%% Remove pending out subscription
- Mod:out_subscription(UserTo, ServerTo,
+ mod_roster:out_subscription(UserTo, ServerTo,
jlib:make_jid(UserFrom, ServerFrom, ""),
unsubscribe),
%% Remove pending in subscription
- Mod:in_subscription(aaaa, UserFrom, ServerFrom,
+ mod_roster:in_subscription(aaaa, UserFrom, ServerFrom,
jlib:make_jid(UserTo, ServerTo, ""),
unsubscribe, ""),
@@ -274,8 +278,6 @@ build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
set_new_rosteritems(UserFrom, ServerFrom,
UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
- Mod = get_roster_mod(ServerFrom),
-
RIFrom = build_roster_record(UserFrom, ServerFrom,
UserTo, ServerTo, NameTo, GroupsFrom),
set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
@@ -287,20 +289,20 @@ set_new_rosteritems(UserFrom, ServerFrom,
set_item(UserTo, ServerTo, "", RITo),
%% From requests
- Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
- Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
+ mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
+ mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
%% To accepts
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
- Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
+ mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
+ mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
%% To requests
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
- Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
+ mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
+ mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
%% From accepts
- Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
- Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
+ mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
+ mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
RIFrom.
@@ -361,15 +363,13 @@ in_subscription(Acc, User, Server, JID, Type, _Reason) ->
process_subscription(in, User, Server, JID, Type, Acc).
out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
- Mod = get_roster_mod(ServerFrom),
-
%% Remove pending out subscription
#jid{luser = UserTo, lserver = ServerTo} = JIDTo,
JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
+ mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
%% Remove pending in subscription
- Mod:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
+ mod_roster:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
out_subscription(User, Server, JID, Type) ->
@@ -401,32 +401,70 @@ process_subscription(Direction, User, Server, JID, _Type, Acc) ->
end.
list_groups(Host) ->
+ list_groups(Host, gen_mod:db_type(Host, ?MODULE)).
+
+list_groups(Host, mnesia) ->
mnesia:dirty_select(
sr_group,
[{#sr_group{group_host = {'$1', '$2'},
_ = '_'},
[{'==', '$2', Host}],
- ['$1']}]).
+ ['$1']}]);
+list_groups(Host, odbc) ->
+ case ejabberd_odbc:sql_query(
+ Host, ["select name from sr_group;"]) of
+ {selected, ["name"], Rs} ->
+ [G || {G} <- Rs];
+ _ ->
+ []
+ end.
groups_with_opts(Host) ->
+ groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)).
+
+groups_with_opts(Host, mnesia) ->
Gs = mnesia:dirty_select(
sr_group,
[{#sr_group{group_host={'$1', Host}, opts='$2', _='_'},
[],
[['$1','$2']] }]),
- lists:map(fun([G,O]) -> {G, O} end, Gs).
+ lists:map(fun([G,O]) -> {G, O} end, Gs);
+groups_with_opts(Host, odbc) ->
+ case ejabberd_odbc:sql_query(
+ Host, ["select name, opts from sr_group;"]) of
+ {selected, ["name", "opts"], Rs} ->
+ [{G, ejabberd_odbc:decode_term(Opts)} || {G, Opts} <- Rs];
+ _ ->
+ []
+ end.
create_group(Host, Group) ->
create_group(Host, Group, []).
create_group(Host, Group, Opts) ->
+ create_group(Host, Group, Opts, gen_mod:db_type(Host, ?MODULE)).
+
+create_group(Host, Group, Opts, mnesia) ->
R = #sr_group{group_host = {Group, Host}, opts = Opts},
F = fun() ->
mnesia:write(R)
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+create_group(Host, Group, Opts, odbc) ->
+ SGroup = ejabberd_odbc:escape(Group),
+ SOpts = ejabberd_odbc:encode_term(Opts),
+ F = fun() ->
+ odbc_queries:update_t("sr_group",
+ ["name", "opts"],
+ [SGroup, SOpts],
+ ["name='", SGroup, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(Host, F).
delete_group(Host, Group) ->
+ delete_group(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+delete_group(Host, Group, mnesia) ->
GroupHost = {Group, Host},
F = fun() ->
%% Delete the group ...
@@ -437,53 +475,102 @@ delete_group(Host, Group) ->
mnesia:delete_object(UserEntry)
end, Users)
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+delete_group(Host, Group, odbc) ->
+ SGroup = ejabberd_odbc:escape(Group),
+ F = fun() ->
+ ejabberd_odbc:sql_query_t(
+ ["delete from sr_group where name='", SGroup, "';"]),
+ ejabberd_odbc:sql_query_t(
+ ["delete from sr_user where grp='", SGroup, "';"])
+ end,
+ ejabberd_odbc:sql_transaction(Host, F).
get_group_opts(Host, Group) ->
+ get_group_opts(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+get_group_opts(Host, Group, mnesia) ->
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
[#sr_group{opts = Opts}] ->
Opts;
_ ->
error
+ end;
+get_group_opts(Host, Group, odbc) ->
+ SGroup = ejabberd_odbc:escape(Group),
+ case catch ejabberd_odbc:sql_query(
+ Host, ["select opts from sr_group "
+ "where name='", SGroup, "';"]) of
+ {selected, ["opts"], [{SOpts}]} ->
+ ejabberd_odbc:decode_term(SOpts);
+ _ ->
+ error
end.
set_group_opts(Host, Group, Opts) ->
+ set_group_opts(Host, Group, Opts, gen_mod:db_type(Host, ?MODULE)).
+
+set_group_opts(Host, Group, Opts, mnesia) ->
R = #sr_group{group_host = {Group, Host}, opts = Opts},
F = fun() ->
mnesia:write(R)
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+set_group_opts(Host, Group, Opts, odbc) ->
+ SGroup = ejabberd_odbc:escape(Group),
+ SOpts = ejabberd_odbc:encode_term(Opts),
+ F = fun() ->
+ odbc_queries:update_t("sr_group",
+ ["name", "opts"],
+ [SGroup, SOpts],
+ ["name='", SGroup, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(Host, F).
get_user_groups(US) ->
Host = element(2, US),
+ DBType = gen_mod:db_type(Host, ?MODULE),
+ get_user_groups(US, Host, DBType) ++ get_special_users_groups(Host).
+
+get_user_groups(US, Host, mnesia) ->
case catch mnesia:dirty_read(sr_user, US) of
Rs when is_list(Rs) ->
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
_ ->
[]
- end ++ get_special_users_groups(Host).
+ end;
+get_user_groups(US, Host, odbc) ->
+ SJID = make_jid_s(US),
+ case catch ejabberd_odbc:sql_query(
+ Host, ["select grp from sr_user "
+ "where jid='", SJID, "';"]) of
+ {selected, ["grp"], Rs} ->
+ [G || {G} <- Rs];
+ _ ->
+ []
+ end.
is_group_enabled(Host1, Group1) ->
{Host, Group} = split_grouphost(Host1, Group1),
- case catch mnesia:dirty_read(sr_group, {Group, Host}) of
- [#sr_group{opts = Opts}] ->
- not lists:member(disabled, Opts);
- _ ->
- false
+ case get_group_opts(Host, Group) of
+ error ->
+ false;
+ Opts ->
+ not lists:member(disabled, Opts)
end.
%% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
get_group_opt(Host, Group, Opt, Default) ->
- case catch mnesia:dirty_read(sr_group, {Group, Host}) of
- [#sr_group{opts = Opts}] ->
+ case get_group_opts(Host, Group) of
+ error ->
+ Default;
+ Opts ->
case lists:keysearch(Opt, 1, Opts) of
{value, {_, Val}} ->
Val;
false ->
Default
- end;
- _ ->
- Default
+ end
end.
get_online_users(Host) ->
@@ -522,6 +609,9 @@ get_group_users(Host, Group, GroupOpts) ->
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
get_group_explicit_users(Host, Group) ->
+ get_group_explicit_users(Host, Group, gen_mod:db_type(Host, ?MODULE)).
+
+get_group_explicit_users(Host, Group, mnesia) ->
Read = (catch mnesia:dirty_index_read(
sr_user,
{Group, Host},
@@ -531,6 +621,21 @@ get_group_explicit_users(Host, Group) ->
[R#sr_user.us || R <- Rs];
_ ->
[]
+ end;
+get_group_explicit_users(Host, Group, odbc) ->
+ SGroup = ejabberd_odbc:escape(Group),
+ case catch ejabberd_odbc:sql_query(
+ Host, ["select jid from sr_user "
+ "where grp='", SGroup, "';"]) of
+ {selected, ["jid"], Rs} ->
+ lists:map(
+ fun({JID}) ->
+ {U, S, _} = jlib:jid_tolower(
+ jlib:string_to_jid(JID)),
+ {U, S}
+ end, Rs);
+ _ ->
+ []
end.
get_group_name(Host1, Group1) ->
@@ -581,15 +686,30 @@ get_special_displayed_groups(GroupsOpts) ->
%% for the list of groups of that server that user is member
%% get the list of groups displayed
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
- Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
- Rs when is_list(Rs) ->
- [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
- #sr_user{group_host = {Group, H}} <- Rs, H == LServer];
- _ ->
- []
- end,
+ Groups = get_user_displayed_groups(LUser, LServer, GroupsOpts,
+ gen_mod:db_type(LServer, ?MODULE)),
displayed_groups(GroupsOpts, Groups).
+get_user_displayed_groups(LUser, LServer, GroupsOpts, mnesia) ->
+ case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
+ Rs when is_list(Rs) ->
+ [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
+ #sr_user{group_host = {Group, H}} <- Rs, H == LServer];
+ _ ->
+ []
+ end;
+get_user_displayed_groups(LUser, LServer, GroupsOpts, odbc) ->
+ SJID = make_jid_s(LUser, LServer),
+ case catch ejabberd_odbc:sql_query(
+ LServer, ["select grp from sr_user "
+ "where jid='", SJID, "';"]) of
+ {selected, ["grp"], Rs} ->
+ [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
+ {Group} <- Rs];
+ _ ->
+ []
+ end.
+
%% @doc Get the list of groups that are displayed to this user
get_user_displayed_groups(US) ->
Host = element(2, US),
@@ -607,13 +727,26 @@ get_user_displayed_groups(US) ->
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
is_user_in_group(US, Group, Host) ->
+ is_user_in_group(US, Group, Host, gen_mod:db_type(Host, ?MODULE)).
+
+is_user_in_group(US, Group, Host, mnesia) ->
case catch mnesia:dirty_match_object(
#sr_user{us=US, group_host={Group, Host}}) of
[] -> lists:member(US, get_group_users(Host, Group));
_ -> true
+ end;
+is_user_in_group(US, Group, Host, odbc) ->
+ SJID = make_jid_s(US),
+ SGroup = ejabberd_odbc:escape(Group),
+ case catch ejabberd_odbc:sql_query(
+ Host, ["select * from sr_user where "
+ "jid='", SJID, "' and grp='", SGroup, "';"]) of
+ {selected, _, []} ->
+ lists:member(US, get_group_users(Host, Group));
+ _ ->
+ true
end.
-
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
add_user_to_group(Host, US, Group) ->
{LUser, LServer} = US,
@@ -634,13 +767,27 @@ add_user_to_group(Host, US, Group) ->
push_user_to_displayed(LUser, LServer, Group, Host, both),
%% Push members of groups that are displayed to this group
push_displayed_to_user(LUser, LServer, Group, Host, both),
- R = #sr_user{us = US, group_host = {Group, Host}},
- F = fun() ->
- mnesia:write(R)
- end,
- mnesia:transaction(F)
+ add_user_to_group(Host, US, Group, gen_mod:db_type(Host, ?MODULE))
end.
+add_user_to_group(Host, US, Group, mnesia) ->
+ R = #sr_user{us = US, group_host = {Group, Host}},
+ F = fun() ->
+ mnesia:write(R)
+ end,
+ mnesia:transaction(F);
+add_user_to_group(Host, US, Group, odbc) ->
+ SJID = make_jid_s(US),
+ SGroup = ejabberd_odbc:escape(Group),
+ F = fun() ->
+ odbc_queries:update_t(
+ "sr_user",
+ ["jid", "grp"],
+ [SJID, SGroup],
+ ["jid='", SJID, "' and grp='", SGroup, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(Host, F).
+
push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
@@ -648,7 +795,6 @@ push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
remove_user_from_group(Host, US, Group) ->
- GroupHost = {Group, Host},
{LUser, LServer} = US,
case ejabberd_regexp:run(LUser, "^@.+@$") of
match ->
@@ -662,11 +808,8 @@ remove_user_from_group(Host, US, Group) ->
end,
?MODULE:set_group_opts(Host, Group, NewGroupOpts);
nomatch ->
- R = #sr_user{us = US, group_host = GroupHost},
- F = fun() ->
- mnesia:delete_object(R)
- end,
- Result = mnesia:transaction(F),
+ Result = remove_user_from_group(Host, US, Group,
+ gen_mod:db_type(Host, ?MODULE)),
%% Push removal of the old user to members of groups where the group that this user was members was displayed
push_user_to_displayed(LUser, LServer, Group, Host, remove),
%% Push removal of members of groups that where displayed to the group which this user has left
@@ -674,6 +817,22 @@ remove_user_from_group(Host, US, Group) ->
Result
end.
+remove_user_from_group(Host, US, Group, mnesia) ->
+ R = #sr_user{us = US, group_host = {Group, Host}},
+ F = fun() ->
+ mnesia:delete_object(R)
+ end,
+ mnesia:transaction(F);
+remove_user_from_group(Host, US, Group, odbc) ->
+ SJID = make_jid_s(US),
+ SGroup = ejabberd_odbc:escape(Group),
+ F = fun() ->
+ ejabberd_odbc:sql_query_t(
+ ["delete from sr_user where jid='",
+ SJID, "' and grp='", SGroup, "';"]),
+ ok
+ end,
+ ejabberd_odbc:sql_transaction(Host, F).
push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
GroupsOpts = groups_with_opts(LServer),
@@ -1099,14 +1258,6 @@ shared_roster_group_parse_query(Host, Group, Query) ->
nothing
end.
-%% Get the roster module for Server.
-get_roster_mod(Server) ->
- case lists:member(mod_roster_odbc,
- gen_mod:loaded_modules(Server)) of
- true -> mod_roster_odbc;
- false -> mod_roster
- end.
-
get_opt(Opts, Opt, Default) ->
case lists:keysearch(Opt, 1, Opts) of
{value, {_, Val}} ->
@@ -1125,3 +1276,12 @@ split_grouphost(Host, Group) ->
[_] ->
{Host, Group}
end.
+
+make_jid_s(U, S) ->
+ ejabberd_odbc:escape(
+ jlib:jid_to_string(
+ jlib:jid_tolower(
+ jlib:make_jid(U, S, "")))).
+
+make_jid_s({U, S}) ->
+ make_jid_s(U, S).
diff --git a/src/mod_shared_roster_odbc.erl b/src/mod_shared_roster_odbc.erl
deleted file mode 100644
index 570e50947..000000000
--- a/src/mod_shared_roster_odbc.erl
+++ /dev/null
@@ -1,1165 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_shared_roster_odbc.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Shared roster management
-%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_shared_roster_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, stop/1,
- item_to_xml/1,
- webadmin_menu/3, webadmin_page/3,
- get_user_roster/2,
- get_subscription_lists/3,
- get_jid_info/4,
- process_item/2,
- in_subscription/6,
- out_subscription/4,
- user_available/1,
- unset_presence/4,
- register_user/2,
- remove_user/2,
- list_groups/1,
- create_group/2,
- create_group/3,
- delete_group/2,
- get_group_opts/2,
- set_group_opts/3,
- get_group_users/2,
- get_group_explicit_users/2,
- is_user_in_group/3,
- add_user_to_group/3,
- remove_user_from_group/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
--include("mod_roster.hrl").
--include("web/ejabberd_http.hrl").
--include("web/ejabberd_web_admin.hrl").
-
-start(Host, _Opts) ->
- ejabberd_hooks:add(webadmin_menu_host, Host,
- ?MODULE, webadmin_menu, 70),
- ejabberd_hooks:add(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:add(roster_get, Host,
- ?MODULE, get_user_roster, 70),
- ejabberd_hooks:add(roster_in_subscription, Host,
- ?MODULE, in_subscription, 30),
- ejabberd_hooks:add(roster_out_subscription, Host,
- ?MODULE, out_subscription, 30),
- ejabberd_hooks:add(roster_get_subscription_lists, Host,
- ?MODULE, get_subscription_lists, 70),
- ejabberd_hooks:add(roster_get_jid_info, Host,
- ?MODULE, get_jid_info, 70),
- ejabberd_hooks:add(roster_process_item, Host,
- ?MODULE, process_item, 50),
- ejabberd_hooks:add(user_available_hook, Host,
- ?MODULE, user_available, 50),
- ejabberd_hooks:add(unset_presence_hook, Host,
- ?MODULE, unset_presence, 50),
- ejabberd_hooks:add(register_user, Host,
- ?MODULE, register_user, 50),
- ejabberd_hooks:add(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50).
-%%ejabberd_hooks:add(remove_user, Host,
-%% ?MODULE, remove_user, 50),
-
-stop(Host) ->
- ejabberd_hooks:delete(webadmin_menu_host, Host,
- ?MODULE, webadmin_menu, 70),
- ejabberd_hooks:delete(webadmin_page_host, Host,
- ?MODULE, webadmin_page, 50),
- ejabberd_hooks:delete(roster_get, Host,
- ?MODULE, get_user_roster, 70),
- ejabberd_hooks:delete(roster_in_subscription, Host,
- ?MODULE, in_subscription, 30),
- ejabberd_hooks:delete(roster_out_subscription, Host,
- ?MODULE, out_subscription, 30),
- ejabberd_hooks:delete(roster_get_subscription_lists, Host,
- ?MODULE, get_subscription_lists, 70),
- ejabberd_hooks:delete(roster_get_jid_info, Host,
- ?MODULE, get_jid_info, 70),
- ejabberd_hooks:delete(roster_process_item, Host,
- ?MODULE, process_item, 50),
- ejabberd_hooks:delete(user_available_hook, Host,
- ?MODULE, user_available, 50),
- ejabberd_hooks:delete(unset_presence_hook, Host,
- ?MODULE, unset_presence, 50),
- ejabberd_hooks:delete(register_user, Host,
- ?MODULE, register_user, 50),
- ejabberd_hooks:delete(anonymous_purge_hook, Host,
- ?MODULE, remove_user, 50),
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50).
-%%ejabberd_hooks:delete(remove_user, Host,
-%% ?MODULE, remove_user, 50),
-
-
-get_user_roster(Items, US) ->
- {U, S} = US,
- DisplayedGroups = get_user_displayed_groups(US),
- %% Get shared roster users in all groups and remove self:
- SRUsers =
- lists:foldl(
- fun(Group, Acc1) ->
- GroupName = get_group_name(S, Group),
- lists:foldl(
- fun(User, Acc2) ->
- if User == US -> Acc2;
- true -> dict:append(User,
- GroupName,
- Acc2)
- end
- end, Acc1, get_group_users(S, Group))
- end, dict:new(), DisplayedGroups),
-
- %% If partially subscribed users are also in shared roster, show them as
- %% totally subscribed:
- {NewItems1, SRUsersRest} =
- lists:mapfoldl(
- fun(Item, SRUsers1) ->
- {_, _, {U1, S1, _}} = Item#roster.usj,
- US1 = {U1, S1},
- case dict:find(US1, SRUsers1) of
- {ok, _GroupNames} ->
- {Item#roster{subscription = both, ask = none},
- dict:erase(US1, SRUsers1)};
- error ->
- {Item, SRUsers1}
- end
- end, SRUsers, Items),
-
- %% Export items in roster format:
- ModVcard = get_vcard_module(S),
- SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
- us = US,
- jid = {U1, S1, ""},
- name = get_rosteritem_name(ModVcard, U1, S1),
- subscription = both,
- ask = none,
- groups = GroupNames} ||
- {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
- SRItems ++ NewItems1.
-
-get_vcard_module(Server) ->
- Modules = gen_mod:loaded_modules(Server),
- [M || M <- Modules,
- (M == mod_vcard) or (M == mod_vcard_odbc) or (M == mod_vcard_ldap)].
-
-get_rosteritem_name([], _, _) ->
- "";
-get_rosteritem_name([ModVcard], U, S) ->
- From = jlib:make_jid("", S, ?MODULE),
- To = jlib:make_jid(U, S, ""),
- IQ = {iq,"",get,"vcard-temp","",
- {xmlelement,"vCard",[{"xmlns","vcard-temp"}],[]}},
- IQ_Vcard = ModVcard:process_sm_iq(From, To, IQ),
- try get_rosteritem_name_vcard(IQ_Vcard#iq.sub_el)
- catch E1:E2 ->
- ?ERROR_MSG("Error ~p found when trying to get the vCard of ~s@~s "
- "in ~p:~n ~p", [E1, U, S, ModVcard, E2]),
- ""
- end.
-
-get_rosteritem_name_vcard([]) ->
- "";
-get_rosteritem_name_vcard([Vcard]) ->
- case xml:get_path_s(Vcard, [{elem, "NICKNAME"}, cdata]) of
- "" -> xml:get_path_s(Vcard, [{elem, "FN"}, cdata]);
- Nickname -> Nickname
- end.
-
-%% This function rewrites the roster entries when moving or renaming
-%% them in the user contact list.
-process_item(RosterItem, Host) ->
- USFrom = {UserFrom, ServerFrom} = RosterItem#roster.us,
- {UserTo, ServerTo, ResourceTo} = RosterItem#roster.jid,
- NameTo = RosterItem#roster.name,
- USTo = {UserTo, ServerTo},
- DisplayedGroups = get_user_displayed_groups(USFrom),
- CommonGroups = lists:filter(fun(Group) ->
- is_user_in_group(USTo, Group, Host)
- end, DisplayedGroups),
- case CommonGroups of
- [] -> RosterItem;
- %% Roster item cannot be removed: We simply reset the original groups:
- _ when RosterItem#roster.subscription == remove ->
- GroupNames = lists:map(fun(Group) ->
- get_group_name(Host, Group)
- end, CommonGroups),
- RosterItem#roster{subscription = both, ask = none,
- groups=[GroupNames]};
- %% Both users have at least a common shared group,
- %% So each user can see the other
- _ ->
- %% Check if the list of groups of the new roster item
- %% include at least a new one
- case lists:subtract(RosterItem#roster.groups, CommonGroups) of
- %% If it doesn't, then remove this user from any
- %% existing roster groups.
- [] ->
- %% Remove pending subscription by setting it
- %% unsubscribed.
- Mod = get_roster_mod(ServerFrom),
-
- %% Remove pending out subscription
- Mod:out_subscription(UserTo, ServerTo,
- jlib:make_jid(UserFrom, ServerFrom, ""),
- unsubscribe),
-
- %% Remove pending in subscription
- Mod:in_subscription(aaaa, UserFrom, ServerFrom,
- jlib:make_jid(UserTo, ServerTo, ""),
- unsubscribe, ""),
-
- %% But we're still subscribed, so respond as such.
- RosterItem#roster{subscription = both, ask = none};
- %% If so, it means the user wants to add that contact
- %% to his personal roster
- PersonalGroups ->
- %% Store roster items in From and To rosters
- set_new_rosteritems(UserFrom, ServerFrom,
- UserTo, ServerTo, ResourceTo, NameTo,
- PersonalGroups)
- end
- end.
-
-build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
- USR2 = {User2, Server2, ""},
- #roster{usj = {User1, Server1, USR2},
- us = {User1, Server1},
- jid = USR2,
- name = Name2,
- subscription = both,
- ask = none,
- groups = Groups
- }.
-
-set_new_rosteritems(UserFrom, ServerFrom,
- UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
- Mod = get_roster_mod(ServerFrom),
-
- RIFrom = build_roster_record(UserFrom, ServerFrom,
- UserTo, ServerTo, NameTo, GroupsFrom),
- set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
- JIDTo = jlib:make_jid(UserTo, ServerTo, ""),
-
- JIDFrom = jlib:make_jid(UserFrom, ServerFrom, ""),
- RITo = build_roster_record(UserTo, ServerTo,
- UserFrom, ServerFrom, UserFrom,[]),
- set_item(UserTo, ServerTo, "", RITo),
-
- %% From requests
- Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
- Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
-
- %% To accepts
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
- Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
-
- %% To requests
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
- Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
-
- %% From accepts
- Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
- Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
-
- RIFrom.
-
-set_item(User, Server, Resource, Item) ->
- ResIQ = #iq{type = set, xmlns = ?NS_ROSTER,
- id = "push" ++ randoms:get_string(),
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_ROSTER}],
- [mod_roster:item_to_xml(Item)]}]},
- ejabberd_router:route(
- jlib:make_jid(User, Server, Resource),
- jlib:make_jid("", Server, ""),
- jlib:iq_to_xml(ResIQ)).
-
-
-get_subscription_lists({F, T}, User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- US = {LUser, LServer},
- DisplayedGroups = get_user_displayed_groups(US),
- SRUsers =
- lists:usort(
- lists:flatmap(
- fun(Group) ->
- get_group_users(LServer, Group)
- end, DisplayedGroups)),
- SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
- {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
-
-get_jid_info({Subscription, Groups}, User, Server, JID) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- US = {LUser, LServer},
- {U1, S1, _} = jlib:jid_tolower(JID),
- US1 = {U1, S1},
- DisplayedGroups = get_user_displayed_groups(US),
- SRUsers =
- lists:foldl(
- fun(Group, Acc1) ->
- lists:foldl(
- fun(User1, Acc2) ->
- dict:append(
- User1, get_group_name(LServer, Group), Acc2)
- end, Acc1, get_group_users(LServer, Group))
- end, dict:new(), DisplayedGroups),
- case dict:find(US1, SRUsers) of
- {ok, GroupNames} ->
- NewGroups = if
- Groups == [] -> GroupNames;
- true -> Groups
- end,
- {both, NewGroups};
- error ->
- {Subscription, Groups}
- end.
-
-in_subscription(Acc, User, Server, JID, Type, _Reason) ->
- process_subscription(in, User, Server, JID, Type, Acc).
-
-out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
- Mod = get_roster_mod(ServerFrom),
-
- %% Remove pending out subscription
- #jid{luser = UserTo, lserver = ServerTo} = JIDTo,
- JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
- Mod:out_subscription(UserTo, ServerTo, JIDFrom, unsubscribe),
-
- %% Remove pending in subscription
- Mod:in_subscription(aaaa, UserFrom, ServerFrom, JIDTo, unsubscribe, ""),
-
- process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
-out_subscription(User, Server, JID, Type) ->
- process_subscription(out, User, Server, JID, Type, false).
-
-process_subscription(Direction, User, Server, JID, _Type, Acc) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- US = {LUser, LServer},
- {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
- US1 = {U1, S1},
- DisplayedGroups = get_user_displayed_groups(US),
- SRUsers =
- lists:usort(
- lists:flatmap(
- fun(Group) ->
- get_group_users(LServer, Group)
- end, DisplayedGroups)),
- case lists:member(US1, SRUsers) of
- true ->
- case Direction of
- in ->
- {stop, false};
- out ->
- stop
- end;
- false ->
- Acc
- end.
-
-list_groups(Host) ->
- case ejabberd_odbc:sql_query(
- Host, ["select name from sr_group;"]) of
- {selected, ["name"], Rs} ->
- [G || {G} <- Rs];
- _ ->
- []
- end.
-
-groups_with_opts(Host) ->
- case ejabberd_odbc:sql_query(
- Host, ["select name, opts from sr_group;"]) of
- {selected, ["name", "opts"], Rs} ->
- [{G, ejabberd_odbc:decode_term(Opts)} || {G, Opts} <- Rs];
- _ ->
- []
- end.
-
-create_group(Host, Group) ->
- create_group(Host, Group, []).
-
-create_group(Host, Group, Opts) ->
- SGroup = ejabberd_odbc:escape(Group),
- SOpts = ejabberd_odbc:encode_term(Opts),
- F = fun() ->
- odbc_queries:update_t("sr_group",
- ["name", "opts"],
- [SGroup, SOpts],
- ["name='", SGroup, "'"])
- end,
- ejabberd_odbc:sql_transaction(Host, F).
-
-delete_group(Host, Group) ->
- SGroup = ejabberd_odbc:escape(Group),
- F = fun() ->
- ejabberd_odbc:sql_query_t(
- ["delete from sr_group where name='", SGroup, "';"]),
- ejabberd_odbc:sql_query_t(
- ["delete from sr_user where grp='", SGroup, "';"])
- end,
- ejabberd_odbc:sql_transaction(Host, F).
-
-get_group_opts(Host, Group) ->
- SGroup = ejabberd_odbc:escape(Group),
- case catch ejabberd_odbc:sql_query(
- Host, ["select opts from sr_group "
- "where name='", SGroup, "';"]) of
- {selected, ["opts"], [{SOpts}]} ->
- ejabberd_odbc:decode_term(SOpts);
- _ ->
- error
- end.
-
-set_group_opts(Host, Group, Opts) ->
- SGroup = ejabberd_odbc:escape(Group),
- SOpts = ejabberd_odbc:encode_term(Opts),
- F = fun() ->
- odbc_queries:update_t("sr_group",
- ["name", "opts"],
- [SGroup, SOpts],
- ["name='", SGroup, "'"])
- end,
- ejabberd_odbc:sql_transaction(Host, F).
-
-get_user_groups(US) ->
- Host = element(2, US),
- SJID = make_jid_s(US),
- case catch ejabberd_odbc:sql_query(
- Host, ["select grp from sr_user "
- "where jid='", SJID, "';"]) of
- {selected, ["grp"], Rs} ->
- [G || {G} <- Rs];
- _ ->
- []
- end ++ get_special_users_groups(Host).
-
-is_group_enabled(Host1, Group1) ->
- {Host, Group} = split_grouphost(Host1, Group1),
- SGroup = ejabberd_odbc:escape(Group),
- case catch ejabberd_odbc:sql_query(
- Host, ["select opts from sr_group "
- "where name='", SGroup, "';"]) of
- {selected, ["opts"], [{SOpts}]} ->
- Opts = ejabberd_odbc:decode_term(SOpts),
- not lists:member(disabled, Opts);
- _ ->
- false
- end.
-
-%% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
-get_group_opt(Host, Group, Opt, Default) ->
- SGroup = ejabberd_odbc:escape(Group),
- case catch ejabberd_odbc:sql_query(
- Host, ["select opts from sr_group "
- "where name='", SGroup, "';"]) of
- {selected, ["opts"], [{SOpts}]} ->
- Opts = ejabberd_odbc:decode_term(SOpts),
- case lists:keysearch(Opt, 1, Opts) of
- {value, {_, Val}} ->
- Val;
- false ->
- Default
- end;
- _ ->
- Default
- end.
-
-get_online_users(Host) ->
- lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]).
-
-get_group_users(Host1, Group1) ->
- {Host, Group} = split_grouphost(Host1, Group1),
- case get_group_opt(Host, Group, all_users, false) of
- true ->
- ejabberd_auth:get_vh_registered_users(Host);
- false ->
- []
- end ++
- case get_group_opt(Host, Group, online_users, false) of
- true ->
- get_online_users(Host);
- false ->
- []
- end ++
- get_group_explicit_users(Host, Group).
-
-get_group_users(Host, Group, GroupOpts) ->
- case proplists:get_value(all_users, GroupOpts, false) of
- true ->
- ejabberd_auth:get_vh_registered_users(Host);
- false ->
- []
- end ++
- case proplists:get_value(online_users, GroupOpts, false) of
- true ->
- get_online_users(Host);
- false ->
- []
- end ++
- get_group_explicit_users(Host, Group).
-
-%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
-get_group_explicit_users(Host, Group) ->
- SGroup = ejabberd_odbc:escape(Group),
- case catch ejabberd_odbc:sql_query(
- Host, ["select jid from sr_user "
- "where grp='", SGroup, "';"]) of
- {selected, ["jid"], Rs} ->
- lists:map(
- fun({JID}) ->
- {U, S, _} = jlib:jid_tolower(
- jlib:string_to_jid(JID)),
- {U, S}
- end, Rs);
- _ ->
- []
- end.
-
-get_group_name(Host1, Group1) ->
- {Host, Group} = split_grouphost(Host1, Group1),
- get_group_opt(Host, Group, name, Group).
-
-%% Get list of names of groups that have @all@/@online@/etc in the memberlist
-get_special_users_groups(Host) ->
- lists:filter(
- fun(Group) ->
- get_group_opt(Host, Group, all_users, false)
- orelse get_group_opt(Host, Group, online_users, false)
- end,
- list_groups(Host)).
-
-%% Get list of names of groups that have @online@ in the memberlist
-get_special_users_groups_online(Host) ->
- lists:filter(
- fun(Group) ->
- get_group_opt(Host, Group, online_users, false)
- end,
- list_groups(Host)).
-
-%% Given two lists of groupnames and their options,
-%% return the list of displayed groups to the second list
-displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
- DisplayedGroups =
- lists:usort(
- lists:flatmap(
- fun({_Group, Opts}) ->
- [G || G <- proplists:get_value(displayed_groups, Opts, []),
- not lists:member(disabled, Opts)]
- end, SelectedGroupsOpts)),
- [G || G <- DisplayedGroups,
- not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
-
-%% Given a list of group names with options,
-%% for those that have @all@ in memberlist,
-%% get the list of groups displayed
-get_special_displayed_groups(GroupsOpts) ->
- Groups = lists:filter(
- fun({_Group, Opts}) ->
- proplists:get_value(all_users, Opts, false)
- end, GroupsOpts),
- displayed_groups(GroupsOpts, Groups).
-
-%% Given a username and server, and a list of group names with options,
-%% for the list of groups of that server that user is member
-%% get the list of groups displayed
-get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
- SJID = make_jid_s(LUser, LServer),
- Groups = case catch ejabberd_odbc:sql_query(
- LServer, ["select grp from sr_user "
- "where jid='", SJID, "';"]) of
- {selected, ["grp"], Rs} ->
- [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
- {Group} <- Rs];
- _ ->
- []
- end,
- displayed_groups(GroupsOpts, Groups).
-
-%% @doc Get the list of groups that are displayed to this user
-get_user_displayed_groups(US) ->
- Host = element(2, US),
- DisplayedGroups1 =
- lists:usort(
- lists:flatmap(
- fun(Group) ->
- case is_group_enabled(Host, Group) of
- true ->
- get_group_opt(Host, Group, displayed_groups, []);
- false ->
- []
- end
- end, get_user_groups(US))),
- [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
-
-is_user_in_group(US, Group, Host) ->
- SJID = make_jid_s(US),
- SGroup = ejabberd_odbc:escape(Group),
- case catch ejabberd_odbc:sql_query(
- Host, ["select * from sr_user where "
- "jid='", SJID, "' and grp='", SGroup, "';"]) of
- {selected, _, []} ->
- lists:member(US, get_group_users(Host, Group));
- _ ->
- true
- end.
-
-%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
-add_user_to_group(Host, US, Group) ->
- {LUser, LServer} = US,
- case ejabberd_regexp:run(LUser, "^@.+@$") of
- match ->
- GroupOpts = ?MODULE:get_group_opts(Host, Group),
- MoreGroupOpts =
- case LUser of
- "@all@" -> [{all_users, true}];
- "@online@" -> [{online_users, true}];
- _ -> []
- end,
- ?MODULE:set_group_opts(
- Host, Group,
- GroupOpts ++ MoreGroupOpts);
- nomatch ->
- %% Push this new user to members of groups where this group is displayed
- push_user_to_displayed(LUser, LServer, Group, Host, both),
- %% Push members of groups that are displayed to this group
- push_displayed_to_user(LUser, LServer, Group, Host, both),
- SJID = make_jid_s(US),
- SGroup = ejabberd_odbc:escape(Group),
- F = fun() ->
- odbc_queries:update_t(
- "sr_user",
- ["jid", "grp"],
- [SJID, SGroup],
- ["jid='", SJID, "' and grp='", SGroup, "'"])
- end,
- ejabberd_odbc:sql_transaction(Host, F)
- end.
-
-push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
- GroupsOpts = groups_with_opts(LServer),
- GroupOpts = proplists:get_value(Group, GroupsOpts, []),
- DisplayedGroups = proplists:get_value(displayed_groups, GroupOpts, []),
- [push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
-
-remove_user_from_group(Host, US, Group) ->
- {LUser, LServer} = US,
- case ejabberd_regexp:run(LUser, "^@.+@$") of
- match ->
- GroupOpts = ?MODULE:get_group_opts(Host, Group),
- NewGroupOpts =
- case LUser of
- "@all@" ->
- lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts);
- "@online@" ->
- lists:filter(fun(X) -> X/={online_users,true} end, GroupOpts)
- end,
- ?MODULE:set_group_opts(Host, Group, NewGroupOpts);
- nomatch ->
- SJID = make_jid_s(US),
- SGroup = ejabberd_odbc:escape(Group),
- F = fun() ->
- ejabberd_odbc:sql_query_t(
- ["delete from sr_user where jid='",
- SJID, "' and grp='", SGroup, "';"]),
- ok
- end,
- Result = ejabberd_odbc:sql_transaction(Host, F),
- %% Push removal of the old user to members of groups where the group that this user was members was displayed
- push_user_to_displayed(LUser, LServer, Group, Host, remove),
- %% Push removal of members of groups that where displayed to the group which this user has left
- push_displayed_to_user(LUser, LServer, Group, Host, remove),
- Result
- end.
-
-
-push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
- GroupsOpts = groups_with_opts(LServer),
- GroupOpts = proplists:get_value(Group, GroupsOpts, []),
- GroupName = proplists:get_value(name, GroupOpts, Group),
- Members = get_group_users(Host, Group),
- lists:foreach(
- fun({U, S}) ->
- push_roster_item(LUser, LServer, U, S, GroupName, Subscription)
- end, Members).
-
-register_user(User, Server) ->
- %% Get list of groups where this user is member
- Groups = get_user_groups({User, Server}),
- %% Push this user to members of groups where is displayed a group which this user is member
- [push_user_to_displayed(User, Server, Group, Server, both) || Group <- Groups].
-
-remove_user(User, Server) ->
- push_user_to_members(User, Server, remove).
-
-push_user_to_members(User, Server, Subscription) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- GroupsOpts = groups_with_opts(LServer),
- SpecialGroups = get_special_displayed_groups(GroupsOpts),
- UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts),
- lists:foreach(
- fun(Group) ->
- remove_user_from_group(LServer, {LUser, LServer}, Group),
- GroupOpts = proplists:get_value(Group, GroupsOpts, []),
- GroupName = proplists:get_value(name, GroupOpts, Group),
- lists:foreach(
- fun({U, S}) ->
- push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
- end, get_group_users(LServer, Group, GroupOpts))
- end, lists:usort(SpecialGroups++UserGroups)).
-
-push_user_to_displayed(LUser, LServer, Group, Host, Subscription) ->
- GroupsOpts = groups_with_opts(Host),
- GroupOpts = proplists:get_value(Group, GroupsOpts, []),
- GroupName = proplists:get_value(name, GroupOpts, Group),
- DisplayedToGroupsOpts = displayed_to_groups(Group, Host),
- [push_user_to_group(LUser, LServer, GroupD, Host, GroupName, Subscription) || {GroupD, _Opts} <- DisplayedToGroupsOpts].
-
-push_user_to_group(LUser, LServer, Group, Host, GroupName, Subscription) ->
- lists:foreach(
- fun({U, S}) when (U == LUser) and (S == LServer) -> ok;
- ({U, S}) ->
- push_roster_item(U, S, LUser, LServer, GroupName, Subscription)
- end, get_group_users(Host, Group)).
-
-%% Get list of groups to which this group is displayed
-displayed_to_groups(GroupName, LServer) ->
- GroupsOpts = groups_with_opts(LServer),
- lists:filter(
- fun({_Group, Opts}) ->
- lists:member(GroupName, proplists:get_value(displayed_groups, Opts, []))
- end, GroupsOpts).
-
-push_item(User, Server, From, Item) ->
- %% It was
- %% ejabberd_sm:route(jlib:make_jid("", "", ""),
- %% jlib:make_jid(User, Server, "")
- %% why?
- ejabberd_sm:route(From, jlib:make_jid(User, Server, ""),
- {xmlelement, "broadcast", [],
- [{item,
- Item#roster.jid,
- Item#roster.subscription}]}),
- Stanza = jlib:iq_to_xml(
- #iq{type = set, xmlns = ?NS_ROSTER,
- id = "push" ++ randoms:get_string(),
- sub_el = [{xmlelement, "query",
- [{"xmlns", ?NS_ROSTER}],
- [item_to_xml(Item)]}]}),
- lists:foreach(
- fun(Resource) ->
- JID = jlib:make_jid(User, Server, Resource),
- ejabberd_router:route(JID, JID, Stanza)
- end, ejabberd_sm:get_user_resources(User, Server)).
-
-push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) ->
- Item = #roster{usj = {User, Server, {ContactU, ContactS, ""}},
- us = {User, Server},
- jid = {ContactU, ContactS, ""},
- name = "",
- subscription = Subscription,
- ask = none,
- groups = [GroupName]},
- push_item(User, Server, jlib:make_jid("", Server, ""), Item).
-
-item_to_xml(Item) ->
- Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
- Attrs2 = case Item#roster.name of
- "" ->
- Attrs1;
- Name ->
- [{"name", Name} | Attrs1]
- end,
- Attrs3 = case Item#roster.subscription of
- none ->
- [{"subscription", "none"} | Attrs2];
- from ->
- [{"subscription", "from"} | Attrs2];
- to ->
- [{"subscription", "to"} | Attrs2];
- both ->
- [{"subscription", "both"} | Attrs2];
- remove ->
- [{"subscription", "remove"} | Attrs2]
- end,
- Attrs4 = case ask_to_pending(Item#roster.ask) of
- out ->
- [{"ask", "subscribe"} | Attrs3];
- both ->
- [{"ask", "subscribe"} | Attrs3];
- _ ->
- Attrs3
- end,
- SubEls1 = lists:map(fun(G) ->
- {xmlelement, "group", [], [{xmlcdata, G}]}
- end, Item#roster.groups),
- SubEls = SubEls1 ++ Item#roster.xs,
- {xmlelement, "item", Attrs4, SubEls}.
-
-ask_to_pending(subscribe) -> out;
-ask_to_pending(unsubscribe) -> none;
-ask_to_pending(Ask) -> Ask.
-
-user_available(New) ->
- LUser = New#jid.luser,
- LServer = New#jid.lserver,
- Resources = ejabberd_sm:get_user_resources(LUser, LServer),
- ?DEBUG("user_available for ~p @ ~p (~p resources)",
- [LUser, LServer, length(Resources)]),
- case length(Resources) of
- %% first session for this user
- 1 ->
- %% This is a simplification - we ignore he 'display'
- %% property - @online@ is always reflective.
- OnlineGroups = get_special_users_groups_online(LServer),
- lists:foreach(
- fun(OG) ->
- ?DEBUG("user_available: pushing ~p @ ~p grp ~p",
- [LUser, LServer, OG ]),
- push_user_to_displayed(LUser, LServer, OG, LServer, both)
- end, OnlineGroups);
- _ ->
- ok
- end.
-
-unset_presence(LUser, LServer, Resource, Status) ->
- Resources = ejabberd_sm:get_user_resources(LUser, LServer),
- ?DEBUG("unset_presence for ~p @ ~p / ~p -> ~p (~p resources)",
- [LUser, LServer, Resource, Status, length(Resources)]),
- %% if user has no resources left...
- case length(Resources) of
- 0 ->
- %% This is a simplification - we ignore he 'display'
- %% property - @online@ is always reflective.
- OnlineGroups = get_special_users_groups_online(LServer),
- %% for each of these groups...
- lists:foreach(
- fun(OG) ->
- %% Push removal of the old user to members of groups
- %% where the group that this uwas members was displayed
- push_user_to_displayed(LUser, LServer, OG, LServer, remove),
- %% Push removal of members of groups that where
- %% displayed to the group which thiuser has left
- push_displayed_to_user(LUser, LServer, OG, LServer,remove)
- end, OnlineGroups);
- _ ->
- ok
- end.
-
-%%---------------------
-%% Web Admin
-%%---------------------
-
-webadmin_menu(Acc, _Host, Lang) ->
- [{"shared-roster", ?T("Shared Roster Groups")} | Acc].
-
-webadmin_page(_, Host,
- #request{us = _US,
- path = ["shared-roster"],
- q = Query,
- lang = Lang} = _Request) ->
- Res = list_shared_roster_groups(Host, Query, Lang),
- {stop, Res};
-
-webadmin_page(_, Host,
- #request{us = _US,
- path = ["shared-roster", Group],
- q = Query,
- lang = Lang} = _Request) ->
- Res = shared_roster_group(Host, Group, Query, Lang),
- {stop, Res};
-
-webadmin_page(Acc, _, _) -> Acc.
-
-list_shared_roster_groups(Host, Query, Lang) ->
- Res = list_sr_groups_parse_query(Host, Query),
- SRGroups = ?MODULE:list_groups(Host),
- FGroups =
- ?XAE("table", [],
- [?XE("tbody",
- lists:map(
- fun(Group) ->
- ?XE("tr",
- [?XE("td", [?INPUT("checkbox", "selected",
- Group)]),
- ?XE("td", [?AC(Group ++ "/", Group)])
- ]
- )
- end, lists:sort(SRGroups)) ++
- [?XE("tr",
- [?X("td"),
- ?XE("td", [?INPUT("text", "namenew", "")]),
- ?XE("td", [?INPUTT("submit", "addnew", "Add New")])
- ]
- )]
- )]),
- ?H1GL(?T("Shared Roster Groups"), "modsharedroster", "mod_shared_roster") ++
- case Res of
- ok -> [?XREST("Submitted")];
- error -> [?XREST("Bad format")];
- nothing -> []
- end ++
- [?XAE("form", [{"action", ""}, {"method", "post"}],
- [FGroups,
- ?BR,
- ?INPUTT("submit", "delete", "Delete Selected")
- ])
- ].
-
-list_sr_groups_parse_query(Host, Query) ->
- case lists:keysearch("addnew", 1, Query) of
- {value, _} ->
- list_sr_groups_parse_addnew(Host, Query);
- _ ->
- case lists:keysearch("delete", 1, Query) of
- {value, _} ->
- list_sr_groups_parse_delete(Host, Query);
- _ ->
- nothing
- end
- end.
-
-list_sr_groups_parse_addnew(Host, Query) ->
- case lists:keysearch("namenew", 1, Query) of
- {value, {_, Group}} when Group /= "" ->
- ?MODULE:create_group(Host, Group),
- ok;
- _ ->
- error
- end.
-
-list_sr_groups_parse_delete(Host, Query) ->
- SRGroups = ?MODULE:list_groups(Host),
- lists:foreach(
- fun(Group) ->
- case lists:member({"selected", Group}, Query) of
- true ->
- ?MODULE:delete_group(Host, Group);
- _ ->
- ok
- end
- end, SRGroups),
- ok.
-
-
-shared_roster_group(Host, Group, Query, Lang) ->
- Res = shared_roster_group_parse_query(Host, Group, Query),
- GroupOpts = ?MODULE:get_group_opts(Host, Group),
- Name = get_opt(GroupOpts, name, ""),
- Description = get_opt(GroupOpts, description, ""),
- AllUsers = get_opt(GroupOpts, all_users, false),
- OnlineUsers = get_opt(GroupOpts, online_users, false),
- %%Disabled = false,
- DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
- Members = ?MODULE:get_group_explicit_users(Host, Group),
- FMembers =
- if
- AllUsers ->
- "@all@\n";
- true ->
- []
- end ++
- if
- OnlineUsers ->
- "@online@\n";
- true ->
- []
- end ++
- [[us_to_list(Member), $\n] || Member <- Members],
- FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
- DescNL = length(ejabberd_regexp:split(Description, "\n")),
- FGroup =
- ?XAE("table", [{"class", "withtextareas"}],
- [?XE("tbody",
- [?XE("tr",
- [?XCT("td", "Name:"),
- ?XE("td", [?INPUT("text", "name", Name)])
- ]
- ),
- ?XE("tr",
- [?XCT("td", "Description:"),
- ?XE("td", [
- ?TEXTAREA("description", integer_to_list(lists:max([3, DescNL])), "20", Description)
- ]
- )
- ]
- ),
- ?XE("tr",
- [?XCT("td", "Members:"),
- ?XE("td", [
- ?TEXTAREA("members", integer_to_list(lists:max([3, length(FMembers)])), "20", FMembers)
- ]
- )
- ]
- ),
- ?XE("tr",
- [?XCT("td", "Displayed Groups:"),
- ?XE("td", [
- ?TEXTAREA("dispgroups", integer_to_list(lists:max([3, length(FDisplayedGroups)])), "20", FDisplayedGroups)
- ]
- )
- ]
- )]
- )]),
- ?H1GL(?T("Shared Roster Groups"), "modsharedroster", "mod_shared_roster") ++
- [?XC("h2", ?T("Group ") ++ Group)] ++
- case Res of
- ok -> [?XREST("Submitted")];
- error -> [?XREST("Bad format")];
- nothing -> []
- end ++
- [?XAE("form", [{"action", ""}, {"method", "post"}],
- [FGroup,
- ?BR,
- ?INPUTT("submit", "submit", "Submit")
- ])
- ].
-
-shared_roster_group_parse_query(Host, Group, Query) ->
- case lists:keysearch("submit", 1, Query) of
- {value, _} ->
- {value, {_, Name}} = lists:keysearch("name", 1, Query),
- {value, {_, Description}} = lists:keysearch("description", 1, Query),
- {value, {_, SMembers}} = lists:keysearch("members", 1, Query),
- {value, {_, SDispGroups}} = lists:keysearch("dispgroups", 1, Query),
- NameOpt =
- if
- Name == "" -> [];
- true -> [{name, Name}]
- end,
- DescriptionOpt =
- if
- Description == "" -> [];
- true -> [{description, Description}]
- end,
- DispGroups = string:tokens(SDispGroups, "\r\n"),
- DispGroupsOpt =
- if
- DispGroups == [] -> [];
- true -> [{displayed_groups, DispGroups}]
- end,
-
- OldMembers = ?MODULE:get_group_explicit_users(
- Host, Group),
- SJIDs = string:tokens(SMembers, ", \r\n"),
- NewMembers =
- lists:foldl(
- fun(_SJID, error) -> error;
- (SJID, USs) ->
- case SJID of
- "@all@" ->
- USs;
- "@online@" ->
- USs;
- _ ->
- case jlib:string_to_jid(SJID) of
- JID when is_record(JID, jid) ->
- [{JID#jid.luser, JID#jid.lserver} | USs];
- error ->
- error
- end
- end
- end, [], SJIDs),
- AllUsersOpt =
- case lists:member("@all@", SJIDs) of
- true -> [{all_users, true}];
- false -> []
- end,
- OnlineUsersOpt =
- case lists:member("@online@", SJIDs) of
- true -> [{online_users, true}];
- false -> []
- end,
-
- ?MODULE:set_group_opts(
- Host, Group,
- NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt ++ OnlineUsersOpt),
-
- if
- NewMembers == error -> error;
- true ->
- AddedMembers = NewMembers -- OldMembers,
- RemovedMembers = OldMembers -- NewMembers,
- lists:foreach(
- fun(US) ->
- ?MODULE:remove_user_from_group(
- Host, US, Group)
- end, RemovedMembers),
- lists:foreach(
- fun(US) ->
- ?MODULE:add_user_to_group(
- Host, US, Group)
- end, AddedMembers),
- ok
- end;
- _ ->
- nothing
- end.
-
-%% Get the roster module for Server.
-get_roster_mod(Server) ->
- case lists:member(mod_roster_odbc,
- gen_mod:loaded_modules(Server)) of
- true -> mod_roster_odbc;
- false -> mod_roster
- end.
-
-get_opt(Opts, Opt, Default) ->
- case lists:keysearch(Opt, 1, Opts) of
- {value, {_, Val}} ->
- Val;
- false ->
- Default
- end.
-
-us_to_list({User, Server}) ->
- jlib:jid_to_string({User, Server, ""}).
-
-split_grouphost(Host, Group) ->
- case string:tokens(Group, "@") of
- [GroupName, HostName] ->
- {HostName, GroupName};
- [_] ->
- {Host, Group}
- end.
-
-make_jid_s(U, S) ->
- ejabberd_odbc:escape(
- jlib:jid_to_string(
- jlib:jid_tolower(
- jlib:make_jid(U, S, "")))).
-
-make_jid_s({U, S}) ->
- make_jid_s(U, S).
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index b796375f5..6381ec55c 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -1,7 +1,7 @@
%%%----------------------------------------------------------------------
%%% File : mod_vcard.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Vcard management in Mnesia
+%%% Purpose : Vcard management
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
@@ -61,25 +61,32 @@
-define(PROCNAME, ejabberd_mod_vcard).
start(Host, Opts) ->
- mnesia:create_table(vcard, [{disc_only_copies, [node()]},
- {attributes, record_info(fields, vcard)}]),
- mnesia:create_table(vcard_search,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, vcard_search)}]),
- update_tables(),
- mnesia:add_table_index(vcard_search, luser),
- mnesia:add_table_index(vcard_search, lfn),
- mnesia:add_table_index(vcard_search, lfamily),
- mnesia:add_table_index(vcard_search, lgiven),
- mnesia:add_table_index(vcard_search, lmiddle),
- mnesia:add_table_index(vcard_search, lnickname),
- mnesia:add_table_index(vcard_search, lbday),
- mnesia:add_table_index(vcard_search, lctry),
- mnesia:add_table_index(vcard_search, llocality),
- mnesia:add_table_index(vcard_search, lemail),
- mnesia:add_table_index(vcard_search, lorgname),
- mnesia:add_table_index(vcard_search, lorgunit),
-
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(vcard,
+ [{disc_only_copies, [node()]},
+ {attributes,
+ record_info(fields, vcard)}]),
+ mnesia:create_table(vcard_search,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, vcard_search)}]),
+ update_tables(),
+ mnesia:add_table_index(vcard_search, luser),
+ mnesia:add_table_index(vcard_search, lfn),
+ mnesia:add_table_index(vcard_search, lfamily),
+ mnesia:add_table_index(vcard_search, lgiven),
+ mnesia:add_table_index(vcard_search, lmiddle),
+ mnesia:add_table_index(vcard_search, lnickname),
+ mnesia:add_table_index(vcard_search, lbday),
+ mnesia:add_table_index(vcard_search, lctry),
+ mnesia:add_table_index(vcard_search, llocality),
+ mnesia:add_table_index(vcard_search, lemail),
+ mnesia:add_table_index(vcard_search, lorgname),
+ mnesia:add_table_index(vcard_search, lorgunit);
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(remove_user, Host,
?MODULE, remove_user, 50),
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
@@ -183,19 +190,45 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
end;
get ->
#jid{luser = LUser, lserver = LServer} = To,
- US = {LUser, LServer},
- F = fun() ->
- mnesia:read({vcard, US})
- end,
- Els = case mnesia:transaction(F) of
- {atomic, Rs} ->
- lists:map(fun(R) ->
- R#vcard.vcard
- end, Rs);
- {aborted, _Reason} ->
- []
- end,
- IQ#iq{type = result, sub_el = Els}
+ case get_vcard(LUser, LServer) of
+ error ->
+ IQ#iq{type = error,
+ sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]};
+ Els ->
+ IQ#iq{type = result, sub_el = Els}
+ end
+ end.
+
+get_vcard(LUser, LServer) ->
+ get_vcard(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_vcard(LUser, LServer, mnesia) ->
+ US = {LUser, LServer},
+ F = fun() ->
+ mnesia:read({vcard, US})
+ end,
+ case mnesia:transaction(F) of
+ {atomic, Rs} ->
+ lists:map(fun(R) ->
+ R#vcard.vcard
+ end, Rs);
+ {aborted, _Reason} ->
+ error
+ end;
+get_vcard(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case catch odbc_queries:get_vcard(LServer, Username) of
+ {selected, ["vcard"], [{SVCARD}]} ->
+ case xml_stream:parse_element(SVCARD) of
+ {error, _Reason} ->
+ error;
+ VCARD ->
+ [VCARD]
+ end;
+ {selected, ["vcard"], []} ->
+ [];
+ _ ->
+ error
end.
set_vcard(User, LServer, VCARD) ->
@@ -231,8 +264,6 @@ set_vcard(User, LServer, VCARD) ->
LOrgName = string2lower(OrgName),
LOrgUnit = string2lower(OrgUnit),
- US = {LUser, LServer},
-
if
(LUser == error) or
(LFN == error) or
@@ -248,26 +279,66 @@ set_vcard(User, LServer, VCARD) ->
(LOrgUnit == error) ->
{error, badarg};
true ->
- F = fun() ->
- mnesia:write(#vcard{us = US, vcard = VCARD}),
- mnesia:write(
- #vcard_search{us = US,
- user = {User, LServer},
- luser = LUser,
- fn = FN, lfn = LFN,
- family = Family, lfamily = LFamily,
- given = Given, lgiven = LGiven,
- middle = Middle, lmiddle = LMiddle,
- nickname = Nickname, lnickname = LNickname,
- bday = BDay, lbday = LBDay,
- ctry = CTRY, lctry = LCTRY,
- locality = Locality, llocality = LLocality,
- email = EMail, lemail = LEMail,
- orgname = OrgName, lorgname = LOrgName,
- orgunit = OrgUnit, lorgunit = LOrgUnit
- })
- end,
- mnesia:transaction(F),
+ case gen_mod:db_type(LServer, ?MODULE) of
+ mnesia ->
+ US = {LUser, LServer},
+ F = fun() ->
+ mnesia:write(#vcard{us = US, vcard = VCARD}),
+ mnesia:write(
+ #vcard_search{us = US,
+ user = {User, LServer},
+ luser = LUser,
+ fn = FN, lfn = LFN,
+ family = Family, lfamily = LFamily,
+ given = Given, lgiven = LGiven,
+ middle = Middle, lmiddle = LMiddle,
+ nickname = Nickname, lnickname = LNickname,
+ bday = BDay, lbday = LBDay,
+ ctry = CTRY, lctry = LCTRY,
+ locality = Locality, llocality = LLocality,
+ email = EMail, lemail = LEMail,
+ orgname = OrgName, lorgname = LOrgName,
+ orgunit = OrgUnit, lorgunit = LOrgUnit
+ })
+ end,
+ mnesia:transaction(F);
+ odbc ->
+ Username = ejabberd_odbc:escape(User),
+ LUsername = ejabberd_odbc:escape(LUser),
+ SVCARD = ejabberd_odbc:escape(
+ xml:element_to_binary(VCARD)),
+
+ SFN = ejabberd_odbc:escape(FN),
+ SLFN = ejabberd_odbc:escape(LFN),
+ SFamily = ejabberd_odbc:escape(Family),
+ SLFamily = ejabberd_odbc:escape(LFamily),
+ SGiven = ejabberd_odbc:escape(Given),
+ SLGiven = ejabberd_odbc:escape(LGiven),
+ SMiddle = ejabberd_odbc:escape(Middle),
+ SLMiddle = ejabberd_odbc:escape(LMiddle),
+ SNickname = ejabberd_odbc:escape(Nickname),
+ SLNickname = ejabberd_odbc:escape(LNickname),
+ SBDay = ejabberd_odbc:escape(BDay),
+ SLBDay = ejabberd_odbc:escape(LBDay),
+ SCTRY = ejabberd_odbc:escape(CTRY),
+ SLCTRY = ejabberd_odbc:escape(LCTRY),
+ SLocality = ejabberd_odbc:escape(Locality),
+ SLLocality = ejabberd_odbc:escape(LLocality),
+ SEMail = ejabberd_odbc:escape(EMail),
+ SLEMail = ejabberd_odbc:escape(LEMail),
+ SOrgName = ejabberd_odbc:escape(OrgName),
+ SLOrgName = ejabberd_odbc:escape(LOrgName),
+ SOrgUnit = ejabberd_odbc:escape(OrgUnit),
+ SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
+
+ odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail,
+ SFN, SFamily, SGiven, SLBDay, SLCTRY,
+ SLEMail, SLFN, SLFamily, SLGiven,
+ SLLocality, SLMiddle, SLNickname,
+ SLOrgName, SLOrgUnit, SLocality,
+ SMiddle, SNickname, SOrgName,
+ SOrgUnit, SVCARD, Username)
+ end,
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
end.
@@ -481,14 +552,34 @@ search_result(Lang, JID, ServerHost, Data) ->
?TLFIELD("text-single", "Email", "email"),
?TLFIELD("text-single", "Organization Name", "orgname"),
?TLFIELD("text-single", "Organization Unit", "orgunit")
- ]}] ++ lists:map(fun record_to_item/1, search(ServerHost, Data)).
+ ]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end,
+ search(ServerHost, Data)).
-define(FIELD(Var, Val),
{xmlelement, "field", [{"var", Var}],
[{xmlelement, "value", [],
[{xmlcdata, Val}]}]}).
-record_to_item(R) ->
+record_to_item(LServer, {Username, FN, Family, Given, Middle,
+ Nickname, BDay, CTRY, Locality,
+ EMail, OrgName, OrgUnit}) ->
+ {xmlelement, "item", [],
+ [
+ ?FIELD("jid", Username ++ "@" ++ LServer),
+ ?FIELD("fn", FN),
+ ?FIELD("last", Family),
+ ?FIELD("first", Given),
+ ?FIELD("middle", Middle),
+ ?FIELD("nick", Nickname),
+ ?FIELD("bday", BDay),
+ ?FIELD("ctry", CTRY),
+ ?FIELD("locality", Locality),
+ ?FIELD("email", EMail),
+ ?FIELD("orgname", OrgName),
+ ?FIELD("orgunit", OrgUnit)
+ ]
+ };
+record_to_item(_LServer, #vcard_search{} = R) ->
{User, Server} = R#vcard_search.user,
{xmlelement, "item", [],
[
@@ -509,9 +600,13 @@ record_to_item(R) ->
search(LServer, Data) ->
- MatchSpec = make_matchspec(LServer, Data),
+ DBType = gen_mod:db_type(LServer, ?MODULE),
+ MatchSpec = make_matchspec(LServer, Data, DBType),
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE,
allow_return_all, false),
+ search(LServer, MatchSpec, AllowReturnAll, DBType).
+
+search(LServer, MatchSpec, AllowReturnAll, mnesia) ->
if
(MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
[];
@@ -535,17 +630,58 @@ search(LServer, Data) ->
lists:sublist(Rs, ?JUD_MATCHES)
end
end
+ end;
+search(LServer, MatchSpec, AllowReturnAll, odbc) ->
+ if
+ (MatchSpec == "") and (not AllowReturnAll) ->
+ [];
+ true ->
+ Limit = case gen_mod:get_module_opt(LServer, ?MODULE,
+ matches, ?JUD_MATCHES) of
+ infinity ->
+ "";
+ Val when is_integer(Val) and (Val > 0) ->
+ [" LIMIT ", integer_to_list(Val)];
+ Val ->
+ ?ERROR_MSG("Illegal option value ~p. "
+ "Default value ~p substituted.",
+ [{matches, Val}, ?JUD_MATCHES]),
+ [" LIMIT ", integer_to_list(?JUD_MATCHES)]
+ end,
+ case catch ejabberd_odbc:sql_query(
+ LServer,
+ ["select username, fn, family, given, middle, "
+ " nickname, bday, ctry, locality, "
+ " email, orgname, orgunit from vcard_search ",
+ MatchSpec, Limit, ";"]) of
+ {selected, ["username", "fn", "family", "given", "middle",
+ "nickname", "bday", "ctry", "locality",
+ "email", "orgname", "orgunit"],
+ Rs} when is_list(Rs) ->
+ Rs;
+ Error ->
+ ?ERROR_MSG("~p", [Error]),
+ []
+ end
end.
-
-make_matchspec(LServer, Data) ->
+make_matchspec(LServer, Data, mnesia) ->
GlobMatch = #vcard_search{_ = '_'},
- Match = filter_fields(Data, GlobMatch, LServer),
- Match.
+ Match = filter_fields(Data, GlobMatch, LServer, mnesia),
+ Match;
+make_matchspec(LServer, Data, odbc) ->
+ filter_fields(Data, "", LServer, odbc).
-filter_fields([], Match, _LServer) ->
+filter_fields([], Match, _LServer, mnesia) ->
Match;
-filter_fields([{SVar, [Val]} | Ds], Match, LServer)
+filter_fields([], Match, _LServer, odbc) ->
+ case Match of
+ "" ->
+ "";
+ _ ->
+ [" where ", Match]
+ end;
+filter_fields([{SVar, [Val]} | Ds], Match, LServer, mnesia)
when is_list(Val) and (Val /= "") ->
LVal = string2lower(Val),
NewMatch = case SVar of
@@ -571,9 +707,46 @@ filter_fields([{SVar, [Val]} | Ds], Match, LServer)
"orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
_ -> Match
end,
- filter_fields(Ds, NewMatch, LServer);
-filter_fields([_ | Ds], Match, LServer) ->
- filter_fields(Ds, Match, LServer).
+ filter_fields(Ds, NewMatch, LServer, mnesia);
+filter_fields([{SVar, [Val]} | Ds], Match, LServer, odbc)
+ when is_list(Val) and (Val /= "") ->
+ LVal = string2lower(Val),
+ NewMatch = case SVar of
+ "user" -> make_val(Match, "lusername", LVal);
+ "fn" -> make_val(Match, "lfn", LVal);
+ "last" -> make_val(Match, "lfamily", LVal);
+ "first" -> make_val(Match, "lgiven", LVal);
+ "middle" -> make_val(Match, "lmiddle", LVal);
+ "nick" -> make_val(Match, "lnickname", LVal);
+ "bday" -> make_val(Match, "lbday", LVal);
+ "ctry" -> make_val(Match, "lctry", LVal);
+ "locality" -> make_val(Match, "llocality", LVal);
+ "email" -> make_val(Match, "lemail", LVal);
+ "orgname" -> make_val(Match, "lorgname", LVal);
+ "orgunit" -> make_val(Match, "lorgunit", LVal);
+ _ -> Match
+ end,
+ filter_fields(Ds, NewMatch, LServer, odbc);
+filter_fields([_ | Ds], Match, LServer, DBType) ->
+ filter_fields(Ds, Match, LServer, DBType).
+
+make_val(Match, Field, Val) ->
+ Condition =
+ case lists:suffix("*", Val) of
+ true ->
+ Val1 = lists:sublist(Val, length(Val) - 1),
+ SVal = ejabberd_odbc:escape_like(Val1) ++ "%",
+ [Field, " LIKE '", SVal, "'"];
+ _ ->
+ SVal = ejabberd_odbc:escape(Val),
+ [Field, " = '", SVal, "'"]
+ end,
+ case Match of
+ "" ->
+ Condition;
+ _ ->
+ [Match, " and ", Condition]
+ end.
make_val(Val) ->
case lists:suffix("*", Val) of
@@ -679,13 +852,21 @@ reindex_vcards() ->
remove_user(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
+ remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_user(LUser, LServer, mnesia) ->
US = {LUser, LServer},
F = fun() ->
mnesia:delete({vcard, US}),
mnesia:delete({vcard_search, US})
end,
- mnesia:transaction(F).
-
+ mnesia:transaction(F);
+remove_user(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ ejabberd_odbc:sql_transaction(
+ LServer,
+ [["delete from vcard where username='", Username, "';"],
+ ["delete from vcard_search where lusername='", Username, "';"]]).
update_tables() ->
update_vcard_table(),
diff --git a/src/mod_vcard_odbc.erl b/src/mod_vcard_odbc.erl
deleted file mode 100644
index f472dcb78..000000000
--- a/src/mod_vcard_odbc.erl
+++ /dev/null
@@ -1,659 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_vcard.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : vCard support via ODBC
-%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License
-%%% along with this program; if not, write to the Free Software
-%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-%%% 02111-1307 USA
-%%%
-%%%----------------------------------------------------------------------
-
--module(mod_vcard_odbc).
--author('alexey@process-one.net').
-
--behaviour(gen_mod).
-
--export([start/2, init/3, stop/1,
- get_sm_features/5,
- process_local_iq/3,
- process_sm_iq/3,
- %reindex_vcards/0,
- remove_user/2]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-
--define(JUD_MATCHES, 30).
--define(PROCNAME, ejabberd_mod_vcard).
-
-start(Host, Opts) ->
- ejabberd_hooks:add(remove_user, Host,
- ?MODULE, remove_user, 50),
- IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
- ?MODULE, process_local_iq, IQDisc),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
- ?MODULE, process_sm_iq, IQDisc),
- ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
- MyHost = gen_mod:get_opt_host(Host, Opts, "vjud.@HOST@"),
- Search = gen_mod:get_opt(search, Opts, true),
- register(gen_mod:get_module_proc(Host, ?PROCNAME),
- spawn(?MODULE, init, [MyHost, Host, Search])).
-
-
-init(Host, ServerHost, Search) ->
- case Search of
- false ->
- loop(Host, ServerHost);
- _ ->
- ejabberd_router:register_route(Host),
- loop(Host, ServerHost)
- end.
-
-loop(Host, ServerHost) ->
- receive
- {route, From, To, Packet} ->
- case catch do_route(ServerHost, From, To, Packet) of
- {'EXIT', Reason} ->
- ?ERROR_MSG("~p", [Reason]);
- _ ->
- ok
- end,
- loop(Host, ServerHost);
- stop ->
- ejabberd_router:unregister_route(Host),
- ok;
- _ ->
- loop(Host, ServerHost)
- end.
-
-stop(Host) ->
- ejabberd_hooks:delete(remove_user, Host,
- ?MODULE, remove_user, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
- ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- Proc ! stop,
- {wait, Proc}.
-
-get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
- Acc;
-
-get_sm_features(Acc, _From, _To, Node, _Lang) ->
- case Node of
- [] ->
- case Acc of
- {result, Features} ->
- {result, [?NS_VCARD | Features]};
- empty ->
- {result, [?NS_VCARD]}
- end;
- _ ->
- Acc
- end.
-
-process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
- get ->
- IQ#iq{type = result,
- sub_el = [{xmlelement, "vCard",
- [{"xmlns", ?NS_VCARD}],
- [{xmlelement, "FN", [],
- [{xmlcdata, "ejabberd"}]},
- {xmlelement, "URL", [],
- [{xmlcdata, ?EJABBERD_URI}]},
- {xmlelement, "DESC", [],
- [{xmlcdata,
- translate:translate(
- Lang,
- "Erlang Jabber Server") ++
- "\nCopyright (c) 2002-2012 ProcessOne"}]},
- {xmlelement, "BDAY", [],
- [{xmlcdata, "2002-11-16"}]}
- ]}]}
- end.
-
-
-process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- #jid{user = User, lserver = LServer} = From,
- case lists:member(LServer, ?MYHOSTS) of
- true ->
- set_vcard(User, LServer, SubEl),
- IQ#iq{type = result, sub_el = []};
- false ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
- end;
- get ->
- #jid{luser = LUser, lserver = LServer} = To,
- Username = ejabberd_odbc:escape(LUser),
- case catch odbc_queries:get_vcard(LServer, Username) of
- {selected, ["vcard"], [{SVCARD}]} ->
- case xml_stream:parse_element(SVCARD) of
- {error, _Reason} ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
- VCARD ->
- IQ#iq{type = result, sub_el = [VCARD]}
- end;
- {selected, ["vcard"], []} ->
- IQ#iq{type = result, sub_el = []};
- _ ->
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
- end
- end.
-
-set_vcard(User, LServer, VCARD) ->
- FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
- Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]),
- Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]),
- Middle = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"}, cdata]),
- Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"}, cdata]),
- BDay = xml:get_path_s(VCARD, [{elem, "BDAY"}, cdata]),
- CTRY = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"}, cdata]),
- Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]),
- EMail1 = xml:get_path_s(VCARD, [{elem, "EMAIL"}, {elem, "USERID"},cdata]),
- EMail2 = xml:get_path_s(VCARD, [{elem, "EMAIL"}, cdata]),
- OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
- OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
- EMail = case EMail1 of
- "" ->
- EMail2;
- _ ->
- EMail1
- end,
-
- LUser = jlib:nodeprep(User),
- LFN = string2lower(FN),
- LFamily = string2lower(Family),
- LGiven = string2lower(Given),
- LMiddle = string2lower(Middle),
- LNickname = string2lower(Nickname),
- LBDay = string2lower(BDay),
- LCTRY = string2lower(CTRY),
- LLocality = string2lower(Locality),
- LEMail = string2lower(EMail),
- LOrgName = string2lower(OrgName),
- LOrgUnit = string2lower(OrgUnit),
-
- if
- (LUser == error) or
- (LFN == error) or
- (LFamily == error) or
- (LGiven == error) or
- (LMiddle == error) or
- (LNickname == error) or
- (LBDay == error) or
- (LCTRY == error) or
- (LLocality == error) or
- (LEMail == error) or
- (LOrgName == error) or
- (LOrgUnit == error) ->
- {error, badarg};
- true ->
- Username = ejabberd_odbc:escape(User),
- LUsername = ejabberd_odbc:escape(LUser),
- SVCARD = ejabberd_odbc:escape(
- xml:element_to_binary(VCARD)),
-
- SFN = ejabberd_odbc:escape(FN),
- SLFN = ejabberd_odbc:escape(LFN),
- SFamily = ejabberd_odbc:escape(Family),
- SLFamily = ejabberd_odbc:escape(LFamily),
- SGiven = ejabberd_odbc:escape(Given),
- SLGiven = ejabberd_odbc:escape(LGiven),
- SMiddle = ejabberd_odbc:escape(Middle),
- SLMiddle = ejabberd_odbc:escape(LMiddle),
- SNickname = ejabberd_odbc:escape(Nickname),
- SLNickname = ejabberd_odbc:escape(LNickname),
- SBDay = ejabberd_odbc:escape(BDay),
- SLBDay = ejabberd_odbc:escape(LBDay),
- SCTRY = ejabberd_odbc:escape(CTRY),
- SLCTRY = ejabberd_odbc:escape(LCTRY),
- SLocality = ejabberd_odbc:escape(Locality),
- SLLocality = ejabberd_odbc:escape(LLocality),
- SEMail = ejabberd_odbc:escape(EMail),
- SLEMail = ejabberd_odbc:escape(LEMail),
- SOrgName = ejabberd_odbc:escape(OrgName),
- SLOrgName = ejabberd_odbc:escape(LOrgName),
- SOrgUnit = ejabberd_odbc:escape(OrgUnit),
- SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
-
- odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail,
- SFN, SFamily, SGiven, SLBDay, SLCTRY,
- SLEMail, SLFN, SLFamily, SLGiven,
- SLLocality, SLMiddle, SLNickname,
- SLOrgName, SLOrgUnit, SLocality,
- SMiddle, SNickname, SOrgName,
- SOrgUnit, SVCARD, Username),
-
- ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
- end.
-
-string2lower(String) ->
- case stringprep:tolower(String) of
- Lower when is_list(Lower) -> Lower;
- error -> string:to_lower(String)
- end.
-
--define(TLFIELD(Type, Label, Var),
- {xmlelement, "field", [{"type", Type},
- {"label", translate:translate(Lang, Label)},
- {"var", Var}], []}).
-
-
--define(FORM(JID),
- [{xmlelement, "instructions", [],
- [{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
- {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
- [{xmlelement, "title", [],
- [{xmlcdata, translate:translate(Lang, "Search users in ") ++
- jlib:jid_to_string(JID)}]},
- {xmlelement, "instructions", [],
- [{xmlcdata, translate:translate(Lang, "Fill in the form to search "
- "for any matching Jabber User "
- "(Add * to the end of field to "
- "match substring)")}]},
- ?TLFIELD("text-single", "User", "user"),
- ?TLFIELD("text-single", "Full Name", "fn"),
- ?TLFIELD("text-single", "Name", "first"),
- ?TLFIELD("text-single", "Middle Name", "middle"),
- ?TLFIELD("text-single", "Family Name", "last"),
- ?TLFIELD("text-single", "Nickname", "nick"),
- ?TLFIELD("text-single", "Birthday", "bday"),
- ?TLFIELD("text-single", "Country", "ctry"),
- ?TLFIELD("text-single", "City", "locality"),
- ?TLFIELD("text-single", "Email", "email"),
- ?TLFIELD("text-single", "Organization Name", "orgname"),
- ?TLFIELD("text-single", "Organization Unit", "orgunit")
- ]}]).
-
-do_route(ServerHost, From, To, Packet) ->
- #jid{user = User, resource = Resource} = To,
- if
- (User /= "") or (Resource /= "") ->
- Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
- ejabberd_router:route(To, From, Err);
- true ->
- IQ = jlib:iq_query_info(Packet),
- case IQ of
- #iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
- case Type of
- set ->
- XDataEl = find_xdata_el(SubEl),
- case XDataEl of
- false ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_BAD_REQUEST),
- ejabberd_router:route(To, From, Err);
- _ ->
- XData = jlib:parse_xdata_submit(XDataEl),
- case XData of
- invalid ->
- Err = jlib:make_error_reply(
- Packet,
- ?ERR_BAD_REQUEST),
- ejabberd_router:route(To, From,
- Err);
- _ ->
- ResIQ =
- IQ#iq{
- type = result,
- sub_el =
- [{xmlelement,
- "query",
- [{"xmlns", ?NS_SEARCH}],
- [{xmlelement, "x",
- [{"xmlns", ?NS_XDATA},
- {"type", "result"}],
- search_result(Lang, To, ServerHost, XData)
- }]}]},
- ejabberd_router:route(
- To, From, jlib:iq_to_xml(ResIQ))
- end
- end;
- get ->
- ResIQ = IQ#iq{type = result,
- sub_el = [{xmlelement,
- "query",
- [{"xmlns", ?NS_SEARCH}],
- ?FORM(To)
- }]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(ResIQ))
- end;
- #iq{type = Type, xmlns = ?NS_DISCO_INFO, lang = Lang} ->
- case Type of
- set ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_NOT_ALLOWED),
- ejabberd_router:route(To, From, Err);
- get ->
- Info = ejabberd_hooks:run_fold(
- disco_info, ServerHost, [],
- [ServerHost, ?MODULE, "", ""]),
- ResIQ =
- IQ#iq{type = result,
- sub_el = [{xmlelement,
- "query",
- [{"xmlns", ?NS_DISCO_INFO}],
- [{xmlelement, "identity",
- [{"category", "directory"},
- {"type", "user"},
- {"name",
- translate:translate(Lang, "vCard User Search")}],
- []},
- {xmlelement, "feature",
- [{"var", ?NS_SEARCH}], []},
- {xmlelement, "feature",
- [{"var", ?NS_VCARD}], []}
- ] ++ Info
- }]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(ResIQ))
- end;
- #iq{type = Type, xmlns = ?NS_DISCO_ITEMS} ->
- case Type of
- set ->
- Err = jlib:make_error_reply(
- Packet, ?ERR_NOT_ALLOWED),
- ejabberd_router:route(To, From, Err);
- get ->
- ResIQ =
- IQ#iq{type = result,
- sub_el = [{xmlelement,
- "query",
- [{"xmlns", ?NS_DISCO_ITEMS}],
- []}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(ResIQ))
- end;
- #iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
- ResIQ =
- IQ#iq{type = result,
- sub_el = [{xmlelement,
- "vCard",
- [{"xmlns", ?NS_VCARD}],
- iq_get_vcard(Lang)}]},
- ejabberd_router:route(To,
- From,
- jlib:iq_to_xml(ResIQ));
- _ ->
- Err = jlib:make_error_reply(Packet,
- ?ERR_SERVICE_UNAVAILABLE),
- ejabberd_router:route(To, From, Err)
- end
- end.
-
-iq_get_vcard(Lang) ->
- [{xmlelement, "FN", [],
- [{xmlcdata, "ejabberd/mod_vcard"}]},
- {xmlelement, "URL", [],
- [{xmlcdata, ?EJABBERD_URI}]},
- {xmlelement, "DESC", [],
- [{xmlcdata, translate:translate(
- Lang,
- "ejabberd vCard module") ++
- "\nCopyright (c) 2003-2012 ProcessOne"}]}].
-
-find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
- find_xdata_el1(SubEls).
-
-find_xdata_el1([]) ->
- false;
-find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
- case xml:get_attr_s("xmlns", Attrs) of
- ?NS_XDATA ->
- {xmlelement, Name, Attrs, SubEls};
- _ ->
- find_xdata_el1(Els)
- end;
-find_xdata_el1([_ | Els]) ->
- find_xdata_el1(Els).
-
--define(LFIELD(Label, Var),
- {xmlelement, "field", [{"label", translate:translate(Lang, Label)},
- {"var", Var}], []}).
-
-search_result(Lang, JID, ServerHost, Data) ->
- [{xmlelement, "title", [],
- [{xmlcdata, translate:translate(Lang, "Search Results for ") ++
- jlib:jid_to_string(JID)}]},
- {xmlelement, "reported", [],
- [?TLFIELD("text-single", "Jabber ID", "jid"),
- ?TLFIELD("text-single", "Full Name", "fn"),
- ?TLFIELD("text-single", "Name", "first"),
- ?TLFIELD("text-single", "Middle Name", "middle"),
- ?TLFIELD("text-single", "Family Name", "last"),
- ?TLFIELD("text-single", "Nickname", "nick"),
- ?TLFIELD("text-single", "Birthday", "bday"),
- ?TLFIELD("text-single", "Country", "ctry"),
- ?TLFIELD("text-single", "City", "locality"),
- ?TLFIELD("text-single", "Email", "email"),
- ?TLFIELD("text-single", "Organization Name", "orgname"),
- ?TLFIELD("text-single", "Organization Unit", "orgunit")
- ]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end,
- search(ServerHost, Data)).
-
--define(FIELD(Var, Val),
- {xmlelement, "field", [{"var", Var}],
- [{xmlelement, "value", [],
- [{xmlcdata, Val}]}]}).
-
-
-record_to_item(LServer, {Username, FN, Family, Given, Middle,
- Nickname, BDay, CTRY, Locality,
- EMail, OrgName, OrgUnit}) ->
- {xmlelement, "item", [],
- [
- ?FIELD("jid", Username ++ "@" ++ LServer),
- ?FIELD("fn", FN),
- ?FIELD("last", Family),
- ?FIELD("first", Given),
- ?FIELD("middle", Middle),
- ?FIELD("nick", Nickname),
- ?FIELD("bday", BDay),
- ?FIELD("ctry", CTRY),
- ?FIELD("locality", Locality),
- ?FIELD("email", EMail),
- ?FIELD("orgname", OrgName),
- ?FIELD("orgunit", OrgUnit)
- ]
- }.
-
-
-search(LServer, Data) ->
- MatchSpec = make_matchspec(LServer, Data),
- AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE,
- allow_return_all, false),
- if
- (MatchSpec == "") and (not AllowReturnAll) ->
- [];
- true ->
- Limit = case gen_mod:get_module_opt(LServer, ?MODULE,
- matches, ?JUD_MATCHES) of
- infinity ->
- "";
- Val when is_integer(Val) and (Val > 0) ->
- [" LIMIT ", integer_to_list(Val)];
- Val ->
- ?ERROR_MSG("Illegal option value ~p. "
- "Default value ~p substituted.",
- [{matches, Val}, ?JUD_MATCHES]),
- [" LIMIT ", integer_to_list(?JUD_MATCHES)]
- end,
- case catch ejabberd_odbc:sql_query(
- LServer,
- ["select username, fn, family, given, middle, "
- " nickname, bday, ctry, locality, "
- " email, orgname, orgunit from vcard_search ",
- MatchSpec, Limit, ";"]) of
- {selected, ["username", "fn", "family", "given", "middle",
- "nickname", "bday", "ctry", "locality",
- "email", "orgname", "orgunit"],
- Rs} when is_list(Rs) ->
- Rs;
- Error ->
- ?ERROR_MSG("~p", [Error]),
- []
- end
- end.
-
-
-make_matchspec(LServer, Data) ->
- filter_fields(Data, "", LServer).
-
-filter_fields([], Match, _LServer) ->
- case Match of
- "" ->
- "";
- _ ->
- [" where ", Match]
- end;
-filter_fields([{SVar, [Val]} | Ds], Match, LServer)
- when is_list(Val) and (Val /= "") ->
- LVal = string2lower(Val),
- NewMatch = case SVar of
- "user" -> make_val(Match, "lusername", LVal);
- "fn" -> make_val(Match, "lfn", LVal);
- "last" -> make_val(Match, "lfamily", LVal);
- "first" -> make_val(Match, "lgiven", LVal);
- "middle" -> make_val(Match, "lmiddle", LVal);
- "nick" -> make_val(Match, "lnickname", LVal);
- "bday" -> make_val(Match, "lbday", LVal);
- "ctry" -> make_val(Match, "lctry", LVal);
- "locality" -> make_val(Match, "llocality", LVal);
- "email" -> make_val(Match, "lemail", LVal);
- "orgname" -> make_val(Match, "lorgname", LVal);
- "orgunit" -> make_val(Match, "lorgunit", LVal);
- _ -> Match
- end,
- filter_fields(Ds, NewMatch, LServer);
-filter_fields([_ | Ds], Match, LServer) ->
- filter_fields(Ds, Match, LServer).
-
-make_val(Match, Field, Val) ->
- Condition =
- case lists:suffix("*", Val) of
- true ->
- Val1 = lists:sublist(Val, length(Val) - 1),
- SVal = ejabberd_odbc:escape_like(Val1) ++ "%",
- [Field, " LIKE '", SVal, "'"];
- _ ->
- SVal = ejabberd_odbc:escape(Val),
- [Field, " = '", SVal, "'"]
- end,
- case Match of
- "" ->
- Condition;
- _ ->
- [Match, " and ", Condition]
- end.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%set_vcard_t(R, _) ->
-% US = R#vcard.us,
-% User = US,
-% VCARD = R#vcard.vcard,
-%
-% FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]),
-% Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]),
-% Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]),
-% Middle = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"}, cdata]),
-% Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"}, cdata]),
-% BDay = xml:get_path_s(VCARD, [{elem, "BDAY"}, cdata]),
-% CTRY = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"}, cdata]),
-% Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]),
-% EMail = xml:get_path_s(VCARD, [{elem, "EMAIL"}, cdata]),
-% OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]),
-% OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]),
-%
-% {LUser, _LServer} = US,
-% LFN = stringprep:tolower(FN),
-% LFamily = stringprep:tolower(Family),
-% LGiven = stringprep:tolower(Given),
-% LMiddle = stringprep:tolower(Middle),
-% LNickname = stringprep:tolower(Nickname),
-% LBDay = stringprep:tolower(BDay),
-% LCTRY = stringprep:tolower(CTRY),
-% LLocality = stringprep:tolower(Locality),
-% LEMail = stringprep:tolower(EMail),
-% LOrgName = stringprep:tolower(OrgName),
-% LOrgUnit = stringprep:tolower(OrgUnit),
-%
-% if
-% (LUser == error) or
-% (LFN == error) or
-% (LFamily == error) or
-% (LGiven == error) or
-% (LMiddle == error) or
-% (LNickname == error) or
-% (LBDay == error) or
-% (LCTRY == error) or
-% (LLocality == error) or
-% (LEMail == error) or
-% (LOrgName == error) or
-% (LOrgUnit == error) ->
-% {error, badarg};
-% true ->
-% mnesia:write(
-% #vcard_search{us = US,
-% user = User, luser = LUser,
-% fn = FN, lfn = LFN,
-% family = Family, lfamily = LFamily,
-% given = Given, lgiven = LGiven,
-% middle = Middle, lmiddle = LMiddle,
-% nickname = Nickname, lnickname = LNickname,
-% bday = BDay, lbday = LBDay,
-% ctry = CTRY, lctry = LCTRY,
-% locality = Locality, llocality = LLocality,
-% email = EMail, lemail = LEMail,
-% orgname = OrgName, lorgname = LOrgName,
-% orgunit = OrgUnit, lorgunit = LOrgUnit
-% })
-% end.
-%
-%
-%reindex_vcards() ->
-% F = fun() ->
-% mnesia:foldl(fun set_vcard_t/2, [], vcard)
-% end,
-% mnesia:transaction(F).
-
-
-remove_user(User, Server) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- Username = ejabberd_odbc:escape(LUser),
- ejabberd_odbc:sql_transaction(
- LServer,
- [["delete from vcard where username='", Username, "';"],
- ["delete from vcard_search where lusername='", Username, "';"]]).
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index 38503c4c3..3ee632aaa 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -26,10 +26,16 @@
%% gen_mod callbacks
%%====================================================================
-start(Host, _Opts) ->
- mnesia:create_table(vcard_xupdate,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, vcard_xupdate)}]),
+start(Host, Opts) ->
+ case gen_mod:db_type(Opts) of
+ mnesia ->
+ mnesia:create_table(vcard_xupdate,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, vcard_xupdate)}]);
+ _ ->
+ ok
+ end,
ejabberd_hooks:add(c2s_update_presence, Host,
?MODULE, update_presence, 100),
ejabberd_hooks:add(vcard_set, Host,
@@ -68,28 +74,66 @@ vcard_set(LUser, LServer, VCARD) ->
ejabberd_sm:force_update_presence(US).
%%====================================================================
-%% Mnesia storage
+%% Storage
%%====================================================================
add_xupdate(LUser, LServer, Hash) ->
+ add_xupdate(LUser, LServer, Hash, gen_mod:db_type(LServer, ?MODULE)).
+
+add_xupdate(LUser, LServer, Hash, mnesia) ->
F = fun() ->
mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+add_xupdate(LUser, LServer, Hash, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ SHash = ejabberd_odbc:escape(Hash),
+ F = fun() ->
+ odbc_queries:update_t(
+ "vcard_xupdate",
+ ["username", "hash"],
+ [Username, SHash],
+ ["username='", Username, "'"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
get_xupdate(LUser, LServer) ->
+ get_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+get_xupdate(LUser, LServer, mnesia) ->
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of
[#vcard_xupdate{hash = Hash}] ->
Hash;
_ ->
undefined
+ end;
+get_xupdate(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ case ejabberd_odbc:sql_query(
+ LServer, ["select hash from vcard_xupdate "
+ "where username='", Username, "';"]) of
+ {selected, ["hash"], [{Hash}]} ->
+ Hash;
+ _ ->
+ undefined
end.
remove_xupdate(LUser, LServer) ->
+ remove_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
+
+remove_xupdate(LUser, LServer, mnesia) ->
F = fun() ->
mnesia:delete({vcard_xupdate, {LUser, LServer}})
end,
- mnesia:transaction(F).
+ mnesia:transaction(F);
+remove_xupdate(LUser, LServer, odbc) ->
+ Username = ejabberd_odbc:escape(LUser),
+ F = fun() ->
+ ejabberd_odbc:sql_query_t(
+ ["delete from vcard_xupdate where "
+ "username='", Username, "';"])
+ end,
+ ejabberd_odbc:sql_transaction(LServer, F).
%%%----------------------------------------------------------------------
%%% Presence stanza rebuilding
diff --git a/src/mod_vcard_xupdate_odbc.erl b/src/mod_vcard_xupdate_odbc.erl
deleted file mode 100644
index b6a5adfb4..000000000
--- a/src/mod_vcard_xupdate_odbc.erl
+++ /dev/null
@@ -1,128 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_vcard_xupdate_odbc.erl
-%%% Author : Igor Goryachev <igor@goryachev.org>
-%%% Purpose : Add avatar hash in presence on behalf of client (XEP-0153)
-%%% Created : 9 Mar 2007 by Igor Goryachev <igor@goryachev.org>
-%%%----------------------------------------------------------------------
-
--module(mod_vcard_xupdate_odbc).
-
--behaviour(gen_mod).
-
-%% gen_mod callbacks
--export([start/2,
- stop/1]).
-
-%% hooks
--export([update_presence/3,
- vcard_set/3]).
-
--include("ejabberd.hrl").
--include("jlib.hrl").
-
-%%====================================================================
-%% gen_mod callbacks
-%%====================================================================
-
-start(Host, _Opts) ->
- ejabberd_hooks:add(c2s_update_presence, Host,
- ?MODULE, update_presence, 100),
- ejabberd_hooks:add(vcard_set, Host,
- ?MODULE, vcard_set, 100),
- ok.
-
-stop(Host) ->
- ejabberd_hooks:delete(c2s_update_presence, Host,
- ?MODULE, update_presence, 100),
- ejabberd_hooks:delete(vcard_set, Host,
- ?MODULE, vcard_set, 100),
- ok.
-
-%%====================================================================
-%% Hooks
-%%====================================================================
-
-update_presence({xmlelement, "presence", Attrs, _Els} = Packet, User, Host) ->
- case xml:get_attr_s("type", Attrs) of
- [] ->
- presence_with_xupdate(Packet, User, Host);
- _ ->
- Packet
- end;
-update_presence(Packet, _User, _Host) ->
- Packet.
-
-vcard_set(LUser, LServer, VCARD) ->
- US = {LUser, LServer},
- case xml:get_path_s(VCARD, [{elem, "PHOTO"}, {elem, "BINVAL"}, cdata]) of
- [] ->
- remove_xupdate(LUser, LServer);
- BinVal ->
- add_xupdate(LUser, LServer, sha:sha(jlib:decode_base64(BinVal)))
- end,
- ejabberd_sm:force_update_presence(US).
-
-%%====================================================================
-%% ODBC storage
-%%====================================================================
-
-add_xupdate(LUser, LServer, Hash) ->
- Username = ejabberd_odbc:escape(LUser),
- SHash = ejabberd_odbc:escape(Hash),
- F = fun() ->
- odbc_queries:update_t(
- "vcard_xupdate",
- ["username", "hash"],
- [Username, SHash],
- ["username='", Username, "'"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-get_xupdate(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- case ejabberd_odbc:sql_query(
- LServer, ["select hash from vcard_xupdate "
- "where username='", Username, "';"]) of
- {selected, ["hash"], [{Hash}]} ->
- Hash;
- _ ->
- undefined
- end.
-
-remove_xupdate(LUser, LServer) ->
- Username = ejabberd_odbc:escape(LUser),
- F = fun() ->
- ejabberd_odbc:sql_query_t(
- ["delete from vcard_xupdate where "
- "username='", Username, "';"])
- end,
- ejabberd_odbc:sql_transaction(LServer, F).
-
-%%%----------------------------------------------------------------------
-%%% Presence stanza rebuilding
-%%%----------------------------------------------------------------------
-
-presence_with_xupdate({xmlelement, "presence", Attrs, Els}, User, Host) ->
- XPhotoEl = build_xphotoel(User, Host),
- Els2 = presence_with_xupdate2(Els, [], XPhotoEl),
- {xmlelement, "presence", Attrs, Els2}.
-
-presence_with_xupdate2([], Els2, XPhotoEl) ->
- lists:reverse([XPhotoEl | Els2]);
-%% This clause assumes that the x element contains only the XMLNS attribute:
-presence_with_xupdate2([{xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], _}
- | Els], Els2, XPhotoEl) ->
- presence_with_xupdate2(Els, Els2, XPhotoEl);
-presence_with_xupdate2([El | Els], Els2, XPhotoEl) ->
- presence_with_xupdate2(Els, [El | Els2], XPhotoEl).
-
-build_xphotoel(User, Host) ->
- Hash = get_xupdate(User, Host),
- PhotoSubEls = case Hash of
- Hash when is_list(Hash) ->
- [{xmlcdata, Hash}];
- _ ->
- []
- end,
- PhotoEl = [{xmlelement, "photo", [], PhotoSubEls}],
- {xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], PhotoEl}.
diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl
index 447926e26..59e9b0313 100644
--- a/src/web/ejabberd_web_admin.erl
+++ b/src/web/ejabberd_web_admin.erl
@@ -1564,7 +1564,6 @@ list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
[list_given_users(Host, Sub, "../../", Lang, URLFunc)].
list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
- ModLast = get_lastactivity_module(Host),
ModOffline = get_offlinemsg_module(Host),
?XE("table",
[?XE("thead",
@@ -1583,7 +1582,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
FLast =
case ejabberd_sm:get_user_resources(User, Server) of
[] ->
- case ModLast:get_last_info(User, Server) of
+ case mod_last:get_last_info(User, Server) of
not_found ->
?T("Never");
{ok, Shift, _Status} ->
@@ -1618,22 +1617,17 @@ get_offlinemsg_length(ModOffline, User, Server) ->
end.
get_offlinemsg_module(Server) ->
- case [mod_offline, mod_offline_odbc] -- gen_mod:loaded_modules(Server) of
- [mod_offline, mod_offline_odbc] -> none;
- [mod_offline_odbc] -> mod_offline;
- [mod_offline] -> mod_offline_odbc
- end.
-
-get_lastactivity_module(Server) ->
- case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
- true -> mod_last;
- _ -> mod_last_odbc
+ case gen_mod:is_loaded(Server, mod_offline) of
+ true ->
+ mod_offline;
+ false ->
+ none
end.
get_lastactivity_menuitem_list(Server) ->
- case get_lastactivity_module(Server) of
- mod_last -> [{"last-activity", "Last Activity"}];
- mod_last_odbc -> []
+ case gen_mod:db_type(Server, mod_last) of
+ mnesia -> [{"last-activity", "Last Activity"}];
+ _ -> []
end.
us_to_list({User, Server}) ->
@@ -1735,10 +1729,9 @@ user_info(User, Server, Query, Lang) ->
UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
[User, Server, Lang]),
%% Code copied from list_given_users/5:
- ModLast = get_lastactivity_module(Server),
LastActivity = case ejabberd_sm:get_user_resources(User, Server) of
[] ->
- case ModLast:get_last_info(User, Server) of
+ case mod_last:get_last_info(User, Server) of
not_found ->
?T("Never");
{ok, Shift, _Status} ->