mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Merge SQL and Mnesia code into one module (EJAB-1560)
This commit is contained in:
parent
453e249de3
commit
437f68a9f3
10
src/acl.erl
10
src/acl.erl
@ -241,9 +241,9 @@ is_glob_match(String, Glob) ->
|
|||||||
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
|
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
|
||||||
|
|
||||||
loaded_shared_roster_module(Host) ->
|
loaded_shared_roster_module(Host) ->
|
||||||
case {gen_mod:is_loaded(Host, mod_shared_roster_odbc),
|
case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
|
||||||
gen_mod:is_loaded(Host, mod_shared_roster_ldap)} of
|
true ->
|
||||||
{true, _} -> mod_shared_roster_odbc;
|
mod_shared_roster_ldap;
|
||||||
{_, true} -> mod_shared_roster_ldap;
|
false ->
|
||||||
_ -> mod_shared_roster
|
mod_shared_roster
|
||||||
end.
|
end.
|
||||||
|
@ -78,16 +78,12 @@
|
|||||||
mod_irc,
|
mod_irc,
|
||||||
mod_irc_connection,
|
mod_irc_connection,
|
||||||
mod_last,
|
mod_last,
|
||||||
mod_last_odbc,
|
|
||||||
mod_muc,
|
mod_muc,
|
||||||
mod_muc_log,
|
mod_muc_log,
|
||||||
mod_muc_room,
|
mod_muc_room,
|
||||||
mod_offline,
|
mod_offline,
|
||||||
mod_offline_odbc,
|
|
||||||
mod_privacy,
|
mod_privacy,
|
||||||
mod_privacy_odbc,
|
|
||||||
mod_private,
|
mod_private,
|
||||||
mod_private_odbc,
|
|
||||||
mod_proxy65,
|
mod_proxy65,
|
||||||
mod_proxy65_lib,
|
mod_proxy65_lib,
|
||||||
mod_proxy65_service,
|
mod_proxy65_service,
|
||||||
@ -96,14 +92,12 @@
|
|||||||
mod_pubsub,
|
mod_pubsub,
|
||||||
mod_register,
|
mod_register,
|
||||||
mod_roster,
|
mod_roster,
|
||||||
mod_roster_odbc,
|
|
||||||
mod_service_log,
|
mod_service_log,
|
||||||
mod_shared_roster,
|
mod_shared_roster,
|
||||||
mod_stats,
|
mod_stats,
|
||||||
mod_time,
|
mod_time,
|
||||||
mod_vcard,
|
mod_vcard,
|
||||||
mod_vcard_ldap,
|
mod_vcard_ldap,
|
||||||
mod_vcard_odbc,
|
|
||||||
mod_version,
|
mod_version,
|
||||||
node_buddy,
|
node_buddy,
|
||||||
node_club,
|
node_club,
|
||||||
|
@ -373,13 +373,16 @@ import_dir(Path) ->
|
|||||||
%%%
|
%%%
|
||||||
|
|
||||||
delete_expired_messages() ->
|
delete_expired_messages() ->
|
||||||
{atomic, ok} = mod_offline:remove_expired_messages(),
|
lists:foreach(
|
||||||
ok.
|
fun(Host) ->
|
||||||
|
{atomic, ok} = mod_offline:remove_expired_messages(Host)
|
||||||
|
end, ?MYHOSTS).
|
||||||
|
|
||||||
delete_old_messages(Days) ->
|
delete_old_messages(Days) ->
|
||||||
{atomic, _} = mod_offline:remove_old_messages(Days),
|
lists:foreach(
|
||||||
ok.
|
fun(Host) ->
|
||||||
|
{atomic, _} = mod_offline:remove_old_messages(Days, Host)
|
||||||
|
end, ?MYHOSTS).
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Mnesia management
|
%%% Mnesia management
|
||||||
|
@ -293,27 +293,20 @@ get_last_access(User, Server) ->
|
|||||||
get_last_info(User, Server) ->
|
get_last_info(User, Server) ->
|
||||||
case get_mod_last_enabled(Server) of
|
case get_mod_last_enabled(Server) of
|
||||||
mod_last -> mod_last:get_last_info(User, Server);
|
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
|
no_mod_last -> mod_last_required
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (Server) -> mod_last | mod_last_odbc | no_mod_last
|
%% @spec (Server) -> mod_last | no_mod_last
|
||||||
get_mod_last_enabled(Server) ->
|
get_mod_last_enabled(Server) ->
|
||||||
ML = gen_mod:is_loaded(Server, mod_last),
|
case gen_mod:is_loaded(Server, mod_last) of
|
||||||
MLO = gen_mod:is_loaded(Server, mod_last_odbc),
|
true -> mod_last;
|
||||||
case {ML, MLO} of
|
false -> no_mod_last
|
||||||
{true, _} -> mod_last;
|
|
||||||
{false, true} -> mod_last_odbc;
|
|
||||||
{false, false} -> no_mod_last
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_mod_last_configured(Server) ->
|
get_mod_last_configured(Server) ->
|
||||||
ML = is_configured(Server, mod_last),
|
case is_configured(Server, mod_last) of
|
||||||
MLO = is_configured(Server, mod_last_odbc),
|
true -> mod_last;
|
||||||
case {ML, MLO} of
|
false -> no_mod_last
|
||||||
{true, _} -> mod_last;
|
|
||||||
{false, true} -> mod_last_odbc;
|
|
||||||
{false, false} -> no_mod_last
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
is_configured(Host, Module) ->
|
is_configured(Host, Module) ->
|
||||||
|
@ -467,6 +467,8 @@ process_host_term(Term, Host, State) ->
|
|||||||
State;
|
State;
|
||||||
{odbc_server, ODBC_server} ->
|
{odbc_server, ODBC_server} ->
|
||||||
add_option({odbc_server, Host}, ODBC_server, State);
|
add_option({odbc_server, Host}, ODBC_server, State);
|
||||||
|
{modules, Modules} ->
|
||||||
|
add_option({modules, Host}, replace_modules(Modules), State);
|
||||||
{Opt, Val} ->
|
{Opt, Val} ->
|
||||||
add_option({Opt, Host}, Val, State)
|
add_option({Opt, Host}, Val, State)
|
||||||
end.
|
end.
|
||||||
@ -610,3 +612,30 @@ is_file_readable(Path) ->
|
|||||||
{error, _Reason} ->
|
{error, _Reason} ->
|
||||||
false
|
false
|
||||||
end.
|
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).
|
||||||
|
@ -289,9 +289,10 @@ create_user(User,Password,Domain) ->
|
|||||||
|
|
||||||
populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
|
populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
|
||||||
io:format("Trying to add/update roster list...",[]),
|
io:format("Trying to add/update roster list...",[]),
|
||||||
case loaded_module(Domain,[mod_roster_odbc,mod_roster]) of
|
case loaded_module(Domain, mod_roster) of
|
||||||
{ok, M} ->
|
{ok, _DBType} ->
|
||||||
case M:set_items(User, Domain, exmpp_xml:xmlel_to_xmlelement(El)) of
|
case mod_roster:set_items(User, Domain,
|
||||||
|
exmpp_xml:xmlel_to_xmlelement(El)) of
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
io:format(" DONE.~n",[]),
|
io:format(" DONE.~n",[]),
|
||||||
ok;
|
ok;
|
||||||
@ -302,7 +303,7 @@ populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:roster'}) ->
|
|||||||
{error, not_found}
|
{error, not_found}
|
||||||
end;
|
end;
|
||||||
E -> io:format(" ERROR: ~p~n",[E]),
|
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)]),
|
[exmpp_xml:document_to_list(El)]),
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end;
|
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'}) ->
|
populate_user(User,Domain,El=#xmlel{name='vCard', ns='vcard-temp'}) ->
|
||||||
io:format("Trying to add/update vCards...",[]),
|
io:format("Trying to add/update vCards...",[]),
|
||||||
case loaded_module(Domain,[mod_vcard,mod_vcard_odbc]) of
|
case loaded_module(Domain, mod_vcard) of
|
||||||
{ok, M} -> FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
|
{ok, _} -> FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
|
||||||
IQ = iq_to_old_iq(#iq{type = set, payload = El}),
|
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} ->
|
{error,_Err} ->
|
||||||
io:format(" ERROR.~n",[]),
|
io:format(" ERROR.~n",[]),
|
||||||
?ERROR_MSG("Error processing vcard ~s : ~p ~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;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
io:format(" ERROR.~n",[]),
|
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)]),
|
[exmpp_xml:document_to_list(El)]),
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end;
|
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'}) ->
|
populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
|
||||||
io:format("Trying to add/update offline-messages...",[]),
|
io:format("Trying to add/update offline-messages...",[]),
|
||||||
case loaded_module(Domain, [mod_offline, mod_offline_odbc]) of
|
case loaded_module(Domain, mod_offline) of
|
||||||
{ok, M} ->
|
{ok, _DBType} ->
|
||||||
ok = exmpp_xml:foreach(
|
ok = exmpp_xml:foreach(
|
||||||
fun (_Element, {xmlcdata, _}) ->
|
fun (_Element, {xmlcdata, _}) ->
|
||||||
ok;
|
ok;
|
||||||
@ -367,11 +368,11 @@ populate_user(User,Domain,El=#xmlel{name='offline-messages'}) ->
|
|||||||
FullUser = jid_to_old_jid(exmpp_jid:make(User,
|
FullUser = jid_to_old_jid(exmpp_jid:make(User,
|
||||||
Domain)),
|
Domain)),
|
||||||
OldChild = exmpp_xml:xmlel_to_xmlelement(Child),
|
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",[]);
|
end, El), io:format(" DONE.~n",[]);
|
||||||
_ ->
|
_ ->
|
||||||
io:format(" ERROR.~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)]),
|
[exmpp_xml:document_to_list(El)]),
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end;
|
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'}) ->
|
populate_user(User,Domain,El=#xmlel{name='query', ns='jabber:iq:private'}) ->
|
||||||
io:format("Trying to add/update private storage...",[]),
|
io:format("Trying to add/update private storage...",[]),
|
||||||
case loaded_module(Domain,[mod_private_odbc,mod_private]) of
|
case loaded_module(Domain, mod_private) of
|
||||||
{ok, M} ->
|
{ok, _DBType} ->
|
||||||
FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
|
FullUser = jid_to_old_jid(exmpp_jid:make(User, Domain)),
|
||||||
IQ = iq_to_old_iq(#iq{type = set,
|
IQ = iq_to_old_iq(#iq{type = set,
|
||||||
ns = 'jabber:iq:private',
|
ns = 'jabber:iq:private',
|
||||||
kind = request,
|
kind = request,
|
||||||
iq_ns = 'jabberd:client',
|
iq_ns = 'jabberd:client',
|
||||||
payload = El}),
|
payload = El}),
|
||||||
case M:process_sm_iq(FullUser, FullUser, IQ ) of
|
case mod_private:process_sm_iq(FullUser, FullUser, IQ ) of
|
||||||
{error, _Err} ->
|
{error, _Err} ->
|
||||||
io:format(" ERROR.~n",[]),
|
io:format(" ERROR.~n",[]),
|
||||||
?ERROR_MSG("Error processing private storage ~s : ~p ~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;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
io:format(" ERROR.~n",[]),
|
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)]),
|
[exmpp_xml:document_to_list(El)]),
|
||||||
{error, not_found}
|
{error, not_found}
|
||||||
end;
|
end;
|
||||||
@ -415,13 +416,12 @@ populate_user(_User, _Domain, _El) ->
|
|||||||
%%%==================================
|
%%%==================================
|
||||||
%%%% Utilities
|
%%%% Utilities
|
||||||
|
|
||||||
loaded_module(Domain,Options) ->
|
loaded_module(Domain, Module) ->
|
||||||
LoadedModules = gen_mod:loaded_modules(Domain),
|
case gen_mod:is_loaded(Domain, Module) of
|
||||||
case lists:filter(fun(Module) ->
|
true ->
|
||||||
lists:member(Module, LoadedModules)
|
{ok, gen_mod:db_type(Domain, Module)};
|
||||||
end, Options) of
|
false ->
|
||||||
[M|_] -> {ok, M};
|
{error, not_found}
|
||||||
[] -> {error,not_found}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
jid_to_old_jid(Jid) ->
|
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()
|
%% @spec (InfoName::atom(), Username::string(), Host::string()) -> string()
|
||||||
extract_user_info(roster, Username, Host) ->
|
extract_user_info(roster, Username, Host) ->
|
||||||
case loaded_module(Host,[mod_roster_odbc,mod_roster]) of
|
case loaded_module(Host, mod_roster) of
|
||||||
{ok, M} ->
|
{ok, _DBType} ->
|
||||||
From = To = jlib:make_jid(Username, Host, ""),
|
From = To = jlib:make_jid(Username, Host, ""),
|
||||||
SubelGet = {xmlelement, "query", [{"xmlns",?NS_ROSTER}], []},
|
SubelGet = {xmlelement, "query", [{"xmlns",?NS_ROSTER}], []},
|
||||||
%%IQGet = #iq{type=get, xmlns=?NS_ROSTER, payload=SubelGet}, % this is for 3.0.0 version
|
%%IQGet = #iq{type=get, xmlns=?NS_ROSTER, payload=SubelGet}, % this is for 3.0.0 version
|
||||||
IQGet = {iq, "", get, ?NS_ROSTER, "" , SubelGet},
|
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
|
%%[El] = Res#iq.payload, % this is for 3.0.0 version
|
||||||
{iq, _, result, _, _, Els} = Res,
|
{iq, _, result, _, _, Els} = Res,
|
||||||
case Els of
|
case Els of
|
||||||
@ -592,8 +592,8 @@ extract_user_info(roster, Username, Host) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
extract_user_info(offline, Username, Host) ->
|
extract_user_info(offline, Username, Host) ->
|
||||||
case loaded_module(Host,[mod_offline,mod_offline_odbc]) of
|
case loaded_module(Host, mod_offline) of
|
||||||
{ok, mod_offline} ->
|
{ok, mnesia} ->
|
||||||
Els = mnesia_pop_offline_messages([], Username, Host),
|
Els = mnesia_pop_offline_messages([], Username, Host),
|
||||||
case Els of
|
case Els of
|
||||||
[] -> "";
|
[] -> "";
|
||||||
@ -601,30 +601,30 @@ extract_user_info(offline, Username, Host) ->
|
|||||||
OfEl = {xmlelement, "offline-messages", [], Els},
|
OfEl = {xmlelement, "offline-messages", [], Els},
|
||||||
exmpp_xml:document_to_list(OfEl)
|
exmpp_xml:document_to_list(OfEl)
|
||||||
end;
|
end;
|
||||||
{ok, mod_offline_odbc} ->
|
{ok, odbc} ->
|
||||||
"";
|
"";
|
||||||
_E ->
|
_E ->
|
||||||
""
|
""
|
||||||
end;
|
end;
|
||||||
|
|
||||||
extract_user_info(private, Username, Host) ->
|
extract_user_info(private, Username, Host) ->
|
||||||
case loaded_module(Host,[mod_private,mod_private_odbc]) of
|
case loaded_module(Host, mod_private) of
|
||||||
{ok, mod_private} ->
|
{ok, mnesia} ->
|
||||||
get_user_private_mnesia(Username, Host);
|
get_user_private_mnesia(Username, Host);
|
||||||
{ok, mod_private_odbc} ->
|
{ok, odbc} ->
|
||||||
"";
|
"";
|
||||||
_E ->
|
_E ->
|
||||||
""
|
""
|
||||||
end;
|
end;
|
||||||
|
|
||||||
extract_user_info(vcard, Username, Host) ->
|
extract_user_info(vcard, Username, Host) ->
|
||||||
case loaded_module(Host,[mod_vcard, mod_vcard_odbc, mod_vcard_odbc]) of
|
case loaded_module(Host, mod_vcard) of
|
||||||
{ok, M} ->
|
{ok, _DBType} ->
|
||||||
From = To = jlib:make_jid(Username, Host, ""),
|
From = To = jlib:make_jid(Username, Host, ""),
|
||||||
SubelGet = {xmlelement, "vCard", [{"xmlns",?NS_VCARD}], []},
|
SubelGet = {xmlelement, "vCard", [{"xmlns",?NS_VCARD}], []},
|
||||||
%%IQGet = #iq{type=get, xmlns=?NS_VCARD, payload=SubelGet}, % this is for 3.0.0 version
|
%%IQGet = #iq{type=get, xmlns=?NS_VCARD, payload=SubelGet}, % this is for 3.0.0 version
|
||||||
IQGet = {iq, "", get, ?NS_VCARD, "" , SubelGet},
|
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
|
%%[El] = Res#iq.payload, % this is for 3.0.0 version
|
||||||
{iq, _, result, _, _, Els} = Res,
|
{iq, _, result, _, _, Els} = Res,
|
||||||
case Els of
|
case Els of
|
||||||
|
@ -383,7 +383,7 @@ export_privacy(Server, Output) ->
|
|||||||
fun({Name, List}) ->
|
fun({Name, List}) ->
|
||||||
SName = ejabberd_odbc:escape(Name),
|
SName = ejabberd_odbc:escape(Name),
|
||||||
RItems = lists:map(
|
RItems = lists:map(
|
||||||
fun mod_privacy_odbc:item_to_raw/1,
|
fun mod_privacy:item_to_raw/1,
|
||||||
List),
|
List),
|
||||||
ID = integer_to_list(get_id()),
|
ID = integer_to_list(get_id()),
|
||||||
["delete from privacy_list "
|
["delete from privacy_list "
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
get_opt/2,
|
get_opt/2,
|
||||||
get_opt/3,
|
get_opt/3,
|
||||||
get_opt_host/3,
|
get_opt_host/3,
|
||||||
|
db_type/1,
|
||||||
|
db_type/2,
|
||||||
get_module_opt/4,
|
get_module_opt/4,
|
||||||
get_module_opt_host/3,
|
get_module_opt_host/3,
|
||||||
loaded_modules/1,
|
loaded_modules/1,
|
||||||
@ -192,6 +194,18 @@ get_opt_host(Host, Opts, Default) ->
|
|||||||
Val = get_opt(host, Opts, Default),
|
Val = get_opt(host, Opts, Default),
|
||||||
ejabberd_regexp:greplace(Val, "@HOST@", Host).
|
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) ->
|
loaded_modules(Host) ->
|
||||||
ets:select(ejabberd_modules,
|
ets:select(ejabberd_modules,
|
||||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
||||||
|
@ -116,54 +116,28 @@ xdb_data(_User, _Server, {xmlcdata, _CData}) ->
|
|||||||
ok;
|
ok;
|
||||||
xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
|
xdb_data(User, Server, {xmlelement, _Name, Attrs, _Els} = El) ->
|
||||||
From = jlib:make_jid(User, Server, ""),
|
From = jlib:make_jid(User, Server, ""),
|
||||||
LServer = jlib:nameprep(Server),
|
|
||||||
case xml:get_attr_s("xmlns", Attrs) of
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
?NS_AUTH ->
|
?NS_AUTH ->
|
||||||
Password = xml:get_tag_cdata(El),
|
Password = xml:get_tag_cdata(El),
|
||||||
ejabberd_auth:set_password(User, Server, Password),
|
ejabberd_auth:set_password(User, Server, Password),
|
||||||
ok;
|
ok;
|
||||||
?NS_ROSTER ->
|
?NS_ROSTER ->
|
||||||
case lists:member(mod_roster_odbc,
|
catch mod_roster:set_items(User, Server, El),
|
||||||
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,
|
|
||||||
ok;
|
ok;
|
||||||
?NS_LAST ->
|
?NS_LAST ->
|
||||||
TimeStamp = xml:get_attr_s("last", Attrs),
|
TimeStamp = xml:get_attr_s("last", Attrs),
|
||||||
Status = xml:get_tag_cdata(El),
|
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(
|
catch mod_last:store_last_info(
|
||||||
User,
|
User,
|
||||||
Server,
|
Server,
|
||||||
list_to_integer(TimeStamp),
|
list_to_integer(TimeStamp),
|
||||||
Status)
|
Status),
|
||||||
end,
|
|
||||||
ok;
|
ok;
|
||||||
?NS_VCARD ->
|
?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(
|
catch mod_vcard:process_sm_iq(
|
||||||
From,
|
From,
|
||||||
jlib:make_jid("", Server, ""),
|
jlib:make_jid("", Server, ""),
|
||||||
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El})
|
#iq{type = set, xmlns = ?NS_VCARD, sub_el = El}),
|
||||||
end,
|
|
||||||
ok;
|
ok;
|
||||||
"jabber:x:offline" ->
|
"jabber:x:offline" ->
|
||||||
process_offline(Server, From, El),
|
process_offline(Server, From, El),
|
||||||
|
@ -56,12 +56,21 @@
|
|||||||
-define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
|
-define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
|
||||||
tokenize(Node) -> string:tokens(Node, "/#").
|
tokenize(Node) -> string:tokens(Node, "/#").
|
||||||
|
|
||||||
start(Host, _Opts) ->
|
start(Host, Opts) ->
|
||||||
mnesia:create_table(motd, [{disc_copies, [node()]},
|
case gen_mod:db_type(Opts) of
|
||||||
{attributes, record_info(fields, motd)}]),
|
mnesia ->
|
||||||
mnesia:create_table(motd_users, [{disc_copies, [node()]},
|
mnesia:create_table(motd,
|
||||||
{attributes, record_info(fields, motd_users)}]),
|
[{disc_copies, [node()]},
|
||||||
update_tables(),
|
{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,
|
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||||
?MODULE, announce, 50),
|
?MODULE, announce, 50),
|
||||||
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
|
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
|
||||||
@ -743,16 +752,33 @@ announce_all_hosts_motd(From, To, Packet) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
announce_motd(Host, Packet) ->
|
announce_motd(Host, Packet) ->
|
||||||
announce_motd_update(Host, Packet),
|
LServer = jlib:nameprep(Host),
|
||||||
Sessions = ejabberd_sm:get_vh_session_list(Host),
|
announce_motd_update(LServer, Packet),
|
||||||
announce_online1(Sessions, Host, 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() ->
|
F = fun() ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({U, S, _R}) ->
|
fun({U, S, _R}) ->
|
||||||
mnesia:write(#motd_users{us = {U, S}})
|
mnesia:write(#motd_users{us = {U, S}})
|
||||||
end, Sessions)
|
end, Sessions)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
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) ->
|
announce_motd_update(From, To, Packet) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
@ -778,10 +804,23 @@ announce_all_hosts_motd_update(From, To, Packet) ->
|
|||||||
|
|
||||||
announce_motd_update(LServer, Packet) ->
|
announce_motd_update(LServer, Packet) ->
|
||||||
announce_motd_delete(LServer),
|
announce_motd_delete(LServer),
|
||||||
|
case gen_mod:db_type(LServer, ?MODULE) of
|
||||||
|
mnesia ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(#motd{server = LServer, packet = Packet})
|
mnesia:write(#motd{server = LServer, packet = Packet})
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
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) ->
|
announce_motd_delete(From, To, Packet) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
@ -806,6 +845,8 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
announce_motd_delete(LServer) ->
|
announce_motd_delete(LServer) ->
|
||||||
|
case gen_mod:db_type(LServer, ?MODULE) of
|
||||||
|
mnesia ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({motd, LServer}),
|
mnesia:delete({motd, LServer}),
|
||||||
mnesia:write_lock_table(motd_users),
|
mnesia:write_lock_table(motd_users),
|
||||||
@ -818,9 +859,18 @@ announce_motd_delete(LServer) ->
|
|||||||
mnesia:delete({motd_users, US})
|
mnesia:delete({motd_users, US})
|
||||||
end, Users)
|
end, Users)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
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
|
case catch mnesia:dirty_read({motd, LServer}) of
|
||||||
[#motd{packet = Packet}] ->
|
[#motd{packet = Packet}] ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
@ -837,17 +887,71 @@ send_motd(#jid{luser = LUser, lserver = LServer} = JID) ->
|
|||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
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) ->
|
get_stored_motd(LServer) ->
|
||||||
case catch mnesia:dirty_read({motd, LServer}) of
|
case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
[#motd{packet = Packet}] ->
|
{ok, Packet} ->
|
||||||
{xml:get_subtag_cdata(Packet, "subject"),
|
{xml:get_subtag_cdata(Packet, "subject"),
|
||||||
xml:get_subtag_cdata(Packet, "body")};
|
xml:get_subtag_cdata(Packet, "body")};
|
||||||
_ ->
|
error ->
|
||||||
{"", ""}
|
{"", ""}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_stored_motd_packet(LServer, mnesia) ->
|
||||||
|
case catch mnesia:dirty_read({motd, LServer}) of
|
||||||
|
[#motd{packet = Packet}] ->
|
||||||
|
{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
|
%% This function is similar to others, but doesn't perform any ACL verification
|
||||||
send_announcement_to_all(Host, SubjectS, BodyS) ->
|
send_announcement_to_all(Host, SubjectS, BodyS) ->
|
||||||
SubjectEls = if SubjectS /= [] ->
|
SubjectEls = if SubjectS /= [] ->
|
||||||
|
@ -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).
|
|
@ -92,16 +92,6 @@ process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
|
|||||||
process_iq_set(Acc, _, _, _) ->
|
process_iq_set(Acc, _, _, _) ->
|
||||||
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) ->
|
list_to_blocklist_jids([], JIDs) ->
|
||||||
JIDs;
|
JIDs;
|
||||||
|
|
||||||
@ -148,6 +138,35 @@ parse_blocklist_items([_ | Els], JIDs) ->
|
|||||||
parse_blocklist_items(Els, JIDs).
|
parse_blocklist_items(Els, JIDs).
|
||||||
|
|
||||||
process_blocklist_block(LUser, LServer, 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 =
|
F =
|
||||||
fun() ->
|
fun() ->
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||||
@ -173,71 +192,60 @@ process_blocklist_block(LUser, LServer, JIDs) ->
|
|||||||
List = []
|
List = []
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
NewList = Filter(List),
|
||||||
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),
|
|
||||||
NewLists = [{NewDefault, NewList} | NewLists1],
|
NewLists = [{NewDefault, NewList} | NewLists1],
|
||||||
mnesia:write(P#privacy{default = NewDefault,
|
mnesia:write(P#privacy{default = NewDefault,
|
||||||
lists = NewLists}),
|
lists = NewLists}),
|
||||||
{ok, NewDefault, NewList}
|
{ok, NewDefault, NewList}
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
mnesia:transaction(F);
|
||||||
{atomic, {error, _} = Error} ->
|
process_blocklist_block(LUser, LServer, Filter, odbc) ->
|
||||||
Error;
|
F = fun() ->
|
||||||
{atomic, {ok, Default, List}} ->
|
Default =
|
||||||
UserList = make_userlist(Default, List),
|
case mod_privacy:sql_get_default_privacy_list_t(LUser) of
|
||||||
broadcast_list_update(LUser, LServer, Default, UserList),
|
{selected, ["name"], []} ->
|
||||||
broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
|
Name = "Blocked contacts",
|
||||||
{result, [], UserList};
|
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);
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
List = []
|
||||||
end.
|
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) ->
|
process_blocklist_unblock_all(LUser, LServer) ->
|
||||||
F =
|
Filter = fun(List) ->
|
||||||
fun() ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
% No lists, nothing to unblock
|
|
||||||
ok;
|
|
||||||
[#privacy{default = Default,
|
|
||||||
lists = Lists} = P] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
% Default list, remove all deny items
|
|
||||||
NewList =
|
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun(#listitem{action = A}) ->
|
fun(#listitem{action = A}) ->
|
||||||
A =/= deny
|
A =/= deny
|
||||||
end, List),
|
end, List)
|
||||||
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{lists = NewLists}),
|
|
||||||
|
|
||||||
{ok, Default, NewList};
|
|
||||||
false ->
|
|
||||||
% No default list, nothing to unblock
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case process_blocklist_unblock_all(
|
||||||
{atomic, {error, _} = Error} ->
|
LUser, LServer, Filter, gen_mod:db_type(LServer, mod_privacy)) of
|
||||||
Error;
|
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
{atomic, {ok, Default, List}} ->
|
{atomic, {ok, Default, List}} ->
|
||||||
@ -249,7 +257,7 @@ process_blocklist_unblock_all(LUser, LServer) ->
|
|||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_blocklist_unblock(LUser, LServer, JIDs) ->
|
process_blocklist_unblock_all(LUser, LServer, Filter, mnesia) ->
|
||||||
F =
|
F =
|
||||||
fun() ->
|
fun() ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
@ -260,17 +268,8 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
|
|||||||
lists = Lists} = P] ->
|
lists = Lists} = P] ->
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
{value, {_, List}} ->
|
{value, {_, List}} ->
|
||||||
% Default list, remove matching deny items
|
% Default list, remove all deny items
|
||||||
NewList =
|
NewList = Filter(List),
|
||||||
lists:filter(
|
|
||||||
fun(#listitem{action = deny,
|
|
||||||
type = jid,
|
|
||||||
value = JID}) ->
|
|
||||||
not(lists:member(JID, JIDs));
|
|
||||||
(_) ->
|
|
||||||
true
|
|
||||||
end, List),
|
|
||||||
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
NewLists1 = lists:keydelete(Default, 1, Lists),
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
NewLists = [{Default, NewList} | NewLists1],
|
||||||
mnesia:write(P#privacy{lists = NewLists}),
|
mnesia:write(P#privacy{lists = NewLists}),
|
||||||
@ -282,9 +281,55 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
mnesia:transaction(F);
|
||||||
{atomic, {error, _} = Error} ->
|
process_blocklist_unblock_all(LUser, LServer, Filter, odbc) ->
|
||||||
Error;
|
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} ->
|
{atomic, ok} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
{atomic, {ok, Default, List}} ->
|
{atomic, {ok, Default, List}} ->
|
||||||
@ -296,8 +341,68 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
|
|||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
process_blocklist_unblock(LUser, LServer, Filter, mnesia) ->
|
||||||
|
F =
|
||||||
|
fun() ->
|
||||||
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
|
[] ->
|
||||||
|
% No lists, nothing to unblock
|
||||||
|
ok;
|
||||||
|
[#privacy{default = Default,
|
||||||
|
lists = Lists} = P] ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
% Default list, remove matching deny items
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewLists1 = lists:keydelete(Default, 1, Lists),
|
||||||
|
NewLists = [{Default, NewList} | NewLists1],
|
||||||
|
mnesia:write(P#privacy{lists = NewLists}),
|
||||||
|
|
||||||
|
{ok, Default, NewList};
|
||||||
|
false ->
|
||||||
|
% No default list, nothing to unblock
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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) ->
|
make_userlist(Name, List) ->
|
||||||
NeedDb = is_list_needdb(List),
|
NeedDb = mod_privacy:is_list_needdb(List),
|
||||||
#userlist{name = Name, list = List, needdb = NeedDb}.
|
#userlist{name = Name, list = List, needdb = NeedDb}.
|
||||||
|
|
||||||
broadcast_list_update(LUser, LServer, Name, UserList) ->
|
broadcast_list_update(LUser, LServer, Name, UserList) ->
|
||||||
@ -315,14 +420,11 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
|
|||||||
[{blocking, Event}]}).
|
[{blocking, Event}]}).
|
||||||
|
|
||||||
process_blocklist_get(LUser, LServer) ->
|
process_blocklist_get(LUser, LServer) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case process_blocklist_get(
|
||||||
{'EXIT', _Reason} ->
|
LUser, LServer, gen_mod:db_type(LServer, mod_privacy)) of
|
||||||
|
error ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
[] ->
|
List ->
|
||||||
{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, []),
|
JIDs = list_to_blocklist_jids(List, []),
|
||||||
Items = lists:map(
|
Items = lists:map(
|
||||||
fun(JID) ->
|
fun(JID) ->
|
||||||
@ -332,8 +434,38 @@ process_blocklist_get(LUser, LServer) ->
|
|||||||
end, JIDs),
|
end, JIDs),
|
||||||
{result,
|
{result,
|
||||||
[{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
|
[{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
|
||||||
Items}]};
|
Items}]}
|
||||||
_ ->
|
end.
|
||||||
{result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]}
|
|
||||||
end
|
process_blocklist_get(LUser, LServer, mnesia) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
error;
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
List;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
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.
|
end.
|
||||||
|
@ -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.
|
|
@ -1782,15 +1782,13 @@ stop_node(From, Host, ENode, Action, XData) ->
|
|||||||
|
|
||||||
|
|
||||||
get_last_info(User, Server) ->
|
get_last_info(User, Server) ->
|
||||||
ML = lists:member(mod_last, gen_mod:loaded_modules(Server)),
|
case gen_mod:is_loaded(Server, mod_last) of
|
||||||
MLO = lists:member(mod_last_odbc, gen_mod:loaded_modules(Server)),
|
true ->
|
||||||
case {ML, MLO} of
|
mod_last:get_last_info(User, Server);
|
||||||
{true, _} -> mod_last:get_last_info(User, Server);
|
false ->
|
||||||
{false, true} -> mod_last_odbc:get_last_info(User, Server);
|
not_found
|
||||||
{false, false} -> not_found
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
adhoc_sm_commands(_Acc, From,
|
adhoc_sm_commands(_Acc, From,
|
||||||
|
@ -4,7 +4,7 @@ include ..\Makefile.inc
|
|||||||
EFLAGS = -I .. -pz ..
|
EFLAGS = -I .. -pz ..
|
||||||
|
|
||||||
OUTDIR = ..
|
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
|
SOURCE = iconv_erl.c
|
||||||
OBJECT = iconv_erl.o
|
OBJECT = iconv_erl.o
|
||||||
@ -25,9 +25,6 @@ $(OUTDIR)\iconv.beam : iconv.erl
|
|||||||
$(OUTDIR)\mod_irc.beam : mod_irc.erl
|
$(OUTDIR)\mod_irc.beam : mod_irc.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) 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
|
$(OUTDIR)\mod_irc_connection.beam : mod_irc_connection.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl
|
||||||
|
|
||||||
|
@ -98,11 +98,17 @@ stop(Host) ->
|
|||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
iconv:start(),
|
iconv:start(),
|
||||||
|
MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(irc_custom,
|
mnesia:create_table(irc_custom,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, irc_custom)}]),
|
{attributes,
|
||||||
MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
|
record_info(fields, irc_custom)}]),
|
||||||
update_table(MyHost),
|
update_table(MyHost);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
Access = gen_mod:get_opt(access, Opts, all),
|
Access = gen_mod:get_opt(access, Opts, all),
|
||||||
catch ets:new(irc_connection, [named_table,
|
catch ets:new(irc_connection, [named_table,
|
||||||
public,
|
public,
|
||||||
@ -218,7 +224,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
|
|||||||
Info = ejabberd_hooks:run_fold(
|
Info = ejabberd_hooks:run_fold(
|
||||||
disco_info, ServerHost, [],
|
disco_info, ServerHost, [],
|
||||||
[ServerHost, ?MODULE, "", ""]),
|
[ServerHost, ?MODULE, "", ""]),
|
||||||
case iq_disco(Node, Lang) of
|
case iq_disco(ServerHost, Node, Lang) of
|
||||||
[] ->
|
[] ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
@ -263,7 +269,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
|
|||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
[{"xmlns", XMLNS},
|
[{"xmlns", XMLNS},
|
||||||
{"node", Node}],
|
{"node", Node}],
|
||||||
command_items(Host, Lang)}]},
|
command_items(ServerHost,
|
||||||
|
Host, Lang)}]},
|
||||||
Res = jlib:iq_to_xml(ResIQ);
|
Res = jlib:iq_to_xml(ResIQ);
|
||||||
_ ->
|
_ ->
|
||||||
Res = jlib:make_error_reply(
|
Res = jlib:make_error_reply(
|
||||||
@ -273,7 +280,7 @@ do_route1(Host, ServerHost, From, To, Packet) ->
|
|||||||
From,
|
From,
|
||||||
Res);
|
Res);
|
||||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
#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,
|
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||||
lang = Lang} = IQ ->
|
lang = Lang} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
@ -287,7 +294,8 @@ do_route1(Host, ServerHost, From, To, Packet) ->
|
|||||||
#iq{type = set, xmlns = ?NS_COMMANDS,
|
#iq{type = set, xmlns = ?NS_COMMANDS,
|
||||||
lang = _Lang, sub_el = SubEl} = IQ ->
|
lang = _Lang, sub_el = SubEl} = IQ ->
|
||||||
Request = adhoc:parse_request(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}} ->
|
{value, {_, _, Function}} ->
|
||||||
case catch Function(From, To, Request) of
|
case catch Function(From, To, Request) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
@ -394,7 +402,7 @@ closed_connection(Host, From, Server) ->
|
|||||||
ets:delete(irc_connection, {From, Server, Host}).
|
ets:delete(irc_connection, {From, Server, Host}).
|
||||||
|
|
||||||
|
|
||||||
iq_disco([], Lang) ->
|
iq_disco(_ServerHost, [], Lang) ->
|
||||||
[{xmlelement, "identity",
|
[{xmlelement, "identity",
|
||||||
[{"category", "conference"},
|
[{"category", "conference"},
|
||||||
{"type", "irc"},
|
{"type", "irc"},
|
||||||
@ -404,8 +412,8 @@ iq_disco([], Lang) ->
|
|||||||
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
||||||
{xmlelement, "feature", [{"var", ?NS_VCARD}], []},
|
{xmlelement, "feature", [{"var", ?NS_VCARD}], []},
|
||||||
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
|
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
|
||||||
iq_disco(Node, Lang) ->
|
iq_disco(ServerHost, Node, Lang) ->
|
||||||
case lists:keysearch(Node, 1, commands()) of
|
case lists:keysearch(Node, 1, commands(ServerHost)) of
|
||||||
{value, {_, Name, _}} ->
|
{value, {_, Name, _}} ->
|
||||||
[{xmlelement, "identity",
|
[{xmlelement, "identity",
|
||||||
[{"category", "automation"},
|
[{"category", "automation"},
|
||||||
@ -428,20 +436,23 @@ iq_get_vcard(Lang) ->
|
|||||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
|
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
|
||||||
"\nCopyright (c) 2003-2012 ProcessOne"}]}].
|
"\nCopyright (c) 2003-2012 ProcessOne"}]}].
|
||||||
|
|
||||||
command_items(Host, Lang) ->
|
command_items(ServerHost, Host, Lang) ->
|
||||||
lists:map(fun({Node, Name, _Function})
|
lists:map(fun({Node, Name, _Function})
|
||||||
-> {xmlelement, "item",
|
-> {xmlelement, "item",
|
||||||
[{"jid", Host},
|
[{"jid", Host},
|
||||||
{"node", Node},
|
{"node", Node},
|
||||||
{"name", translate:translate(Lang, Name)}], []}
|
{"name", translate:translate(Lang, Name)}], []}
|
||||||
end, commands()).
|
end, commands(ServerHost)).
|
||||||
|
|
||||||
commands() ->
|
commands(ServerHost) ->
|
||||||
[{"join", "Join channel", fun adhoc_join/3},
|
[{"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) ->
|
process_register(ServerHost, Host, From, To, #iq{} = IQ) ->
|
||||||
case catch process_irc_register(Host, From, To, IQ) of
|
case catch process_irc_register(ServerHost, Host, From, To, IQ) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
?ERROR_MSG("~p", [Reason]);
|
?ERROR_MSG("~p", [Reason]);
|
||||||
ResIQ ->
|
ResIQ ->
|
||||||
@ -471,7 +482,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
|||||||
find_xdata_el1([_ | Els]) ->
|
find_xdata_el1([_ | 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,
|
#iq{type = Type, xmlns = XMLNS,
|
||||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
@ -497,7 +508,8 @@ process_irc_register(Host, From, _To,
|
|||||||
xml:get_tag_attr_s("node", SubEl),
|
xml:get_tag_attr_s("node", SubEl),
|
||||||
"/"),
|
"/"),
|
||||||
case set_form(
|
case set_form(
|
||||||
Host, From, Node, Lang, XData) of
|
ServerHost, Host, From,
|
||||||
|
Node, Lang, XData) of
|
||||||
{result, Res} ->
|
{result, Res} ->
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
@ -517,7 +529,7 @@ process_irc_register(Host, From, _To,
|
|||||||
get ->
|
get ->
|
||||||
Node =
|
Node =
|
||||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
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} ->
|
{result, Res} ->
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
@ -530,20 +542,49 @@ process_irc_register(Host, From, _To,
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_data(ServerHost, Host, From) ->
|
||||||
|
LServer = jlib:nameprep(ServerHost),
|
||||||
|
get_data(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
get_data(_LServer, Host, From, mnesia) ->
|
||||||
get_form(Host, From, [], Lang) ->
|
#jid{luser = LUser, lserver = LServer} = From,
|
||||||
#jid{user = User, server = Server,
|
|
||||||
luser = LUser, lserver = LServer} = From,
|
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
DefaultEncoding = get_default_encoding(Host),
|
|
||||||
Customs =
|
|
||||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
error;
|
||||||
[] ->
|
[] ->
|
||||||
{User, []};
|
empty;
|
||||||
[#irc_custom{data = Data}] ->
|
[#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 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(username, Data),
|
||||||
xml:get_attr_s(connections_params, Data)}
|
xml:get_attr_s(connections_params, Data)}
|
||||||
end,
|
end,
|
||||||
@ -614,15 +655,41 @@ get_form(Host, From, [], Lang) ->
|
|||||||
]}]}
|
]}]}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
get_form(_Host, _, _, _Lang) ->
|
get_form(_ServerHost, _Host, _, _, _Lang) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{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_data(_LServer, Host, From, Data, mnesia) ->
|
||||||
set_form(Host, From, [], _Lang, XData) ->
|
|
||||||
{LUser, LServer, _} = jlib:jid_tolower(From),
|
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||||
US = {LUser, LServer},
|
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),
|
case {lists:keysearch("username", 1, XData),
|
||||||
lists:keysearch("connections_params", 1, XData)} of
|
lists:keysearch("connections_params", 1, XData)} of
|
||||||
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
|
{{value, {_, [Username]}}, {value, {_, Strings}}} ->
|
||||||
@ -633,17 +700,11 @@ set_form(Host, From, [], _Lang, XData) ->
|
|||||||
{ok, Tokens, _} ->
|
{ok, Tokens, _} ->
|
||||||
case erl_parse:parse_term(Tokens) of
|
case erl_parse:parse_term(Tokens) of
|
||||||
{ok, ConnectionsParams} ->
|
{ok, ConnectionsParams} ->
|
||||||
case mnesia:transaction(
|
case set_data(ServerHost, Host, From,
|
||||||
fun() ->
|
|
||||||
mnesia:write(
|
|
||||||
#irc_custom{us_host =
|
|
||||||
{US, Host},
|
|
||||||
data =
|
|
||||||
[{username,
|
[{username,
|
||||||
Username},
|
Username},
|
||||||
{connections_params,
|
{connections_params,
|
||||||
ConnectionsParams}]})
|
ConnectionsParams}]) of
|
||||||
end) of
|
|
||||||
{atomic, _} ->
|
{atomic, _} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
_ ->
|
_ ->
|
||||||
@ -660,7 +721,7 @@ set_form(Host, From, [], _Lang, XData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
set_form(_Host, _, _, _Lang, _XData) ->
|
set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
@ -679,16 +740,14 @@ get_default_encoding(ServerHost) ->
|
|||||||
Result.
|
Result.
|
||||||
|
|
||||||
get_connection_params(Host, ServerHost, From, IRCServer) ->
|
get_connection_params(Host, ServerHost, From, IRCServer) ->
|
||||||
#jid{user = User, server = _Server,
|
#jid{user = User, server = _Server} = From,
|
||||||
luser = LUser, lserver = LServer} = From,
|
|
||||||
US = {LUser, LServer},
|
|
||||||
DefaultEncoding = get_default_encoding(ServerHost),
|
DefaultEncoding = get_default_encoding(ServerHost),
|
||||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
case get_data(ServerHost, Host, From) of
|
||||||
{'EXIT', _Reason} ->
|
error ->
|
||||||
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
|
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
|
||||||
[] ->
|
empty ->
|
||||||
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
|
{User, DefaultEncoding, ?DEFAULT_IRC_PORT, ""};
|
||||||
[#irc_custom{data = Data}] ->
|
Data ->
|
||||||
Username = xml:get_attr_s(username, Data),
|
Username = xml:get_attr_s(username, Data),
|
||||||
{NewUsername, NewEncoding, NewPort, NewPassword} =
|
{NewUsername, NewEncoding, NewPort, NewPassword} =
|
||||||
case lists:keysearch(IRCServer, 1, xml:get_attr_s(connections_params, Data)) of
|
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
|
||||||
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:produce_response(Request,
|
||||||
#adhoc_response{status = canceled});
|
#adhoc_response{status = canceled});
|
||||||
adhoc_register(From, To, #adhoc_request{lang = Lang,
|
adhoc_register(ServerHost, From, To, #adhoc_request{lang = Lang,
|
||||||
node = _Node,
|
node = _Node,
|
||||||
xdata = XData,
|
xdata = XData,
|
||||||
action = Action} = Request) ->
|
action = Action} = Request) ->
|
||||||
#jid{user = User, luser = LUser, lserver = LServer} = From,
|
#jid{user = User} = From,
|
||||||
#jid{lserver = Host} = To,
|
#jid{lserver = Host} = To,
|
||||||
US = {LUser, LServer},
|
|
||||||
%% Generate form for setting username and encodings. If the user
|
%% Generate form for setting username and encodings. If the user
|
||||||
%% hasn't begun to fill out the form, generate an initial form
|
%% hasn't begun to fill out the form, generate an initial form
|
||||||
%% based on current values.
|
%% based on current values.
|
||||||
if XData == false ->
|
if XData == false ->
|
||||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
case get_data(ServerHost, Host, From) of
|
||||||
{'EXIT', _Reason} ->
|
error ->
|
||||||
Username = User,
|
Username = User,
|
||||||
ConnectionsParams = [];
|
ConnectionsParams = [];
|
||||||
[] ->
|
empty ->
|
||||||
Username = User,
|
Username = User,
|
||||||
ConnectionsParams = [];
|
ConnectionsParams = [];
|
||||||
[#irc_custom{data = Data}] ->
|
Data ->
|
||||||
Username = xml:get_attr_s(username, Data),
|
Username = xml:get_attr_s(username, Data),
|
||||||
ConnectionsParams = xml:get_attr_s(connections_params, Data)
|
ConnectionsParams = xml:get_attr_s(connections_params, Data)
|
||||||
end,
|
end,
|
||||||
@ -832,17 +890,11 @@ adhoc_register(From, To, #adhoc_request{lang = Lang,
|
|||||||
if Error /= false ->
|
if Error /= false ->
|
||||||
Error;
|
Error;
|
||||||
Action == "complete" ->
|
Action == "complete" ->
|
||||||
case mnesia:transaction(
|
case set_data(ServerHost, Host, From,
|
||||||
fun () ->
|
|
||||||
mnesia:write(
|
|
||||||
#irc_custom{us_host =
|
|
||||||
{US, Host},
|
|
||||||
data =
|
|
||||||
[{username,
|
[{username,
|
||||||
Username},
|
Username},
|
||||||
{connections_params,
|
{connections_params,
|
||||||
ConnectionsParams}]})
|
ConnectionsParams}]) of
|
||||||
end) of
|
|
||||||
{atomic, _} ->
|
{atomic, _} ->
|
||||||
adhoc:produce_response(Request, #adhoc_response{status = completed});
|
adhoc:produce_response(Request, #adhoc_response{status = completed});
|
||||||
_ ->
|
_ ->
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -47,10 +47,16 @@
|
|||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(last_activity,
|
mnesia:create_table(last_activity,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, last_activity)}]),
|
{attributes,
|
||||||
update_table(),
|
record_info(fields, last_activity)}]),
|
||||||
|
update_table();
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
|
||||||
?MODULE, process_local_iq, IQDisc),
|
?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
|
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()) ->
|
%% @spec (LUser::string(), LServer::string()) ->
|
||||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
||||||
get_last(LUser, LServer) ->
|
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
|
case catch mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
{error, Reason};
|
{error, Reason};
|
||||||
@ -152,6 +161,21 @@ get_last(LUser, LServer) ->
|
|||||||
not_found;
|
not_found;
|
||||||
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
[#last_activity{timestamp = TimeStamp, status = Status}] ->
|
||||||
{ok, TimeStamp, 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.
|
end.
|
||||||
|
|
||||||
get_last_iq(IQ, SubEl, LUser, LServer) ->
|
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) ->
|
store_last_info(User, Server, TimeStamp, Status) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
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},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(#last_activity{us = US,
|
mnesia:write(#last_activity{us = US,
|
||||||
timestamp = TimeStamp,
|
timestamp = TimeStamp,
|
||||||
status = Status})
|
status = Status})
|
||||||
end,
|
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()) ->
|
%% @spec (LUser::string(), LServer::string()) ->
|
||||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found
|
%% {ok, TimeStamp::integer(), Status::string()} | not_found
|
||||||
@ -207,12 +240,18 @@ get_last_info(LUser, LServer) ->
|
|||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||||
|
remove_user(LUser, LServer, DBType).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({last_activity, US})
|
mnesia:delete({last_activity, US})
|
||||||
end,
|
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() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, last_activity),
|
Fields = record_info(fields, last_activity),
|
||||||
|
@ -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).
|
|
@ -4,7 +4,7 @@ include ..\Makefile.inc
|
|||||||
EFLAGS = -I .. -pz ..
|
EFLAGS = -I .. -pz ..
|
||||||
|
|
||||||
OUTDIR = ..
|
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)
|
ALL : $(BEAMS)
|
||||||
|
|
||||||
@ -14,9 +14,6 @@ CLEAN :
|
|||||||
$(OUTDIR)\mod_muc.beam : mod_muc.erl
|
$(OUTDIR)\mod_muc.beam : mod_muc.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) 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
|
$(OUTDIR)\mod_muc_log.beam : mod_muc_log.erl
|
||||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_log.erl
|
erlc -W $(EFLAGS) -o $(OUTDIR) mod_muc_log.erl
|
||||||
|
|
||||||
|
@ -111,26 +111,70 @@ create_room(Host, Name, From, Nick, Opts) ->
|
|||||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
gen_server:call(Proc, {create, Name, From, Nick, Opts}).
|
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() ->
|
F = fun() ->
|
||||||
mnesia:write(#muc_room{name_host = {Name, Host},
|
mnesia:write(#muc_room{name_host = {Name, Host},
|
||||||
opts = Opts})
|
opts = Opts})
|
||||||
end,
|
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
|
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
|
||||||
[#muc_room{opts = Opts}] ->
|
[#muc_room{opts = Opts}] ->
|
||||||
Opts;
|
Opts;
|
||||||
_ ->
|
_ ->
|
||||||
error
|
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.
|
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() ->
|
F = fun() ->
|
||||||
mnesia:delete({muc_room, {Name, Host}})
|
mnesia:delete({muc_room, {Name, Host}})
|
||||||
end,
|
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) ->
|
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
|
||||||
Rsm = jlib:rsm_decode(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, "") ->
|
can_use_nick(_ServerHost, _Host, _JID, "") ->
|
||||||
false;
|
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),
|
{LUser, LServer, _} = jlib:jid_tolower(JID),
|
||||||
LUS = {LUser, LServer},
|
LUS = {LUser, LServer},
|
||||||
case catch mnesia:dirty_select(
|
case catch mnesia:dirty_select(
|
||||||
@ -160,6 +208,21 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
|
|||||||
true;
|
true;
|
||||||
[#muc_registered{us_host = {U, _Host}}] ->
|
[#muc_registered{us_host = {U, _Host}}] ->
|
||||||
U == LUS
|
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.
|
end.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
@ -174,21 +237,28 @@ can_use_nick(_ServerHost, Host, JID, Nick) ->
|
|||||||
%% Description: Initiates the server
|
%% Description: Initiates the server
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
|
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,
|
mnesia:create_table(muc_room,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, muc_room)}]),
|
{attributes,
|
||||||
|
record_info(fields, muc_room)}]),
|
||||||
mnesia:create_table(muc_registered,
|
mnesia:create_table(muc_registered,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, muc_registered)}]),
|
{attributes,
|
||||||
|
record_info(fields, muc_registered)}]),
|
||||||
|
mnesia:add_table_index(muc_registered, nick);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
mnesia:create_table(muc_online_room,
|
mnesia:create_table(muc_online_room,
|
||||||
[{ram_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, muc_online_room)}]),
|
{attributes, record_info(fields, muc_online_room)}]),
|
||||||
mnesia:add_table_copy(muc_online_room, node(), ram_copies),
|
mnesia:add_table_copy(muc_online_room, node(), ram_copies),
|
||||||
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
|
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),
|
clean_table_from_bad_node(node(), MyHost),
|
||||||
mnesia:add_table_index(muc_registered, nick),
|
|
||||||
mnesia:subscribe(system),
|
mnesia:subscribe(system),
|
||||||
Access = gen_mod:get_opt(access, Opts, all),
|
Access = gen_mod:get_opt(access, Opts, all),
|
||||||
AccessCreate = gen_mod:get_opt(access_create, 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,
|
Host, ServerHost, Access,
|
||||||
Room, HistorySize,
|
Room, HistorySize,
|
||||||
RoomShaper, From,
|
RoomShaper, From,
|
||||||
Nick, NewOpts, ?MODULE),
|
Nick, NewOpts),
|
||||||
register_room(Host, Room, Pid),
|
register_room(Host, Room, Pid),
|
||||||
{reply, ok, State}.
|
{reply, ok, State}.
|
||||||
|
|
||||||
@ -379,7 +449,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
|||||||
[{xmlelement, "query",
|
[{xmlelement, "query",
|
||||||
[{"xmlns", XMLNS}],
|
[{"xmlns", XMLNS}],
|
||||||
iq_get_register_info(
|
iq_get_register_info(
|
||||||
Host, From, Lang)}]},
|
ServerHost, Host, From, Lang)}]},
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
From,
|
From,
|
||||||
jlib:iq_to_xml(Res));
|
jlib:iq_to_xml(Res));
|
||||||
@ -387,7 +457,8 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
|||||||
xmlns = ?NS_REGISTER = XMLNS,
|
xmlns = ?NS_REGISTER = XMLNS,
|
||||||
lang = Lang,
|
lang = Lang,
|
||||||
sub_el = SubEl} = IQ ->
|
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} ->
|
{result, IQRes} ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
sub_el =
|
sub_el =
|
||||||
@ -519,16 +590,38 @@ check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
|
|||||||
false
|
false
|
||||||
end.
|
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(
|
case catch mnesia:dirty_select(
|
||||||
muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'},
|
muc_room, [{#muc_room{name_host = {'_', Host}, _ = '_'},
|
||||||
[],
|
[],
|
||||||
['$_']}]) of
|
['$_']}]) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
?ERROR_MSG("~p", [Reason]),
|
?ERROR_MSG("~p", [Reason]),
|
||||||
ok;
|
[];
|
||||||
Rs ->
|
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(
|
lists:foreach(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
{Room, Host} = R#muc_room.name_host,
|
{Room, Host} = R#muc_room.name_host,
|
||||||
@ -541,30 +634,28 @@ load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
|
|||||||
Room,
|
Room,
|
||||||
HistorySize,
|
HistorySize,
|
||||||
RoomShaper,
|
RoomShaper,
|
||||||
R#muc_room.opts,
|
R#muc_room.opts),
|
||||||
?MODULE),
|
|
||||||
register_room(Host, Room, Pid);
|
register_room(Host, Room, Pid);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
end, Rs)
|
end, get_rooms(ServerHost, Host)).
|
||||||
end.
|
|
||||||
|
|
||||||
start_new_room(Host, ServerHost, Access, Room,
|
start_new_room(Host, ServerHost, Access, Room,
|
||||||
HistorySize, RoomShaper, From,
|
HistorySize, RoomShaper, From,
|
||||||
Nick, DefRoomOpts) ->
|
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]),
|
?DEBUG("MUC: open new room '~s'~n", [Room]),
|
||||||
mod_muc_room:start(Host, ServerHost, Access,
|
mod_muc_room:start(Host, ServerHost, Access,
|
||||||
Room, HistorySize,
|
Room, HistorySize,
|
||||||
RoomShaper, From,
|
RoomShaper, From,
|
||||||
Nick, DefRoomOpts, ?MODULE);
|
Nick, DefRoomOpts);
|
||||||
[#muc_room{opts = Opts}|_] ->
|
Opts ->
|
||||||
?DEBUG("MUC: restore room '~s'~n", [Room]),
|
?DEBUG("MUC: restore room '~s'~n", [Room]),
|
||||||
mod_muc_room:start(Host, ServerHost, Access,
|
mod_muc_room:start(Host, ServerHost, Access,
|
||||||
Room, HistorySize,
|
Room, HistorySize,
|
||||||
RoomShaper, Opts, ?MODULE)
|
RoomShaper, Opts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
register_room(Host, Room, Pid) ->
|
register_room(Host, Room, Pid) ->
|
||||||
@ -693,16 +784,42 @@ flush() ->
|
|||||||
iq_get_unique(From) ->
|
iq_get_unique(From) ->
|
||||||
{xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}.
|
{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),
|
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||||
LUS = {LUser, LServer},
|
LUS = {LUser, LServer},
|
||||||
{Nick, Registered} =
|
|
||||||
case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
|
case catch mnesia:dirty_read(muc_registered, {LUS, Host}) of
|
||||||
{'EXIT', _Reason} ->
|
{'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 get_nick(ServerHost, Host, From) of
|
||||||
|
error ->
|
||||||
{"", []};
|
{"", []};
|
||||||
[#muc_registered{nick = N}] ->
|
N ->
|
||||||
{N, [{xmlelement, "registered", [], []}]}
|
{N, [{xmlelement, "registered", [], []}]}
|
||||||
end,
|
end,
|
||||||
Registered ++
|
Registered ++
|
||||||
@ -722,7 +839,11 @@ iq_get_register_info(Host, From, Lang) ->
|
|||||||
Lang, "Enter nickname you want to register")}]},
|
Lang, "Enter nickname you want to register")}]},
|
||||||
?XFIELD("text-single", "Nickname", "nick", Nick)]}].
|
?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),
|
{LUser, LServer, _} = jlib:jid_tolower(From),
|
||||||
LUS = {LUser, LServer},
|
LUS = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
@ -755,7 +876,48 @@ iq_set_register_info(Host, From, Nick, Lang) ->
|
|||||||
end
|
end
|
||||||
end
|
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} ->
|
{atomic, ok} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
{atomic, false} ->
|
{atomic, false} ->
|
||||||
@ -765,7 +927,7 @@ iq_set_register_info(Host, From, Nick, Lang) ->
|
|||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_iq_register_set(Host, From, SubEl, Lang) ->
|
process_iq_register_set(ServerHost, Host, From, SubEl, Lang) ->
|
||||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||||
case xml:get_subtag(SubEl, "remove") of
|
case xml:get_subtag(SubEl, "remove") of
|
||||||
false ->
|
false ->
|
||||||
@ -783,7 +945,8 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
|
|||||||
_ ->
|
_ ->
|
||||||
case lists:keysearch("nick", 1, XData) of
|
case lists:keysearch("nick", 1, XData) of
|
||||||
{value, {_, [Nick]}} when Nick /= "" ->
|
{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",
|
ErrText = "You must fill in field \"Nickname\" in the form",
|
||||||
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
|
{error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)}
|
||||||
@ -796,7 +959,7 @@ process_iq_register_set(Host, From, SubEl, Lang) ->
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
iq_set_register_info(Host, From, "", Lang)
|
iq_set_register_info(ServerHost, Host, From, "", Lang)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
iq_get_vcard(Lang) ->
|
iq_get_vcard(Lang) ->
|
||||||
|
@ -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).
|
|
@ -31,10 +31,10 @@
|
|||||||
|
|
||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start_link/10,
|
-export([start_link/9,
|
||||||
start_link/8,
|
start_link/7,
|
||||||
start/10,
|
start/9,
|
||||||
start/8,
|
start/7,
|
||||||
route/4]).
|
route/4]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
@ -65,38 +65,38 @@
|
|||||||
-ifdef(NO_TRANSIENT_SUPERVISORS).
|
-ifdef(NO_TRANSIENT_SUPERVISORS).
|
||||||
-define(SUPERVISOR_START,
|
-define(SUPERVISOR_START,
|
||||||
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
gen_fsm:start(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
||||||
RoomShaper, Creator, Nick, DefRoomOpts, Mod],
|
RoomShaper, Creator, Nick, DefRoomOpts],
|
||||||
?FSMOPTS)).
|
?FSMOPTS)).
|
||||||
-else.
|
-else.
|
||||||
-define(SUPERVISOR_START,
|
-define(SUPERVISOR_START,
|
||||||
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
|
Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
|
||||||
supervisor:start_child(
|
supervisor:start_child(
|
||||||
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
||||||
Creator, Nick, DefRoomOpts, Mod])).
|
Creator, Nick, DefRoomOpts])).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
start(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
start(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
||||||
Creator, Nick, DefRoomOpts, Mod) ->
|
Creator, Nick, DefRoomOpts) ->
|
||||||
?SUPERVISOR_START.
|
?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 = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup),
|
||||||
supervisor:start_child(
|
supervisor:start_child(
|
||||||
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
Supervisor, [Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
||||||
Opts, Mod]).
|
Opts]).
|
||||||
|
|
||||||
start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper,
|
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,
|
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
||||||
RoomShaper, Creator, Nick, DefRoomOpts, Mod],
|
RoomShaper, Creator, Nick, DefRoomOpts],
|
||||||
?FSMOPTS).
|
?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,
|
gen_fsm:start_link(?MODULE, [Host, ServerHost, Access, Room, HistorySize,
|
||||||
RoomShaper, Opts, Mod],
|
RoomShaper, Opts],
|
||||||
?FSMOPTS).
|
?FSMOPTS).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -110,14 +110,12 @@ start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, Mod) -
|
|||||||
%% ignore |
|
%% ignore |
|
||||||
%% {stop, StopReason}
|
%% {stop, StopReason}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick,
|
init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts]) ->
|
||||||
DefRoomOpts, Mod]) ->
|
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
Shaper = shaper:new(RoomShaper),
|
Shaper = shaper:new(RoomShaper),
|
||||||
State = set_affiliation(Creator, owner,
|
State = set_affiliation(Creator, owner,
|
||||||
#state{host = Host,
|
#state{host = Host,
|
||||||
server_host = ServerHost,
|
server_host = ServerHost,
|
||||||
mod = Mod,
|
|
||||||
access = Access,
|
access = Access,
|
||||||
room = Room,
|
room = Room,
|
||||||
history = lqueue_new(HistorySize),
|
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, created, State1),
|
||||||
add_to_log(room_existence, started, State1),
|
add_to_log(room_existence, started, State1),
|
||||||
{ok, normal_state, 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),
|
process_flag(trap_exit, true),
|
||||||
Shaper = shaper:new(RoomShaper),
|
Shaper = shaper:new(RoomShaper),
|
||||||
State = set_opts(Opts, #state{host = Host,
|
State = set_opts(Opts, #state{host = Host,
|
||||||
server_host = ServerHost,
|
server_host = ServerHost,
|
||||||
mod = Mod,
|
|
||||||
access = Access,
|
access = Access,
|
||||||
room = Room,
|
room = Room,
|
||||||
history = lqueue_new(HistorySize),
|
history = lqueue_new(HistorySize),
|
||||||
@ -164,8 +161,7 @@ normal_state({route, From, "",
|
|||||||
MinMessageInterval =
|
MinMessageInterval =
|
||||||
trunc(gen_mod:get_module_opt(
|
trunc(gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod,
|
mod_muc, min_message_interval, 0) * 1000000),
|
||||||
min_message_interval, 0) * 1000000),
|
|
||||||
Size = element_size(Packet),
|
Size = element_size(Packet),
|
||||||
{MessageShaper, MessageShaperInterval} =
|
{MessageShaper, MessageShaperInterval} =
|
||||||
shaper:update(Activity#activity.message_shaper, Size),
|
shaper:update(Activity#activity.message_shaper, Size),
|
||||||
@ -286,7 +282,7 @@ normal_state({route, From, "",
|
|||||||
StateData),
|
StateData),
|
||||||
case (NSD#state.config)#config.persistent of
|
case (NSD#state.config)#config.persistent of
|
||||||
true ->
|
true ->
|
||||||
(NSD#state.mod):store_room(
|
mod_muc:store_room(
|
||||||
NSD#state.server_host,
|
NSD#state.server_host,
|
||||||
NSD#state.host,
|
NSD#state.host,
|
||||||
NSD#state.room,
|
NSD#state.room,
|
||||||
@ -485,7 +481,7 @@ normal_state({route, From, Nick,
|
|||||||
MinPresenceInterval =
|
MinPresenceInterval =
|
||||||
trunc(gen_mod:get_module_opt(
|
trunc(gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, min_presence_interval, 0) * 1000000),
|
mod_muc, min_presence_interval, 0) * 1000000),
|
||||||
if
|
if
|
||||||
(Now >= Activity#activity.presence_time + MinPresenceInterval) and
|
(Now >= Activity#activity.presence_time + MinPresenceInterval) and
|
||||||
(Activity#activity.presence == undefined) ->
|
(Activity#activity.presence == undefined) ->
|
||||||
@ -855,8 +851,7 @@ terminate(Reason, _StateName, StateData) ->
|
|||||||
tab_remove_online_user(LJID, StateData)
|
tab_remove_online_user(LJID, StateData)
|
||||||
end, [], StateData#state.users),
|
end, [], StateData#state.users),
|
||||||
add_to_log(room_existence, stopped, StateData),
|
add_to_log(room_existence, stopped, StateData),
|
||||||
(StateData#state.mod):room_destroyed(
|
mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
|
||||||
StateData#state.host, StateData#state.room, self(),
|
|
||||||
StateData#state.server_host),
|
StateData#state.server_host),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -892,7 +887,7 @@ process_groupchat_message(From, {xmlelement, "message", Attrs, _Els} = Packet,
|
|||||||
FromNick},
|
FromNick},
|
||||||
case (NSD#state.config)#config.persistent of
|
case (NSD#state.config)#config.persistent of
|
||||||
true ->
|
true ->
|
||||||
(NSD#state.mod):store_room(
|
mod_muc:store_room(
|
||||||
NSD#state.server_host,
|
NSD#state.server_host,
|
||||||
NSD#state.host,
|
NSD#state.host,
|
||||||
NSD#state.room,
|
NSD#state.room,
|
||||||
@ -1031,7 +1026,7 @@ process_presence(From, Nick, {xmlelement, "presence", Attrs, _Els} = Packet,
|
|||||||
case is_nick_change(From, Nick, StateData) of
|
case is_nick_change(From, Nick, StateData) of
|
||||||
true ->
|
true ->
|
||||||
case {nick_collision(From, Nick, StateData),
|
case {nick_collision(From, Nick, StateData),
|
||||||
(StateData#state.mod):can_use_nick(
|
mod_muc:can_use_nick(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.host, From, Nick),
|
StateData#state.host, From, Nick),
|
||||||
{(StateData#state.config)#config.allow_visitor_nickchange,
|
{(StateData#state.config)#config.allow_visitor_nickchange,
|
||||||
@ -1428,11 +1423,11 @@ get_max_users(StateData) ->
|
|||||||
|
|
||||||
get_service_max_users(StateData) ->
|
get_service_max_users(StateData) ->
|
||||||
gen_mod:get_module_opt(StateData#state.server_host,
|
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) ->
|
get_max_users_admin_threshold(StateData) ->
|
||||||
gen_mod:get_module_opt(StateData#state.server_host,
|
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) ->
|
get_user_activity(JID, StateData) ->
|
||||||
case treap:lookup(jlib:jid_tolower(JID),
|
case treap:lookup(jlib:jid_tolower(JID),
|
||||||
@ -1442,11 +1437,11 @@ get_user_activity(JID, StateData) ->
|
|||||||
MessageShaper =
|
MessageShaper =
|
||||||
shaper:new(gen_mod:get_module_opt(
|
shaper:new(gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, user_message_shaper, none)),
|
mod_muc, user_message_shaper, none)),
|
||||||
PresenceShaper =
|
PresenceShaper =
|
||||||
shaper:new(gen_mod:get_module_opt(
|
shaper:new(gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, user_presence_shaper, none)),
|
mod_muc, user_presence_shaper, none)),
|
||||||
#activity{message_shaper = MessageShaper,
|
#activity{message_shaper = MessageShaper,
|
||||||
presence_shaper = PresenceShaper}
|
presence_shaper = PresenceShaper}
|
||||||
end.
|
end.
|
||||||
@ -1455,11 +1450,11 @@ store_user_activity(JID, UserActivity, StateData) ->
|
|||||||
MinMessageInterval =
|
MinMessageInterval =
|
||||||
gen_mod:get_module_opt(
|
gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, min_message_interval, 0),
|
mod_muc, min_message_interval, 0),
|
||||||
MinPresenceInterval =
|
MinPresenceInterval =
|
||||||
gen_mod:get_module_opt(
|
gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, min_presence_interval, 0),
|
mod_muc, min_presence_interval, 0),
|
||||||
Key = jlib:jid_tolower(JID),
|
Key = jlib:jid_tolower(JID),
|
||||||
Now = now_to_usec(now()),
|
Now = now_to_usec(now()),
|
||||||
Activity1 = clean_treap(StateData#state.activity, {1, -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),
|
NConferences = tab_count_user(From),
|
||||||
MaxConferences = gen_mod:get_module_opt(
|
MaxConferences = gen_mod:get_module_opt(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.mod, max_user_conferences, 10),
|
mod_muc, max_user_conferences, 10),
|
||||||
Collision = nick_collision(From, Nick, StateData),
|
Collision = nick_collision(From, Nick, StateData),
|
||||||
case {(ServiceAffiliation == owner orelse
|
case {(ServiceAffiliation == owner orelse
|
||||||
((Affiliation == admin orelse Affiliation == owner) andalso
|
((Affiliation == admin orelse Affiliation == owner) andalso
|
||||||
@ -1748,7 +1743,8 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
|||||||
NUsers < MaxUsers) andalso
|
NUsers < MaxUsers) andalso
|
||||||
NConferences < MaxConferences,
|
NConferences < MaxConferences,
|
||||||
Collision,
|
Collision,
|
||||||
(StateData#state.mod):can_use_nick(StateData#state.server_host,
|
mod_muc:can_use_nick(
|
||||||
|
StateData#state.server_host,
|
||||||
StateData#state.host, From, Nick),
|
StateData#state.host, From, Nick),
|
||||||
get_default_role(Affiliation, StateData)} of
|
get_default_role(Affiliation, StateData)} of
|
||||||
{false, _, _, _} ->
|
{false, _, _, _} ->
|
||||||
@ -2589,7 +2585,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
|
|||||||
end, StateData, lists:flatten(Res)),
|
end, StateData, lists:flatten(Res)),
|
||||||
case (NSD#state.config)#config.persistent of
|
case (NSD#state.config)#config.persistent of
|
||||||
true ->
|
true ->
|
||||||
(NSD#state.mod):store_room(NSD#state.server_host,
|
mod_muc:store_room(NSD#state.server_host,
|
||||||
NSD#state.host, NSD#state.room,
|
NSD#state.host, NSD#state.room,
|
||||||
make_opts(NSD));
|
make_opts(NSD));
|
||||||
_ ->
|
_ ->
|
||||||
@ -3091,8 +3087,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
|
|||||||
jlib:parse_xdata_submit(XEl)) of
|
jlib:parse_xdata_submit(XEl)) of
|
||||||
{value, {_, [N]}} ->
|
{value, {_, [N]}} ->
|
||||||
length(N) =< gen_mod:get_module_opt(StateData#state.server_host,
|
length(N) =< gen_mod:get_module_opt(StateData#state.server_host,
|
||||||
StateData#state.mod,
|
mod_muc, max_room_name,
|
||||||
max_room_name, infinite);
|
infinite);
|
||||||
_ ->
|
_ ->
|
||||||
true
|
true
|
||||||
end,
|
end,
|
||||||
@ -3101,8 +3097,8 @@ is_allowed_room_name_desc_limits(XEl, StateData) ->
|
|||||||
jlib:parse_xdata_submit(XEl)) of
|
jlib:parse_xdata_submit(XEl)) of
|
||||||
{value, {_, [D]}} ->
|
{value, {_, [D]}} ->
|
||||||
length(D) =< gen_mod:get_module_opt(StateData#state.server_host,
|
length(D) =< gen_mod:get_module_opt(StateData#state.server_host,
|
||||||
StateData#state.mod,
|
mod_muc, max_room_desc,
|
||||||
max_room_desc, infinite);
|
infinite);
|
||||||
_ ->
|
_ ->
|
||||||
true
|
true
|
||||||
end,
|
end,
|
||||||
@ -3173,9 +3169,7 @@ is_password_settings_correct(XEl, StateData) ->
|
|||||||
|| JID <- JIDList]}).
|
|| JID <- JIDList]}).
|
||||||
|
|
||||||
get_default_room_maxusers(RoomState) ->
|
get_default_room_maxusers(RoomState) ->
|
||||||
DefRoomOpts = gen_mod:get_module_opt(
|
DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
|
||||||
RoomState#state.server_host,
|
|
||||||
RoomState#state.mod, default_room_options, []),
|
|
||||||
RoomState2 = set_opts(DefRoomOpts, RoomState),
|
RoomState2 = set_opts(DefRoomOpts, RoomState),
|
||||||
(RoomState2#state.config)#config.max_users.
|
(RoomState2#state.config)#config.max_users.
|
||||||
|
|
||||||
@ -3487,14 +3481,14 @@ set_xoption([_ | _Opts], _Config) ->
|
|||||||
|
|
||||||
change_config(Config, StateData) ->
|
change_config(Config, StateData) ->
|
||||||
NSD = StateData#state{config = Config},
|
NSD = StateData#state{config = Config},
|
||||||
Mod = StateData#state.mod,
|
|
||||||
case {(StateData#state.config)#config.persistent,
|
case {(StateData#state.config)#config.persistent,
|
||||||
Config#config.persistent} of
|
Config#config.persistent} of
|
||||||
{_, true} ->
|
{_, true} ->
|
||||||
Mod:store_room(NSD#state.server_host, NSD#state.host,
|
mod_muc:store_room(NSD#state.server_host, NSD#state.host,
|
||||||
NSD#state.room, make_opts(NSD));
|
NSD#state.room, make_opts(NSD));
|
||||||
{true, false} ->
|
{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} ->
|
{false, false} ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
@ -3625,7 +3619,7 @@ destroy_room(DEl, StateData) ->
|
|||||||
end, ?DICT:to_list(StateData#state.users)),
|
end, ?DICT:to_list(StateData#state.users)),
|
||||||
case (StateData#state.config)#config.persistent of
|
case (StateData#state.config)#config.persistent of
|
||||||
true ->
|
true ->
|
||||||
(StateData#state.mod):forget_room(
|
mod_muc:forget_room(
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
StateData#state.host, StateData#state.room);
|
StateData#state.host, StateData#state.room);
|
||||||
false ->
|
false ->
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% File : mod_offline.erl
|
%%% File : mod_offline.erl
|
||||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
%%% 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>
|
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||||
%%%
|
%%%
|
||||||
%%%
|
%%%
|
||||||
@ -29,15 +29,17 @@
|
|||||||
|
|
||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
|
-export([count_offline_messages/2]).
|
||||||
|
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
loop/1,
|
loop/2,
|
||||||
stop/1,
|
stop/1,
|
||||||
store_packet/3,
|
store_packet/3,
|
||||||
resend_offline_messages/2,
|
resend_offline_messages/2,
|
||||||
pop_offline_messages/3,
|
pop_offline_messages/3,
|
||||||
get_sm_features/5,
|
get_sm_features/5,
|
||||||
remove_expired_messages/0,
|
remove_expired_messages/1,
|
||||||
remove_old_messages/1,
|
remove_old_messages/2,
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
get_queue_length/2,
|
get_queue_length/2,
|
||||||
webadmin_page/3,
|
webadmin_page/3,
|
||||||
@ -58,11 +60,17 @@
|
|||||||
-define(MAX_USER_MESSAGES, infinity).
|
-define(MAX_USER_MESSAGES, infinity).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(offline_msg,
|
mnesia:create_table(offline_msg,
|
||||||
[{disc_only_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, offline_msg)}]),
|
{attributes,
|
||||||
update_table(),
|
record_info(fields, offline_msg)}]),
|
||||||
|
update_table();
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(offline_message_hook, Host,
|
ejabberd_hooks:add(offline_message_hook, Host,
|
||||||
?MODULE, store_packet, 50),
|
?MODULE, store_packet, 50),
|
||||||
ejabberd_hooks:add(resend_offline_messages_hook, Host,
|
ejabberd_hooks:add(resend_offline_messages_hook, Host,
|
||||||
@ -83,16 +91,23 @@ start(Host, Opts) ->
|
|||||||
?MODULE, webadmin_user_parse_query, 50),
|
?MODULE, webadmin_user_parse_query, 50),
|
||||||
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
|
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
|
||||||
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
register(gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
|
spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])).
|
||||||
|
|
||||||
loop(AccessMaxOfflineMsgs) ->
|
loop(Host, AccessMaxOfflineMsgs) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{us=US} = Msg ->
|
#offline_msg{us = User} = Msg ->
|
||||||
Msgs = receive_all(US, [Msg]),
|
DBType = gen_mod:db_type(Host, ?MODULE),
|
||||||
|
Msgs = receive_all(User, [Msg], DBType),
|
||||||
Len = length(Msgs),
|
Len = length(Msgs),
|
||||||
{User, Host} = US,
|
|
||||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
||||||
User, Host),
|
User, Host),
|
||||||
|
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() ->
|
F = fun() ->
|
||||||
%% Only count messages if needed:
|
%% Only count messages if needed:
|
||||||
Count = if MaxOfflineMsgs =/= infinity ->
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
@ -117,10 +132,47 @@ loop(AccessMaxOfflineMsgs) ->
|
|||||||
end, Msgs)
|
end, Msgs)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F),
|
mnesia:transaction(F);
|
||||||
loop(AccessMaxOfflineMsgs);
|
store_offline_msg(Host, User, Msgs, Len, MaxOfflineMsgs, odbc) ->
|
||||||
_ ->
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
loop(AccessMaxOfflineMsgs)
|
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.
|
end.
|
||||||
|
|
||||||
%% Function copied from ejabberd_sm.erl:
|
%% Function copied from ejabberd_sm.erl:
|
||||||
@ -132,15 +184,27 @@ get_max_user_messages(AccessRule, LUser, Host) ->
|
|||||||
_ -> ?MAX_USER_MESSAGES
|
_ -> ?MAX_USER_MESSAGES
|
||||||
end.
|
end.
|
||||||
|
|
||||||
receive_all(US, Msgs) ->
|
receive_all(US, Msgs, DBType) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{us=US} = Msg ->
|
#offline_msg{us=US} = Msg ->
|
||||||
receive_all(US, [Msg | Msgs])
|
receive_all(US, [Msg | Msgs], DBType)
|
||||||
after 0 ->
|
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.
|
end.
|
||||||
|
|
||||||
|
|
||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
ejabberd_hooks:delete(offline_message_hook, Host,
|
ejabberd_hooks:delete(offline_message_hook, Host,
|
||||||
?MODULE, store_packet, 50),
|
?MODULE, store_packet, 50),
|
||||||
@ -325,6 +389,10 @@ resend_offline_messages(User, Server) ->
|
|||||||
pop_offline_messages(Ls, User, Server) ->
|
pop_offline_messages(Ls, User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
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},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
Rs = mnesia:wread({offline_msg, US}),
|
Rs = mnesia:wread({offline_msg, US}),
|
||||||
@ -346,7 +414,7 @@ pop_offline_messages(Ls, User, Server) ->
|
|||||||
calendar:now_to_universal_time(
|
calendar:now_to_universal_time(
|
||||||
R#offline_msg.timestamp),
|
R#offline_msg.timestamp),
|
||||||
utc,
|
utc,
|
||||||
jlib:make_jid("", Server, ""),
|
jlib:make_jid("", LServer, ""),
|
||||||
"Offline Storage"),
|
"Offline Storage"),
|
||||||
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
%% TODO: Delete the next three lines once XEP-0091 is Obsolete
|
||||||
jlib:timestamp_to_xml(
|
jlib:timestamp_to_xml(
|
||||||
@ -365,10 +433,39 @@ pop_offline_messages(Ls, User, Server) ->
|
|||||||
lists:keysort(#offline_msg.timestamp, Rs)));
|
lists:keysort(#offline_msg.timestamp, Rs)));
|
||||||
_ ->
|
_ ->
|
||||||
Ls
|
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.
|
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(),
|
TimeStamp = now(),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write_lock_table(offline_msg),
|
mnesia:write_lock_table(offline_msg),
|
||||||
@ -387,9 +484,16 @@ remove_expired_messages() ->
|
|||||||
end
|
end
|
||||||
end, ok, offline_msg)
|
end, ok, offline_msg)
|
||||||
end,
|
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(),
|
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||||
S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
|
S = MegaSecs * 1000000 + Secs - 60 * 60 * 24 * Days,
|
||||||
MegaSecs1 = S div 1000000,
|
MegaSecs1 = S div 1000000,
|
||||||
@ -404,16 +508,25 @@ remove_old_messages(Days) ->
|
|||||||
(_Rec, _Acc) -> ok
|
(_Rec, _Acc) -> ok
|
||||||
end, ok, offline_msg)
|
end, ok, offline_msg)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F);
|
||||||
|
remove_old_messages(_Days, _LServer, odbc) ->
|
||||||
|
%% TODO
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({offline_msg, US})
|
mnesia:delete({offline_msg, US})
|
||||||
end,
|
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() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, offline_msg),
|
Fields = record_info(fields, offline_msg),
|
||||||
@ -527,13 +640,32 @@ webadmin_page(_, Host,
|
|||||||
|
|
||||||
webadmin_page(Acc, _, _) -> Acc.
|
webadmin_page(Acc, _, _) -> Acc.
|
||||||
|
|
||||||
user_queue(User, Server, Query, Lang) ->
|
read_all_msgs(LUser, LServer, mnesia) ->
|
||||||
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
|
US = {LUser, LServer},
|
||||||
Res = user_queue_parse_query(US, Query),
|
lists:keysort(#offline_msg.timestamp,
|
||||||
MsgsAll = lists:keysort(#offline_msg.timestamp,
|
mnesia:dirty_read({offline_msg, US}));
|
||||||
mnesia:dirty_read({offline_msg, US})),
|
read_all_msgs(LUser, LServer, odbc) ->
|
||||||
Msgs = get_messages_subset(User, Server, MsgsAll),
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
FMsgs =
|
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(
|
lists:map(
|
||||||
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
||||||
packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
|
packet = {xmlelement, Name, Attrs, Els}} = Msg) ->
|
||||||
@ -556,7 +688,28 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
?XAC("td", [{"class", "valign"}], STo),
|
?XAC("td", [{"class", "valign"}], STo),
|
||||||
?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
|
?XAE("td", [{"class", "valign"}], [?XC("pre", FPacket)])]
|
||||||
)
|
)
|
||||||
end, Msgs),
|
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) ->
|
||||||
|
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"),
|
[?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
|
||||||
[us_to_list(US)]))] ++
|
[us_to_list(US)]))] ++
|
||||||
case Res of
|
case Res of
|
||||||
@ -587,7 +740,8 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
?INPUTT("submit", "delete", "Delete Selected")
|
?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
|
case lists:keysearch("delete", 1, Query) of
|
||||||
{value, _} ->
|
{value, _} ->
|
||||||
Msgs = lists:keysort(#offline_msg.timestamp,
|
Msgs = lists:keysort(#offline_msg.timestamp,
|
||||||
@ -609,15 +763,74 @@ user_queue_parse_query(US, Query) ->
|
|||||||
ok;
|
ok;
|
||||||
false ->
|
false ->
|
||||||
nothing
|
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.
|
end.
|
||||||
|
|
||||||
us_to_list({User, Server}) ->
|
us_to_list({User, Server}) ->
|
||||||
jlib:jid_to_string({User, Server, ""}).
|
jlib:jid_to_string({User, Server, ""}).
|
||||||
|
|
||||||
get_queue_length(User, Server) ->
|
get_queue_length(LUser, LServer) ->
|
||||||
length(mnesia:dirty_read({offline_msg, {User, Server}})).
|
get_queue_length(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
get_messages_subset(User, Host, MsgsAll) ->
|
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, DBType) ->
|
||||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
||||||
max_user_offline_messages),
|
max_user_offline_messages),
|
||||||
MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
|
MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of
|
||||||
@ -625,17 +838,23 @@ get_messages_subset(User, Host, MsgsAll) ->
|
|||||||
_ -> 100
|
_ -> 100
|
||||||
end,
|
end,
|
||||||
Length = length(MsgsAll),
|
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;
|
MsgsAll;
|
||||||
get_messages_subset2(Max, Length, MsgsAll) ->
|
get_messages_subset2(Max, Length, MsgsAll, mnesia) ->
|
||||||
FirstN = Max,
|
FirstN = Max,
|
||||||
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
|
{MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll),
|
||||||
MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
|
MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2),
|
||||||
NoJID = jlib:make_jid("...", "...", ""),
|
NoJID = jlib:make_jid("...", "...", ""),
|
||||||
IntermediateMsg = #offline_msg{timestamp = now(), from = NoJID, to = NoJID,
|
IntermediateMsg = #offline_msg{timestamp = now(), from = NoJID, to = NoJID,
|
||||||
packet = {xmlelement, "...", [], []}},
|
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.
|
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
||||||
|
|
||||||
webadmin_user(Acc, User, Server, Lang) ->
|
webadmin_user(Acc, User, Server, Lang) ->
|
||||||
@ -644,8 +863,13 @@ webadmin_user(Acc, User, Server, Lang) ->
|
|||||||
integer_to_list(QueueLen))],
|
integer_to_list(QueueLen))],
|
||||||
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
|
||||||
|
|
||||||
webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
delete_all_msgs(User, Server) ->
|
||||||
US = {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() ->
|
F = fun() ->
|
||||||
mnesia:write_lock_table(offline_msg),
|
mnesia:write_lock_table(offline_msg),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
@ -653,7 +877,15 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
|||||||
mnesia:delete_object(Msg)
|
mnesia:delete_object(Msg)
|
||||||
end, mnesia:dirty_read({offline_msg, US}))
|
end, mnesia:dirty_read({offline_msg, US}))
|
||||||
end,
|
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} ->
|
{aborted, Reason} ->
|
||||||
?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
|
?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]),
|
||||||
{stop, error};
|
{stop, error};
|
||||||
@ -663,3 +895,14 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
|||||||
end;
|
end;
|
||||||
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
||||||
Acc.
|
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.
|
||||||
|
@ -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.
|
|
@ -36,8 +36,21 @@
|
|||||||
get_user_list/3,
|
get_user_list/3,
|
||||||
check_packet/6,
|
check_packet/6,
|
||||||
remove_user/2,
|
remove_user/2,
|
||||||
|
item_to_raw/1,
|
||||||
|
raw_to_item/1,
|
||||||
|
is_list_needdb/1,
|
||||||
updated_list/3]).
|
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("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
@ -45,9 +58,15 @@
|
|||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||||
mnesia:create_table(privacy, [{disc_copies, [node()]},
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
|
mnesia:create_table(privacy,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, privacy)}]),
|
{attributes, record_info(fields, privacy)}]),
|
||||||
update_table(),
|
update_table();
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(privacy_iq_get, Host,
|
ejabberd_hooks:add(privacy_iq_get, Host,
|
||||||
?MODULE, process_iq_get, 50),
|
?MODULE, process_iq_get, 50),
|
||||||
ejabberd_hooks:add(privacy_iq_set, Host,
|
ejabberd_hooks:add(privacy_iq_set, Host,
|
||||||
@ -102,24 +121,14 @@ process_iq_get(_, From, _To, #iq{sub_el = SubEl},
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
process_lists_get(LUser, LServer, Active) ->
|
process_lists_get(LUser, LServer, Active) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case process_lists_get(LUser, LServer, Active,
|
||||||
{'EXIT', _Reason} ->
|
gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
|
error ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
[] ->
|
{_Default, []} ->
|
||||||
{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
|
{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
{Default, LItems} ->
|
||||||
case Lists of
|
|
||||||
[] ->
|
|
||||||
{result, [{xmlelement, "query",
|
|
||||||
[{"xmlns", ?NS_PRIVACY}], []}]};
|
|
||||||
_ ->
|
|
||||||
LItems = lists:map(
|
|
||||||
fun({N, _}) ->
|
|
||||||
{xmlelement, "list",
|
|
||||||
[{"name", N}], []}
|
|
||||||
end, Lists),
|
|
||||||
DItems =
|
DItems =
|
||||||
case Default of
|
case Default of
|
||||||
none ->
|
none ->
|
||||||
@ -139,32 +148,90 @@ process_lists_get(LUser, LServer, Active) ->
|
|||||||
{result,
|
{result,
|
||||||
[{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
|
[{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
|
||||||
ADItems}]}
|
ADItems}]}
|
||||||
end
|
end.
|
||||||
|
|
||||||
|
process_lists_get(LUser, LServer, _Active, mnesia) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
error;
|
||||||
|
[] ->
|
||||||
|
{none, []};
|
||||||
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
|
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.
|
end.
|
||||||
|
|
||||||
process_list_get(LUser, LServer, {value, Name}) ->
|
process_list_get(LUser, LServer, {value, Name}) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case process_list_get(LUser, LServer, Name,
|
||||||
{'EXIT', _Reason} ->
|
gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
|
error ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||||
[] ->
|
not_found ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
%{result, [{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}], []}]};
|
Items ->
|
||||||
[#privacy{lists = Lists}] ->
|
LItems = lists:map(fun item_to_xml/1, Items),
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
LItems = lists:map(fun item_to_xml/1, List),
|
|
||||||
{result,
|
{result,
|
||||||
[{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
|
[{xmlelement, "query", [{"xmlns", ?NS_PRIVACY}],
|
||||||
[{xmlelement, "list",
|
[{xmlelement, "list",
|
||||||
[{"name", Name}], LItems}]}]};
|
[{"name", Name}], LItems}]}]}
|
||||||
_ ->
|
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
process_list_get(_LUser, _LServer, false) ->
|
process_list_get(_LUser, _LServer, false) ->
|
||||||
{error, ?ERR_BAD_REQUEST}.
|
{error, ?ERR_BAD_REQUEST}.
|
||||||
|
|
||||||
|
process_list_get(LUser, LServer, Name, mnesia) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
error;
|
||||||
|
[] ->
|
||||||
|
not_found;
|
||||||
|
[#privacy{lists = Lists}] ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
List;
|
||||||
|
_ ->
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
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) ->
|
item_to_xml(Item) ->
|
||||||
Attrs1 = [{"action", action_to_list(Item#listitem.action)},
|
Attrs1 = [{"action", action_to_list(Item#listitem.action)},
|
||||||
{"order", order_to_list(Item#listitem.order)}],
|
{"order", order_to_list(Item#listitem.order)}],
|
||||||
@ -269,98 +336,194 @@ process_iq_set(_, From, _To, #iq{sub_el = SubEl}) ->
|
|||||||
{error, ?ERR_BAD_REQUEST}
|
{error, ?ERR_BAD_REQUEST}
|
||||||
end.
|
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() ->
|
F = fun() ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
[] ->
|
[] ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
not_found;
|
||||||
[#privacy{lists = Lists} = P] ->
|
[#privacy{lists = Lists} = P] ->
|
||||||
case lists:keymember(Name, 1, Lists) of
|
case lists:keymember(Name, 1, Lists) of
|
||||||
true ->
|
true ->
|
||||||
mnesia:write(P#privacy{default = Name,
|
mnesia:write(P#privacy{default = Name,
|
||||||
lists = Lists}),
|
lists = Lists}),
|
||||||
{result, []};
|
ok;
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}
|
not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
mnesia:transaction(F);
|
||||||
{atomic, {error, _} = Error} ->
|
process_default_set(LUser, LServer, {value, Name}, odbc) ->
|
||||||
Error;
|
F = fun() ->
|
||||||
{atomic, {result, _} = Res} ->
|
case sql_get_privacy_list_names_t(LUser) of
|
||||||
Res;
|
{selected, ["name"], []} ->
|
||||||
_ ->
|
not_found;
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{selected, ["name"], Names} ->
|
||||||
end;
|
case lists:member({Name}, Names) of
|
||||||
|
true ->
|
||||||
process_default_set(LUser, LServer, false) ->
|
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() ->
|
F = fun() ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
[] ->
|
[] ->
|
||||||
{result, []};
|
ok;
|
||||||
[R] ->
|
[R] ->
|
||||||
mnesia:write(R#privacy{default = none}),
|
mnesia:write(R#privacy{default = none})
|
||||||
{result, []}
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
mnesia:transaction(F);
|
||||||
{atomic, {error, _} = Error} ->
|
process_default_set(LUser, LServer, false, odbc) ->
|
||||||
Error;
|
case catch sql_unset_default_privacy_list(LUser, LServer) of
|
||||||
{atomic, {result, _} = Res} ->
|
{'EXIT', _Reason} ->
|
||||||
Res;
|
{atomic, error};
|
||||||
|
{error, _Reason} ->
|
||||||
|
{atomic, error};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{atomic, ok}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
process_active_set(LUser, LServer, {value, Name}) ->
|
process_active_set(LUser, LServer, {value, Name}) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case process_active_set(LUser, LServer, Name,
|
||||||
[] ->
|
gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
|
error ->
|
||||||
{error, ?ERR_ITEM_NOT_FOUND};
|
{error, ?ERR_ITEM_NOT_FOUND};
|
||||||
[#privacy{lists = Lists}] ->
|
Items ->
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
NeedDb = is_list_needdb(Items),
|
||||||
{value, {_, List}} ->
|
{result, [], #userlist{name = Name, list = Items, needdb = NeedDb}}
|
||||||
NeedDb = is_list_needdb(List),
|
|
||||||
{result, [], #userlist{name = Name, list = List, needdb = NeedDb}};
|
|
||||||
false ->
|
|
||||||
{error, ?ERR_ITEM_NOT_FOUND}
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
process_active_set(_LUser, _LServer, false) ->
|
process_active_set(_LUser, _LServer, false) ->
|
||||||
{result, [], #userlist{}}.
|
{result, [], #userlist{}}.
|
||||||
|
|
||||||
|
process_active_set(LUser, LServer, Name, mnesia) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
[] ->
|
||||||
|
error;
|
||||||
|
[#privacy{lists = Lists}] ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
List;
|
||||||
|
false ->
|
||||||
|
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.
|
||||||
|
|
||||||
|
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) ->
|
process_list_set(LUser, LServer, {value, Name}, Els) ->
|
||||||
case parse_items(Els) of
|
case parse_items(Els) of
|
||||||
false ->
|
false ->
|
||||||
{error, ?ERR_BAD_REQUEST};
|
{error, ?ERR_BAD_REQUEST};
|
||||||
remove ->
|
remove ->
|
||||||
F =
|
case remove_privacy_list(LUser, LServer, Name,
|
||||||
fun() ->
|
gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
{atomic, conflict} ->
|
||||||
[] ->
|
|
||||||
{result, []};
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
% TODO: check active
|
|
||||||
if
|
|
||||||
Name == Default ->
|
|
||||||
{error, ?ERR_CONFLICT};
|
{error, ?ERR_CONFLICT};
|
||||||
true ->
|
{atomic, ok} ->
|
||||||
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} ->
|
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:make_jid(LUser, LServer, ""),
|
jlib:make_jid(LUser, LServer, ""),
|
||||||
jlib:make_jid(LUser, LServer, ""),
|
jlib:make_jid(LUser, LServer, ""),
|
||||||
@ -368,30 +531,14 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
|||||||
[{privacy_list,
|
[{privacy_list,
|
||||||
#userlist{name = Name, list = []},
|
#userlist{name = Name, list = []},
|
||||||
Name}]}),
|
Name}]}),
|
||||||
Res;
|
{result, []};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end;
|
end;
|
||||||
List ->
|
List ->
|
||||||
F =
|
case set_privacy_list(LUser, LServer, Name, List,
|
||||||
fun() ->
|
gen_mod:db_type(LServer, ?MODULE)) of
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
{atomic, ok} ->
|
||||||
[] ->
|
|
||||||
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} ->
|
|
||||||
NeedDb = is_list_needdb(List),
|
NeedDb = is_list_needdb(List),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:make_jid(LUser, LServer, ""),
|
jlib:make_jid(LUser, LServer, ""),
|
||||||
@ -400,7 +547,7 @@ process_list_set(LUser, LServer, {value, Name}, Els) ->
|
|||||||
[{privacy_list,
|
[{privacy_list,
|
||||||
#userlist{name = Name, list = List, needdb = NeedDb},
|
#userlist{name = Name, list = List, needdb = NeedDb},
|
||||||
Name}]}),
|
Name}]}),
|
||||||
Res;
|
{result, []};
|
||||||
_ ->
|
_ ->
|
||||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end
|
end
|
||||||
@ -532,30 +679,51 @@ is_list_needdb(Items) ->
|
|||||||
end
|
end
|
||||||
end, Items).
|
end, Items).
|
||||||
|
|
||||||
get_user_list(_, User, Server) ->
|
get_user_list(Acc, User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
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
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
[] ->
|
[] ->
|
||||||
#userlist{};
|
{none, []};
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
case Default of
|
case Default of
|
||||||
none ->
|
none ->
|
||||||
#userlist{};
|
{none, []};
|
||||||
_ ->
|
_ ->
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
{value, {_, List}} ->
|
{value, {_, List}} ->
|
||||||
NeedDb = is_list_needdb(List),
|
{Default, List};
|
||||||
#userlist{name = Default, list = List, needdb = NeedDb};
|
|
||||||
_ ->
|
_ ->
|
||||||
#userlist{}
|
{none, []}
|
||||||
end
|
end
|
||||||
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.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%% From is the sender, To is the destination.
|
%% From is the sender, To is the destination.
|
||||||
%% If Dir = out, User@Server is the sender account (From).
|
%% If Dir = out, User@Server is the sender account (From).
|
||||||
%% If Dir = in, User@Server is the destination account (To).
|
%% 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) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({privacy,
|
mnesia:delete({privacy,
|
||||||
{LUser, LServer}})
|
{LUser, LServer}})
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F);
|
||||||
|
remove_user(LUser, LServer, odbc) ->
|
||||||
|
sql_del_privacy_lists(LUser, LServer).
|
||||||
|
|
||||||
updated_list(_,
|
updated_list(_,
|
||||||
#userlist{name = OldName} = Old,
|
#userlist{name = OldName} = Old,
|
||||||
@ -716,6 +888,160 @@ updated_list(_,
|
|||||||
Old
|
Old
|
||||||
end.
|
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() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, privacy),
|
Fields = record_info(fields, privacy),
|
||||||
|
@ -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).
|
|
@ -46,10 +46,16 @@
|
|||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(private_storage,
|
mnesia:create_table(private_storage,
|
||||||
[{disc_only_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{attributes, record_info(fields, private_storage)}]),
|
{attributes,
|
||||||
update_table(),
|
record_info(fields, private_storage)}]),
|
||||||
|
update_table();
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(remove_user, Host,
|
ejabberd_hooks:add(remove_user, Host,
|
||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE,
|
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]
|
sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]
|
||||||
};
|
};
|
||||||
Data ->
|
Data ->
|
||||||
mnesia:transaction(fun() ->
|
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||||
lists:foreach(fun
|
F = fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun
|
||||||
(Datum) ->
|
(Datum) ->
|
||||||
set_data(LUser, LServer, Datum)
|
set_data(LUser, LServer,
|
||||||
|
Datum, DBType)
|
||||||
end, Data)
|
end, Data)
|
||||||
end),
|
end,
|
||||||
|
case DBType of
|
||||||
|
odbc ->
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F);
|
||||||
|
mnesia ->
|
||||||
|
mnesia:transaction(F)
|
||||||
|
end,
|
||||||
IQ#iq{type = result, sub_el = []}
|
IQ#iq{type = result, sub_el = []}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
@ -132,30 +147,53 @@ filter_xmlels([_ | Xmlels], Data) ->
|
|||||||
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{
|
mnesia:write(#private_storage{
|
||||||
usns = {LUser, LServer, XmlNS},
|
usns = {LUser, LServer, XmlNS},
|
||||||
xml = Xmlel
|
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, 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);
|
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
|
case mnesia:dirty_read(private_storage, {LUser, LServer, XmlNS}) of
|
||||||
[#private_storage{xml = Storage_Xmlel}] ->
|
[#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.
|
end.
|
||||||
|
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
remove_user(LUser, LServer, gen_mod:db_type(Server, ?MODULE)).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
Namespaces = mnesia:select(
|
Namespaces = mnesia:select(
|
||||||
private_storage,
|
private_storage,
|
||||||
@ -169,8 +207,10 @@ remove_user(User, Server) ->
|
|||||||
{LUser, LServer, Namespace}})
|
{LUser, LServer, Namespace}})
|
||||||
end, Namespaces)
|
end, Namespaces)
|
||||||
end,
|
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() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, private_storage),
|
Fields = record_info(fields, private_storage),
|
||||||
|
@ -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).
|
|
@ -65,14 +65,21 @@
|
|||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
||||||
mnesia:create_table(roster,[{disc_copies, [node()]},
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
|
mnesia:create_table(roster,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, roster)}]),
|
{attributes, record_info(fields, roster)}]),
|
||||||
mnesia:create_table(roster_version, [{disc_copies, [node()]},
|
mnesia:create_table(roster_version,
|
||||||
{attributes, record_info(fields, roster_version)}]),
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, roster_version)}]),
|
||||||
update_table(),
|
update_table(),
|
||||||
mnesia:add_table_index(roster, us),
|
mnesia:add_table_index(roster, us),
|
||||||
mnesia:add_table_index(roster_version, us),
|
mnesia:add_table_index(roster_version, us);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(roster_get, Host,
|
ejabberd_hooks:add(roster_get, Host,
|
||||||
?MODULE, get_user_roster, 50),
|
?MODULE, get_user_roster, 50),
|
||||||
ejabberd_hooks:add(roster_in_subscription, Host,
|
ejabberd_hooks:add(roster_in_subscription, Host,
|
||||||
@ -170,14 +177,66 @@ roster_version(LServer ,LUser) ->
|
|||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case roster_version_on_db(LServer) of
|
case roster_version_on_db(LServer) of
|
||||||
true ->
|
true ->
|
||||||
case mnesia:dirty_read(roster_version, US) of
|
case read_roster_version(LUser, LServer) of
|
||||||
[#roster_version{version = V}] -> V;
|
error ->
|
||||||
[] -> not_found
|
not_found;
|
||||||
|
V ->
|
||||||
|
V
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
|
roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US]))
|
||||||
end.
|
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.
|
%% Load roster from DB only if neccesary.
|
||||||
%% It is neccesary if
|
%% It is neccesary if
|
||||||
%% - roster versioning is disabled in server OR
|
%% - roster versioning is disabled in server OR
|
||||||
@ -196,53 +255,112 @@ process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) ->
|
|||||||
{{value, RequestedVersion}, true, true} ->
|
{{value, RequestedVersion}, true, true} ->
|
||||||
%% Retrieve version from DB. Only load entire roster
|
%% Retrieve version from DB. Only load entire roster
|
||||||
%% when neccesary.
|
%% when neccesary.
|
||||||
case mnesia:dirty_read(roster_version, US) of
|
case read_roster_version(LUser, LServer) of
|
||||||
[#roster_version{version = RequestedVersion}] ->
|
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};
|
{false, false};
|
||||||
[#roster_version{version = NewVersion}] ->
|
NewVersion ->
|
||||||
{lists:map(fun item_to_xml/1,
|
{lists:map(
|
||||||
ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), NewVersion};
|
fun item_to_xml/1,
|
||||||
[] ->
|
ejabberd_hooks:run_fold(
|
||||||
RosterVersion = sha:sha(term_to_binary(now())),
|
roster_get, To#jid.lserver, [], [US])),
|
||||||
mnesia:dirty_write(#roster_version{us = US, version = RosterVersion}),
|
NewVersion}
|
||||||
{lists:map(fun item_to_xml/1,
|
|
||||||
ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), RosterVersion}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{{value, RequestedVersion}, true, false} ->
|
{{value, RequestedVersion}, true, false} ->
|
||||||
RosterItems = ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [] , [US]),
|
RosterItems = ejabberd_hooks:run_fold(
|
||||||
|
roster_get, To#jid.lserver, [] , [US]),
|
||||||
case roster_hash(RosterItems) of
|
case roster_hash(RosterItems) of
|
||||||
RequestedVersion ->
|
RequestedVersion ->
|
||||||
{false, false};
|
{false, false};
|
||||||
New ->
|
New ->
|
||||||
{lists:map(fun item_to_xml/1, RosterItems), New}
|
{lists:map(fun item_to_xml/1, RosterItems), New}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{lists:map(fun item_to_xml/1,
|
{lists:map(
|
||||||
ejabberd_hooks:run_fold(roster_get, To#jid.lserver, [], [US])), false}
|
fun item_to_xml/1,
|
||||||
|
ejabberd_hooks:run_fold(
|
||||||
|
roster_get, To#jid.lserver, [], [US])),
|
||||||
|
false}
|
||||||
end,
|
end,
|
||||||
IQ#iq{type = result, sub_el = case {ItemsToSend, VersionToSend} of
|
IQ#iq{type = result,
|
||||||
{false, false} -> [];
|
sub_el = case {ItemsToSend, VersionToSend} of
|
||||||
{Items, false} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}], Items}];
|
{false, false} ->
|
||||||
{Items, Version} -> [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}, {"ver", Version}], Items}]
|
[];
|
||||||
|
{Items, false} ->
|
||||||
|
[{xmlelement, "query",
|
||||||
|
[{"xmlns", ?NS_ROSTER}], Items}];
|
||||||
|
{Items, Version} ->
|
||||||
|
[{xmlelement, "query",
|
||||||
|
[{"xmlns", ?NS_ROSTER}, {"ver", Version}],
|
||||||
|
Items}]
|
||||||
end}
|
end}
|
||||||
catch
|
catch
|
||||||
_:_ ->
|
_:_ ->
|
||||||
IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
IQ#iq{type =error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_user_roster(Acc, {LUser, LServer}) ->
|
||||||
get_user_roster(Acc, US) ->
|
Items = get_roster(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}) ->
|
lists:filter(fun(#roster{subscription = none, ask = in}) ->
|
||||||
false;
|
false;
|
||||||
(_) ->
|
(_) ->
|
||||||
true
|
true
|
||||||
end, Items) ++ Acc;
|
end, Items) ++ Acc.
|
||||||
|
|
||||||
|
get_roster(LUser, LServer) ->
|
||||||
|
get_roster(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
get_roster(LUser, LServer, mnesia) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
case catch mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||||
|
Items when is_list(Items) ->
|
||||||
|
Items;
|
||||||
_ ->
|
_ ->
|
||||||
Acc
|
[]
|
||||||
|
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;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@ -280,6 +398,50 @@ item_to_xml(Item) ->
|
|||||||
SubEls = SubEls1 ++ Item#roster.xs,
|
SubEls = SubEls1 ++ Item#roster.xs,
|
||||||
{xmlelement, "item", Attrs4, SubEls}.
|
{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) ->
|
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
||||||
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
{xmlelement, _Name, _Attrs, Els} = SubEl,
|
||||||
@ -293,40 +455,28 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
|
|||||||
error ->
|
error ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},
|
|
||||||
LJID = jlib:jid_tolower(JID1),
|
LJID = jlib:jid_tolower(JID1),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
Res = mnesia:read({roster, {LUser, LServer, LJID}}),
|
Item = get_roster_by_jid_t(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,
|
|
||||||
Item1 = process_item_attrs(Item, Attrs),
|
Item1 = process_item_attrs(Item, Attrs),
|
||||||
Item2 = process_item_els(Item1, Els),
|
Item2 = process_item_els(Item1, Els),
|
||||||
case Item2#roster.subscription of
|
case Item2#roster.subscription of
|
||||||
remove ->
|
remove ->
|
||||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
del_roster_t(LUser, LServer, LJID);
|
||||||
_ ->
|
_ ->
|
||||||
mnesia:write(Item2)
|
update_roster_t(LUser, LServer, LJID, Item2)
|
||||||
end,
|
end,
|
||||||
%% If the item exist in shared roster, take the
|
%% If the item exist in shared roster, take the
|
||||||
%% subscription information from there:
|
%% subscription information from there:
|
||||||
Item3 = ejabberd_hooks:run_fold(roster_process_item,
|
Item3 = ejabberd_hooks:run_fold(roster_process_item,
|
||||||
LServer, Item2, [LServer]),
|
LServer, Item2, [LServer]),
|
||||||
case roster_version_on_db(LServer) of
|
case roster_version_on_db(LServer) of
|
||||||
true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
|
true -> write_roster_version_t(LUser, LServer);
|
||||||
false -> ok
|
false -> ok
|
||||||
end,
|
end,
|
||||||
{Item, Item3}
|
{Item, Item3}
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case transaction(LServer, F) of
|
||||||
{atomic, {OldItem, Item}} ->
|
{atomic, {OldItem, Item}} ->
|
||||||
push_item(User, LServer, To, Item),
|
push_item(User, LServer, To, Item),
|
||||||
case Item#roster.subscription of
|
case Item#roster.subscription of
|
||||||
@ -351,7 +501,7 @@ process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
|
|||||||
error ->
|
error ->
|
||||||
process_item_attrs(Item, Attrs);
|
process_item_attrs(Item, Attrs);
|
||||||
JID1 ->
|
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)
|
process_item_attrs(Item#roster{jid = JID}, Attrs)
|
||||||
end;
|
end;
|
||||||
"name" ->
|
"name" ->
|
||||||
@ -435,30 +585,54 @@ push_item_version(Server, User, From, Item, RosterVersion) ->
|
|||||||
push_item(User, Server, Resource, From, Item, RosterVersion)
|
push_item(User, Server, Resource, From, Item, RosterVersion)
|
||||||
end, ejabberd_sm:get_user_resources(User, Server)).
|
end, ejabberd_sm:get_user_resources(User, Server)).
|
||||||
|
|
||||||
get_subscription_lists(_, User, Server) ->
|
get_subscription_lists(Acc, User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
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},
|
US = {LUser, LServer},
|
||||||
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||||
Items when is_list(Items) ->
|
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.
|
end.
|
||||||
|
|
||||||
fill_subscription_lists([I | Is], F, T) ->
|
fill_subscription_lists(LServer, [#roster{} = I | Is], F, T) ->
|
||||||
J = element(3, I#roster.usj),
|
J = element(3, I#roster.usj),
|
||||||
case I#roster.subscription of
|
case I#roster.subscription of
|
||||||
both ->
|
both ->
|
||||||
fill_subscription_lists(Is, [J | F], [J | T]);
|
fill_subscription_lists(LServer, Is, [J | F], [J | T]);
|
||||||
from ->
|
from ->
|
||||||
fill_subscription_lists(Is, [J | F], T);
|
fill_subscription_lists(LServer, Is, [J | F], T);
|
||||||
to ->
|
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;
|
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}.
|
{F, T}.
|
||||||
|
|
||||||
ask_to_pending(subscribe) -> out;
|
ask_to_pending(subscribe) -> out;
|
||||||
@ -466,6 +640,25 @@ ask_to_pending(unsubscribe) -> none;
|
|||||||
ask_to_pending(Ask) -> Ask.
|
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) ->
|
in_subscription(_, User, Server, JID, Type, Reason) ->
|
||||||
process_subscription(in, 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) ->
|
out_subscription(User, Server, JID, Type) ->
|
||||||
process_subscription(out, 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) ->
|
process_subscription(Direction, User, Server, JID1, Type, Reason) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
|
||||||
LJID = jlib:jid_tolower(JID1),
|
LJID = jlib:jid_tolower(JID1),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
Item = case mnesia:read({roster, {LUser, LServer, LJID}}) of
|
Item = get_roster_by_jid_with_groups_t(
|
||||||
[] ->
|
LUser, LServer, LJID),
|
||||||
JID = {JID1#jid.user,
|
|
||||||
JID1#jid.server,
|
|
||||||
JID1#jid.resource},
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = US,
|
|
||||||
jid = JID};
|
|
||||||
[I] ->
|
|
||||||
I
|
|
||||||
end,
|
|
||||||
NewState = case Direction of
|
NewState = case Direction of
|
||||||
out ->
|
out ->
|
||||||
out_state_change(Item#roster.subscription,
|
out_state_change(Item#roster.subscription,
|
||||||
@ -518,21 +742,21 @@ process_subscription(Direction, User, Server, JID1, Type, Reason) ->
|
|||||||
{none, AutoReply};
|
{none, AutoReply};
|
||||||
{none, none} when Item#roster.subscription == none,
|
{none, none} when Item#roster.subscription == none,
|
||||||
Item#roster.ask == in ->
|
Item#roster.ask == in ->
|
||||||
mnesia:delete({roster, {LUser, LServer, LJID}}),
|
del_roster_t(LUser, LServer, LJID),
|
||||||
{none, AutoReply};
|
{none, AutoReply};
|
||||||
{Subscription, Pending} ->
|
{Subscription, Pending} ->
|
||||||
NewItem = Item#roster{subscription = Subscription,
|
NewItem = Item#roster{subscription = Subscription,
|
||||||
ask = Pending,
|
ask = Pending,
|
||||||
askmessage = list_to_binary(AskMessage)},
|
askmessage = list_to_binary(AskMessage)},
|
||||||
mnesia:write(NewItem),
|
roster_subscribe_t(LUser, LServer, LJID, NewItem),
|
||||||
case roster_version_on_db(LServer) of
|
case roster_version_on_db(LServer) of
|
||||||
true -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
|
true -> write_roster_version_t(LUser, LServer);
|
||||||
false -> ok
|
false -> ok
|
||||||
end,
|
end,
|
||||||
{{push, NewItem}, AutoReply}
|
{{push, NewItem}, AutoReply}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case transaction(LServer, F) of
|
||||||
{atomic, {Push, AutoReply}} ->
|
{atomic, {Push, AutoReply}} ->
|
||||||
case AutoReply of
|
case AutoReply of
|
||||||
none ->
|
none ->
|
||||||
@ -663,6 +887,9 @@ in_auto_reply(_, _, _) -> none.
|
|||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
send_unsubscription_to_rosteritems(LUser, LServer),
|
send_unsubscription_to_rosteritems(LUser, LServer),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
@ -671,7 +898,12 @@ remove_user(User, Server) ->
|
|||||||
end,
|
end,
|
||||||
mnesia:index_read(roster, US, #roster.us))
|
mnesia:index_read(roster, US, #roster.us))
|
||||||
end,
|
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:
|
%% For each contact with Subscription:
|
||||||
%% Both or From, send a "unsubscribed" presence stanza;
|
%% Both or From, send a "unsubscribed" presence stanza;
|
||||||
@ -725,11 +957,36 @@ set_items(User, Server, SubEl) ->
|
|||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
lists:foreach(fun(El) ->
|
lists:foreach(
|
||||||
|
fun(El) ->
|
||||||
process_item_set_t(LUser, LServer, El)
|
process_item_set_t(LUser, LServer, El)
|
||||||
end, Els)
|
end, Els)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
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}) ->
|
process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
|
||||||
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),
|
||||||
@ -746,9 +1003,9 @@ process_item_set_t(LUser, LServer, {xmlelement, _Name, Attrs, Els}) ->
|
|||||||
Item2 = process_item_els(Item1, Els),
|
Item2 = process_item_els(Item1, Els),
|
||||||
case Item2#roster.subscription of
|
case Item2#roster.subscription of
|
||||||
remove ->
|
remove ->
|
||||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
del_roster_t(LUser, LServer, LJID);
|
||||||
_ ->
|
_ ->
|
||||||
mnesia:write(Item2)
|
update_roster_t(LUser, LServer, LJID, Item2)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
process_item_set_t(_LUser, _LServer, _) ->
|
process_item_set_t(_LUser, _LServer, _) ->
|
||||||
@ -761,7 +1018,7 @@ process_item_attrs_ws(Item, [{Attr, Val} | Attrs]) ->
|
|||||||
error ->
|
error ->
|
||||||
process_item_attrs_ws(Item, Attrs);
|
process_item_attrs_ws(Item, Attrs);
|
||||||
JID1 ->
|
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)
|
process_item_attrs_ws(Item#roster{jid = JID}, Attrs)
|
||||||
end;
|
end;
|
||||||
"name" ->
|
"name" ->
|
||||||
@ -795,6 +1052,11 @@ process_item_attrs_ws(Item, []) ->
|
|||||||
Item.
|
Item.
|
||||||
|
|
||||||
get_in_pending_subscriptions(Ls, User, Server) ->
|
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, ""),
|
JID = jlib:make_jid(User, Server, ""),
|
||||||
US = {JID#jid.luser, JID#jid.lserver},
|
US = {JID#jid.luser, JID#jid.lserver},
|
||||||
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
||||||
@ -825,30 +1087,99 @@ get_in_pending_subscriptions(Ls, User, Server) ->
|
|||||||
Result));
|
Result));
|
||||||
_ ->
|
_ ->
|
||||||
Ls
|
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.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
get_jid_info(_, User, Server, JID) ->
|
read_subscription_and_groups(User, Server, LJID) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LJID = jlib:jid_tolower(JID),
|
|
||||||
LServer = jlib:nameprep(Server),
|
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
|
case catch mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
|
||||||
[#roster{subscription = Subscription, groups = Groups}] ->
|
[#roster{subscription = Subscription, groups = Groups}] ->
|
||||||
{Subscription, 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)),
|
LRJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||||
if
|
if
|
||||||
LRJID == LJID ->
|
LRJID == LJID ->
|
||||||
{none, []};
|
{none, []};
|
||||||
true ->
|
true ->
|
||||||
case catch mnesia:dirty_read(
|
case read_subscription_and_groups(
|
||||||
roster, {LUser, LServer, LRJID}) of
|
User, Server, LRJID) of
|
||||||
[#roster{subscription = Subscription,
|
{Subscription, Groups} ->
|
||||||
groups = Groups}] ->
|
|
||||||
{Subscription, Groups};
|
{Subscription, Groups};
|
||||||
_ ->
|
error ->
|
||||||
{none, []}
|
{none, []}
|
||||||
end
|
end
|
||||||
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() ->
|
update_table() ->
|
||||||
Fields = record_info(fields, roster),
|
Fields = record_info(fields, roster),
|
||||||
@ -927,10 +1327,12 @@ webadmin_page(_, Host,
|
|||||||
webadmin_page(Acc, _, _) -> Acc.
|
webadmin_page(Acc, _, _) -> Acc.
|
||||||
|
|
||||||
user_roster(User, Server, Query, Lang) ->
|
user_roster(User, Server, Query, Lang) ->
|
||||||
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
|
LUser = jlib:nodeprep(User),
|
||||||
Items1 = mnesia:dirty_index_read(roster, US, #roster.us),
|
LServer = jlib:nameprep(Server),
|
||||||
|
US = {LUser, LServer},
|
||||||
|
Items1 = get_roster(LUser, LServer),
|
||||||
Res = user_roster_parse_query(User, Server, Items1, Query),
|
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),
|
SItems = lists:sort(Items),
|
||||||
FItems =
|
FItems =
|
||||||
case SItems of
|
case SItems of
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -63,7 +63,9 @@
|
|||||||
-record(sr_group, {group_host, opts}).
|
-record(sr_group, {group_host, opts}).
|
||||||
-record(sr_user, {us, group_host}).
|
-record(sr_user, {us, group_host}).
|
||||||
|
|
||||||
start(Host, _Opts) ->
|
start(Host, Opts) ->
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(sr_group,
|
mnesia:create_table(sr_group,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, sr_group)}]),
|
{attributes, record_info(fields, sr_group)}]),
|
||||||
@ -71,7 +73,10 @@ start(Host, _Opts) ->
|
|||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, sr_user)}]),
|
{attributes, record_info(fields, sr_user)}]),
|
||||||
mnesia:add_table_index(sr_user, group_host),
|
mnesia:add_table_index(sr_user, group_host);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(webadmin_menu_host, Host,
|
ejabberd_hooks:add(webadmin_menu_host, Host,
|
||||||
?MODULE, webadmin_menu, 70),
|
?MODULE, webadmin_menu, 70),
|
||||||
ejabberd_hooks:add(webadmin_page_host, Host,
|
ejabberd_hooks:add(webadmin_page_host, Host,
|
||||||
@ -181,7 +186,7 @@ get_user_roster(Items, US) ->
|
|||||||
get_vcard_module(Server) ->
|
get_vcard_module(Server) ->
|
||||||
Modules = gen_mod:loaded_modules(Server),
|
Modules = gen_mod:loaded_modules(Server),
|
||||||
[M || M <- Modules,
|
[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([], _, _) ->
|
get_rosteritem_name([], _, _) ->
|
||||||
"";
|
"";
|
||||||
@ -237,15 +242,14 @@ process_item(RosterItem, Host) ->
|
|||||||
[] ->
|
[] ->
|
||||||
%% Remove pending subscription by setting it
|
%% Remove pending subscription by setting it
|
||||||
%% unsubscribed.
|
%% unsubscribed.
|
||||||
Mod = get_roster_mod(ServerFrom),
|
|
||||||
|
|
||||||
%% Remove pending out subscription
|
%% Remove pending out subscription
|
||||||
Mod:out_subscription(UserTo, ServerTo,
|
mod_roster:out_subscription(UserTo, ServerTo,
|
||||||
jlib:make_jid(UserFrom, ServerFrom, ""),
|
jlib:make_jid(UserFrom, ServerFrom, ""),
|
||||||
unsubscribe),
|
unsubscribe),
|
||||||
|
|
||||||
%% Remove pending in subscription
|
%% Remove pending in subscription
|
||||||
Mod:in_subscription(aaaa, UserFrom, ServerFrom,
|
mod_roster:in_subscription(aaaa, UserFrom, ServerFrom,
|
||||||
jlib:make_jid(UserTo, ServerTo, ""),
|
jlib:make_jid(UserTo, ServerTo, ""),
|
||||||
unsubscribe, ""),
|
unsubscribe, ""),
|
||||||
|
|
||||||
@ -274,8 +278,6 @@ build_roster_record(User1, Server1, User2, Server2, Name2, Groups) ->
|
|||||||
|
|
||||||
set_new_rosteritems(UserFrom, ServerFrom,
|
set_new_rosteritems(UserFrom, ServerFrom,
|
||||||
UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
|
UserTo, ServerTo, ResourceTo, NameTo, GroupsFrom) ->
|
||||||
Mod = get_roster_mod(ServerFrom),
|
|
||||||
|
|
||||||
RIFrom = build_roster_record(UserFrom, ServerFrom,
|
RIFrom = build_roster_record(UserFrom, ServerFrom,
|
||||||
UserTo, ServerTo, NameTo, GroupsFrom),
|
UserTo, ServerTo, NameTo, GroupsFrom),
|
||||||
set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
|
set_item(UserFrom, ServerFrom, ResourceTo, RIFrom),
|
||||||
@ -287,20 +289,20 @@ set_new_rosteritems(UserFrom, ServerFrom,
|
|||||||
set_item(UserTo, ServerTo, "", RITo),
|
set_item(UserTo, ServerTo, "", RITo),
|
||||||
|
|
||||||
%% From requests
|
%% From requests
|
||||||
Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
|
mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribe),
|
||||||
Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
|
mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribe, ""),
|
||||||
|
|
||||||
%% To accepts
|
%% To accepts
|
||||||
Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
|
mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribed),
|
||||||
Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
|
mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribed, ""),
|
||||||
|
|
||||||
%% To requests
|
%% To requests
|
||||||
Mod:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
|
mod_roster:out_subscription(UserTo, ServerTo, JIDFrom, subscribe),
|
||||||
Mod:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
|
mod_roster:in_subscription(aaa, UserFrom, ServerFrom, JIDTo, subscribe, ""),
|
||||||
|
|
||||||
%% From accepts
|
%% From accepts
|
||||||
Mod:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
|
mod_roster:out_subscription(UserFrom, ServerFrom, JIDTo, subscribed),
|
||||||
Mod:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
|
mod_roster:in_subscription(aaa, UserTo, ServerTo, JIDFrom, subscribed, ""),
|
||||||
|
|
||||||
RIFrom.
|
RIFrom.
|
||||||
|
|
||||||
@ -361,15 +363,13 @@ in_subscription(Acc, User, Server, JID, Type, _Reason) ->
|
|||||||
process_subscription(in, User, Server, JID, Type, Acc).
|
process_subscription(in, User, Server, JID, Type, Acc).
|
||||||
|
|
||||||
out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
|
out_subscription(UserFrom, ServerFrom, JIDTo, unsubscribed) ->
|
||||||
Mod = get_roster_mod(ServerFrom),
|
|
||||||
|
|
||||||
%% Remove pending out subscription
|
%% Remove pending out subscription
|
||||||
#jid{luser = UserTo, lserver = ServerTo} = JIDTo,
|
#jid{luser = UserTo, lserver = ServerTo} = JIDTo,
|
||||||
JIDFrom = jlib:make_jid(UserFrom, UserTo, ""),
|
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
|
%% 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);
|
process_subscription(out, UserFrom, ServerFrom, JIDTo, unsubscribed, false);
|
||||||
out_subscription(User, Server, JID, Type) ->
|
out_subscription(User, Server, JID, Type) ->
|
||||||
@ -401,32 +401,70 @@ process_subscription(Direction, User, Server, JID, _Type, Acc) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
list_groups(Host) ->
|
list_groups(Host) ->
|
||||||
|
list_groups(Host, gen_mod:db_type(Host, ?MODULE)).
|
||||||
|
|
||||||
|
list_groups(Host, mnesia) ->
|
||||||
mnesia:dirty_select(
|
mnesia:dirty_select(
|
||||||
sr_group,
|
sr_group,
|
||||||
[{#sr_group{group_host = {'$1', '$2'},
|
[{#sr_group{group_host = {'$1', '$2'},
|
||||||
_ = '_'},
|
_ = '_'},
|
||||||
[{'==', '$2', Host}],
|
[{'==', '$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) ->
|
||||||
|
groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)).
|
||||||
|
|
||||||
|
groups_with_opts(Host, mnesia) ->
|
||||||
Gs = mnesia:dirty_select(
|
Gs = mnesia:dirty_select(
|
||||||
sr_group,
|
sr_group,
|
||||||
[{#sr_group{group_host={'$1', Host}, opts='$2', _='_'},
|
[{#sr_group{group_host={'$1', Host}, opts='$2', _='_'},
|
||||||
[],
|
[],
|
||||||
[['$1','$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, []).
|
create_group(Host, Group, []).
|
||||||
|
|
||||||
create_group(Host, Group, Opts) ->
|
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},
|
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(R)
|
mnesia:write(R)
|
||||||
end,
|
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) ->
|
||||||
|
delete_group(Host, Group, gen_mod:db_type(Host, ?MODULE)).
|
||||||
|
|
||||||
|
delete_group(Host, Group, mnesia) ->
|
||||||
GroupHost = {Group, Host},
|
GroupHost = {Group, Host},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
%% Delete the group ...
|
%% Delete the group ...
|
||||||
@ -437,53 +475,102 @@ delete_group(Host, Group) ->
|
|||||||
mnesia:delete_object(UserEntry)
|
mnesia:delete_object(UserEntry)
|
||||||
end, Users)
|
end, Users)
|
||||||
end,
|
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) ->
|
||||||
|
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
|
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||||
[#sr_group{opts = Opts}] ->
|
[#sr_group{opts = Opts}] ->
|
||||||
Opts;
|
Opts;
|
||||||
_ ->
|
_ ->
|
||||||
error
|
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.
|
end.
|
||||||
|
|
||||||
set_group_opts(Host, Group, Opts) ->
|
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},
|
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(R)
|
mnesia:write(R)
|
||||||
end,
|
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) ->
|
get_user_groups(US) ->
|
||||||
Host = element(2, 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
|
case catch mnesia:dirty_read(sr_user, US) of
|
||||||
Rs when is_list(Rs) ->
|
Rs when is_list(Rs) ->
|
||||||
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
|
[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) ->
|
is_group_enabled(Host1, Group1) ->
|
||||||
{Host, Group} = split_grouphost(Host1, Group1),
|
{Host, Group} = split_grouphost(Host1, Group1),
|
||||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
case get_group_opts(Host, Group) of
|
||||||
[#sr_group{opts = Opts}] ->
|
error ->
|
||||||
not lists:member(disabled, Opts);
|
false;
|
||||||
_ ->
|
Opts ->
|
||||||
false
|
not lists:member(disabled, Opts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
|
%% @spec (Host::string(), Group::string(), Opt::atom(), Default) -> OptValue | Default
|
||||||
get_group_opt(Host, Group, Opt, Default) ->
|
get_group_opt(Host, Group, Opt, Default) ->
|
||||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
case get_group_opts(Host, Group) of
|
||||||
[#sr_group{opts = Opts}] ->
|
error ->
|
||||||
|
Default;
|
||||||
|
Opts ->
|
||||||
case lists:keysearch(Opt, 1, Opts) of
|
case lists:keysearch(Opt, 1, Opts) of
|
||||||
{value, {_, Val}} ->
|
{value, {_, Val}} ->
|
||||||
Val;
|
Val;
|
||||||
false ->
|
false ->
|
||||||
Default
|
Default
|
||||||
end;
|
end
|
||||||
_ ->
|
|
||||||
Default
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_online_users(Host) ->
|
get_online_users(Host) ->
|
||||||
@ -522,6 +609,9 @@ get_group_users(Host, Group, GroupOpts) ->
|
|||||||
|
|
||||||
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
|
%% @spec (Host::string(), Group::string()) -> [{User::string(), Server::string()}]
|
||||||
get_group_explicit_users(Host, Group) ->
|
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(
|
Read = (catch mnesia:dirty_index_read(
|
||||||
sr_user,
|
sr_user,
|
||||||
{Group, Host},
|
{Group, Host},
|
||||||
@ -531,6 +621,21 @@ get_group_explicit_users(Host, Group) ->
|
|||||||
[R#sr_user.us || R <- Rs];
|
[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.
|
end.
|
||||||
|
|
||||||
get_group_name(Host1, Group1) ->
|
get_group_name(Host1, Group1) ->
|
||||||
@ -581,14 +686,29 @@ get_special_displayed_groups(GroupsOpts) ->
|
|||||||
%% for the list of groups of that server that user is member
|
%% for the list of groups of that server that user is member
|
||||||
%% get the list of groups displayed
|
%% get the list of groups displayed
|
||||||
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||||
Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
|
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) ->
|
Rs when is_list(Rs) ->
|
||||||
[{Group, proplists:get_value(Group, GroupsOpts, [])} ||
|
[{Group, proplists:get_value(Group, GroupsOpts, [])} ||
|
||||||
#sr_user{group_host = {Group, H}} <- Rs, H == LServer];
|
#sr_user{group_host = {Group, H}} <- Rs, H == LServer];
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end,
|
end;
|
||||||
displayed_groups(GroupsOpts, Groups).
|
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
|
%% @doc Get the list of groups that are displayed to this user
|
||||||
get_user_displayed_groups(US) ->
|
get_user_displayed_groups(US) ->
|
||||||
@ -607,13 +727,26 @@ get_user_displayed_groups(US) ->
|
|||||||
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
|
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
|
||||||
|
|
||||||
is_user_in_group(US, Group, Host) ->
|
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(
|
case catch mnesia:dirty_match_object(
|
||||||
#sr_user{us=US, group_host={Group, Host}}) of
|
#sr_user{us=US, group_host={Group, Host}}) of
|
||||||
[] -> lists:member(US, get_group_users(Host, Group));
|
[] -> lists:member(US, get_group_users(Host, Group));
|
||||||
_ -> true
|
_ -> 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.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
||||||
add_user_to_group(Host, US, Group) ->
|
add_user_to_group(Host, US, Group) ->
|
||||||
{LUser, LServer} = US,
|
{LUser, LServer} = US,
|
||||||
@ -634,12 +767,26 @@ add_user_to_group(Host, US, Group) ->
|
|||||||
push_user_to_displayed(LUser, LServer, Group, Host, both),
|
push_user_to_displayed(LUser, LServer, Group, Host, both),
|
||||||
%% Push members of groups that are displayed to this group
|
%% Push members of groups that are displayed to this group
|
||||||
push_displayed_to_user(LUser, LServer, Group, Host, both),
|
push_displayed_to_user(LUser, LServer, Group, Host, both),
|
||||||
|
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}},
|
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(R)
|
mnesia:write(R)
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F)
|
mnesia:transaction(F);
|
||||||
end.
|
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) ->
|
push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||||
GroupsOpts = groups_with_opts(LServer),
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
@ -648,7 +795,6 @@ push_displayed_to_user(LUser, LServer, Group, Host, Subscription) ->
|
|||||||
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
|
[push_members_to_user(LUser, LServer, DGroup, Host, Subscription) || DGroup <- DisplayedGroups].
|
||||||
|
|
||||||
remove_user_from_group(Host, US, Group) ->
|
remove_user_from_group(Host, US, Group) ->
|
||||||
GroupHost = {Group, Host},
|
|
||||||
{LUser, LServer} = US,
|
{LUser, LServer} = US,
|
||||||
case ejabberd_regexp:run(LUser, "^@.+@$") of
|
case ejabberd_regexp:run(LUser, "^@.+@$") of
|
||||||
match ->
|
match ->
|
||||||
@ -662,11 +808,8 @@ remove_user_from_group(Host, US, Group) ->
|
|||||||
end,
|
end,
|
||||||
?MODULE:set_group_opts(Host, Group, NewGroupOpts);
|
?MODULE:set_group_opts(Host, Group, NewGroupOpts);
|
||||||
nomatch ->
|
nomatch ->
|
||||||
R = #sr_user{us = US, group_host = GroupHost},
|
Result = remove_user_from_group(Host, US, Group,
|
||||||
F = fun() ->
|
gen_mod:db_type(Host, ?MODULE)),
|
||||||
mnesia:delete_object(R)
|
|
||||||
end,
|
|
||||||
Result = mnesia:transaction(F),
|
|
||||||
%% Push removal of the old user to members of groups where the group that this user was members was displayed
|
%% 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_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 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
|
Result
|
||||||
end.
|
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) ->
|
push_members_to_user(LUser, LServer, Group, Host, Subscription) ->
|
||||||
GroupsOpts = groups_with_opts(LServer),
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
@ -1099,14 +1258,6 @@ shared_roster_group_parse_query(Host, Group, Query) ->
|
|||||||
nothing
|
nothing
|
||||||
end.
|
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) ->
|
get_opt(Opts, Opt, Default) ->
|
||||||
case lists:keysearch(Opt, 1, Opts) of
|
case lists:keysearch(Opt, 1, Opts) of
|
||||||
{value, {_, Val}} ->
|
{value, {_, Val}} ->
|
||||||
@ -1125,3 +1276,12 @@ split_grouphost(Host, Group) ->
|
|||||||
[_] ->
|
[_] ->
|
||||||
{Host, Group}
|
{Host, Group}
|
||||||
end.
|
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).
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% File : mod_vcard.erl
|
%%% File : mod_vcard.erl
|
||||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
%%% 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>
|
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||||
%%%
|
%%%
|
||||||
%%%
|
%%%
|
||||||
@ -61,11 +61,16 @@
|
|||||||
-define(PROCNAME, ejabberd_mod_vcard).
|
-define(PROCNAME, ejabberd_mod_vcard).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
mnesia:create_table(vcard, [{disc_only_copies, [node()]},
|
case gen_mod:db_type(Opts) of
|
||||||
{attributes, record_info(fields, vcard)}]),
|
mnesia ->
|
||||||
|
mnesia:create_table(vcard,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, vcard)}]),
|
||||||
mnesia:create_table(vcard_search,
|
mnesia:create_table(vcard_search,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, vcard_search)}]),
|
{attributes,
|
||||||
|
record_info(fields, vcard_search)}]),
|
||||||
update_tables(),
|
update_tables(),
|
||||||
mnesia:add_table_index(vcard_search, luser),
|
mnesia:add_table_index(vcard_search, luser),
|
||||||
mnesia:add_table_index(vcard_search, lfn),
|
mnesia:add_table_index(vcard_search, lfn),
|
||||||
@ -78,8 +83,10 @@ start(Host, Opts) ->
|
|||||||
mnesia:add_table_index(vcard_search, llocality),
|
mnesia:add_table_index(vcard_search, llocality),
|
||||||
mnesia:add_table_index(vcard_search, lemail),
|
mnesia:add_table_index(vcard_search, lemail),
|
||||||
mnesia:add_table_index(vcard_search, lorgname),
|
mnesia:add_table_index(vcard_search, lorgname),
|
||||||
mnesia:add_table_index(vcard_search, lorgunit),
|
mnesia:add_table_index(vcard_search, lorgunit);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(remove_user, Host,
|
ejabberd_hooks:add(remove_user, Host,
|
||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
|
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;
|
end;
|
||||||
get ->
|
get ->
|
||||||
#jid{luser = LUser, lserver = LServer} = To,
|
#jid{luser = LUser, lserver = LServer} = To,
|
||||||
|
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},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:read({vcard, US})
|
mnesia:read({vcard, US})
|
||||||
end,
|
end,
|
||||||
Els = case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, Rs} ->
|
{atomic, Rs} ->
|
||||||
lists:map(fun(R) ->
|
lists:map(fun(R) ->
|
||||||
R#vcard.vcard
|
R#vcard.vcard
|
||||||
end, Rs);
|
end, Rs);
|
||||||
{aborted, _Reason} ->
|
{aborted, _Reason} ->
|
||||||
[]
|
error
|
||||||
end,
|
end;
|
||||||
IQ#iq{type = result, sub_el = Els}
|
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.
|
end.
|
||||||
|
|
||||||
set_vcard(User, LServer, VCARD) ->
|
set_vcard(User, LServer, VCARD) ->
|
||||||
@ -231,8 +264,6 @@ set_vcard(User, LServer, VCARD) ->
|
|||||||
LOrgName = string2lower(OrgName),
|
LOrgName = string2lower(OrgName),
|
||||||
LOrgUnit = string2lower(OrgUnit),
|
LOrgUnit = string2lower(OrgUnit),
|
||||||
|
|
||||||
US = {LUser, LServer},
|
|
||||||
|
|
||||||
if
|
if
|
||||||
(LUser == error) or
|
(LUser == error) or
|
||||||
(LFN == error) or
|
(LFN == error) or
|
||||||
@ -248,6 +279,9 @@ set_vcard(User, LServer, VCARD) ->
|
|||||||
(LOrgUnit == error) ->
|
(LOrgUnit == error) ->
|
||||||
{error, badarg};
|
{error, badarg};
|
||||||
true ->
|
true ->
|
||||||
|
case gen_mod:db_type(LServer, ?MODULE) of
|
||||||
|
mnesia ->
|
||||||
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||||||
mnesia:write(
|
mnesia:write(
|
||||||
@ -267,7 +301,44 @@ set_vcard(User, LServer, VCARD) ->
|
|||||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F),
|
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])
|
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -481,14 +552,34 @@ search_result(Lang, JID, ServerHost, Data) ->
|
|||||||
?TLFIELD("text-single", "Email", "email"),
|
?TLFIELD("text-single", "Email", "email"),
|
||||||
?TLFIELD("text-single", "Organization Name", "orgname"),
|
?TLFIELD("text-single", "Organization Name", "orgname"),
|
||||||
?TLFIELD("text-single", "Organization Unit", "orgunit")
|
?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),
|
-define(FIELD(Var, Val),
|
||||||
{xmlelement, "field", [{"var", Var}],
|
{xmlelement, "field", [{"var", Var}],
|
||||||
[{xmlelement, "value", [],
|
[{xmlelement, "value", [],
|
||||||
[{xmlcdata, Val}]}]}).
|
[{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,
|
{User, Server} = R#vcard_search.user,
|
||||||
{xmlelement, "item", [],
|
{xmlelement, "item", [],
|
||||||
[
|
[
|
||||||
@ -509,9 +600,13 @@ record_to_item(R) ->
|
|||||||
|
|
||||||
|
|
||||||
search(LServer, Data) ->
|
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,
|
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE,
|
||||||
allow_return_all, false),
|
allow_return_all, false),
|
||||||
|
search(LServer, MatchSpec, AllowReturnAll, DBType).
|
||||||
|
|
||||||
|
search(LServer, MatchSpec, AllowReturnAll, mnesia) ->
|
||||||
if
|
if
|
||||||
(MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
|
(MatchSpec == #vcard_search{_ = '_'}) and (not AllowReturnAll) ->
|
||||||
[];
|
[];
|
||||||
@ -535,17 +630,58 @@ search(LServer, Data) ->
|
|||||||
lists:sublist(Rs, ?JUD_MATCHES)
|
lists:sublist(Rs, ?JUD_MATCHES)
|
||||||
end
|
end
|
||||||
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.
|
end.
|
||||||
|
|
||||||
|
make_matchspec(LServer, Data, mnesia) ->
|
||||||
make_matchspec(LServer, Data) ->
|
|
||||||
GlobMatch = #vcard_search{_ = '_'},
|
GlobMatch = #vcard_search{_ = '_'},
|
||||||
Match = filter_fields(Data, GlobMatch, LServer),
|
Match = filter_fields(Data, GlobMatch, LServer, mnesia),
|
||||||
Match.
|
|
||||||
|
|
||||||
filter_fields([], Match, _LServer) ->
|
|
||||||
Match;
|
Match;
|
||||||
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
make_matchspec(LServer, Data, odbc) ->
|
||||||
|
filter_fields(Data, "", LServer, odbc).
|
||||||
|
|
||||||
|
filter_fields([], Match, _LServer, mnesia) ->
|
||||||
|
Match;
|
||||||
|
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 /= "") ->
|
when is_list(Val) and (Val /= "") ->
|
||||||
LVal = string2lower(Val),
|
LVal = string2lower(Val),
|
||||||
NewMatch = case SVar of
|
NewMatch = case SVar of
|
||||||
@ -571,9 +707,46 @@ filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
|||||||
"orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
|
"orgunit" -> Match#vcard_search{lorgunit = make_val(LVal)};
|
||||||
_ -> Match
|
_ -> Match
|
||||||
end,
|
end,
|
||||||
filter_fields(Ds, NewMatch, LServer);
|
filter_fields(Ds, NewMatch, LServer, mnesia);
|
||||||
filter_fields([_ | Ds], Match, LServer) ->
|
filter_fields([{SVar, [Val]} | Ds], Match, LServer, odbc)
|
||||||
filter_fields(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, 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) ->
|
make_val(Val) ->
|
||||||
case lists:suffix("*", Val) of
|
case lists:suffix("*", Val) of
|
||||||
@ -679,13 +852,21 @@ reindex_vcards() ->
|
|||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = jlib:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
|
remove_user(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer, mnesia) ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({vcard, US}),
|
mnesia:delete({vcard, US}),
|
||||||
mnesia:delete({vcard_search, US})
|
mnesia:delete({vcard_search, US})
|
||||||
end,
|
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_tables() ->
|
||||||
update_vcard_table(),
|
update_vcard_table(),
|
||||||
|
@ -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, "';"]]).
|
|
@ -26,10 +26,16 @@
|
|||||||
%% gen_mod callbacks
|
%% gen_mod callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
start(Host, _Opts) ->
|
start(Host, Opts) ->
|
||||||
|
case gen_mod:db_type(Opts) of
|
||||||
|
mnesia ->
|
||||||
mnesia:create_table(vcard_xupdate,
|
mnesia:create_table(vcard_xupdate,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, vcard_xupdate)}]),
|
{attributes,
|
||||||
|
record_info(fields, vcard_xupdate)}]);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_hooks:add(c2s_update_presence, Host,
|
ejabberd_hooks:add(c2s_update_presence, Host,
|
||||||
?MODULE, update_presence, 100),
|
?MODULE, update_presence, 100),
|
||||||
ejabberd_hooks:add(vcard_set, Host,
|
ejabberd_hooks:add(vcard_set, Host,
|
||||||
@ -68,28 +74,66 @@ vcard_set(LUser, LServer, VCARD) ->
|
|||||||
ejabberd_sm:force_update_presence(US).
|
ejabberd_sm:force_update_presence(US).
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Mnesia storage
|
%% Storage
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
add_xupdate(LUser, LServer, Hash) ->
|
add_xupdate(LUser, LServer, Hash) ->
|
||||||
|
add_xupdate(LUser, LServer, Hash, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
add_xupdate(LUser, LServer, Hash, mnesia) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash})
|
mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash})
|
||||||
end,
|
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) ->
|
||||||
|
get_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
get_xupdate(LUser, LServer, mnesia) ->
|
||||||
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of
|
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of
|
||||||
[#vcard_xupdate{hash = Hash}] ->
|
[#vcard_xupdate{hash = Hash}] ->
|
||||||
Hash;
|
Hash;
|
||||||
_ ->
|
_ ->
|
||||||
undefined
|
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.
|
end.
|
||||||
|
|
||||||
remove_xupdate(LUser, LServer) ->
|
remove_xupdate(LUser, LServer) ->
|
||||||
|
remove_xupdate(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||||
|
|
||||||
|
remove_xupdate(LUser, LServer, mnesia) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete({vcard_xupdate, {LUser, LServer}})
|
mnesia:delete({vcard_xupdate, {LUser, LServer}})
|
||||||
end,
|
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
|
%%% Presence stanza rebuilding
|
||||||
|
@ -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}.
|
|
@ -1564,7 +1564,6 @@ list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
|
|||||||
[list_given_users(Host, Sub, "../../", Lang, URLFunc)].
|
[list_given_users(Host, Sub, "../../", Lang, URLFunc)].
|
||||||
|
|
||||||
list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
|
list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
|
||||||
ModLast = get_lastactivity_module(Host),
|
|
||||||
ModOffline = get_offlinemsg_module(Host),
|
ModOffline = get_offlinemsg_module(Host),
|
||||||
?XE("table",
|
?XE("table",
|
||||||
[?XE("thead",
|
[?XE("thead",
|
||||||
@ -1583,7 +1582,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
|
|||||||
FLast =
|
FLast =
|
||||||
case ejabberd_sm:get_user_resources(User, Server) of
|
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 ->
|
not_found ->
|
||||||
?T("Never");
|
?T("Never");
|
||||||
{ok, Shift, _Status} ->
|
{ok, Shift, _Status} ->
|
||||||
@ -1618,22 +1617,17 @@ get_offlinemsg_length(ModOffline, User, Server) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_offlinemsg_module(Server) ->
|
get_offlinemsg_module(Server) ->
|
||||||
case [mod_offline, mod_offline_odbc] -- gen_mod:loaded_modules(Server) of
|
case gen_mod:is_loaded(Server, mod_offline) of
|
||||||
[mod_offline, mod_offline_odbc] -> none;
|
true ->
|
||||||
[mod_offline_odbc] -> mod_offline;
|
mod_offline;
|
||||||
[mod_offline] -> mod_offline_odbc
|
false ->
|
||||||
end.
|
none
|
||||||
|
|
||||||
get_lastactivity_module(Server) ->
|
|
||||||
case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
|
|
||||||
true -> mod_last;
|
|
||||||
_ -> mod_last_odbc
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_lastactivity_menuitem_list(Server) ->
|
get_lastactivity_menuitem_list(Server) ->
|
||||||
case get_lastactivity_module(Server) of
|
case gen_mod:db_type(Server, mod_last) of
|
||||||
mod_last -> [{"last-activity", "Last Activity"}];
|
mnesia -> [{"last-activity", "Last Activity"}];
|
||||||
mod_last_odbc -> []
|
_ -> []
|
||||||
end.
|
end.
|
||||||
|
|
||||||
us_to_list({User, Server}) ->
|
us_to_list({User, Server}) ->
|
||||||
@ -1735,10 +1729,9 @@ user_info(User, Server, Query, Lang) ->
|
|||||||
UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
|
UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
|
||||||
[User, Server, Lang]),
|
[User, Server, Lang]),
|
||||||
%% Code copied from list_given_users/5:
|
%% Code copied from list_given_users/5:
|
||||||
ModLast = get_lastactivity_module(Server),
|
|
||||||
LastActivity = case ejabberd_sm:get_user_resources(User, Server) of
|
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 ->
|
not_found ->
|
||||||
?T("Never");
|
?T("Never");
|
||||||
{ok, Shift, _Status} ->
|
{ok, Shift, _Status} ->
|
||||||
|
Loading…
Reference in New Issue
Block a user