mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-26 16:26:24 +01:00
Fix ejabberd modules
This commit is contained in:
parent
58bed2cbff
commit
597c1c87d4
@ -2,10 +2,10 @@
|
|||||||
%%% File : ejabberd_auth_storage.erl
|
%%% File : ejabberd_auth_storage.erl
|
||||||
%%% Author : Alexey Shchepin <alexey@process-one.net>, Stephan Maka
|
%%% Author : Alexey Shchepin <alexey@process-one.net>, Stephan Maka
|
||||||
%%% Purpose : Authentification via gen_storage
|
%%% Purpose : Authentification via gen_storage
|
||||||
%%% Created : 16 Sep 2008 Stephan Maka
|
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||||
%%%
|
%%%
|
||||||
%%%
|
%%%
|
||||||
%%% ejabberd, Copyright (C) 2002-2008 ProcessOne
|
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||||
%%%
|
%%%
|
||||||
%%% This program is free software; you can redistribute it and/or
|
%%% This program is free software; you can redistribute it and/or
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
%%% modify it under the terms of the GNU General Public License as
|
||||||
@ -24,6 +24,31 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / passwd
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% password = string()
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / users
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% password = text
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / passwd
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / users
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / passwd
|
||||||
|
%%% user_host = {Username::string(), Host::string()}
|
||||||
|
%%% password = string()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / passwd
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% password = text
|
||||||
|
|
||||||
-module(ejabberd_auth_storage).
|
-module(ejabberd_auth_storage).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -48,32 +73,59 @@
|
|||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
-record(passwd, {us, password}).
|
-record(passwd, {user_host, password}).
|
||||||
|
-record(reg_users_counter, {vhost, count}).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% @spec (Host) -> ok
|
||||||
|
%% Host = string()
|
||||||
|
|
||||||
start(Host) ->
|
start(Host) ->
|
||||||
Backend =
|
Backend =
|
||||||
case ejabberd_config:get_local_option({auth_storage, Host}) of
|
case ejabberd_config:get_local_option({auth_storage, Host}) of
|
||||||
undefined -> mnesia;
|
undefined -> mnesia;
|
||||||
B -> B
|
B -> B
|
||||||
end,
|
end,
|
||||||
gen_storage:create_table(Backend, Host, passwd,
|
HostB = list_to_binary(Host),
|
||||||
|
gen_storage:create_table(Backend, HostB, passwd,
|
||||||
[{odbc_host, Host},
|
[{odbc_host, Host},
|
||||||
{disc_copies, [node()]},
|
{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, passwd)},
|
{attributes, record_info(fields, passwd)},
|
||||||
{types, [{us, {text, text}}]}
|
{types, [{user_host, {text, text}}]}
|
||||||
]),
|
]),
|
||||||
update_table(Host),
|
update_table(Host, Backend),
|
||||||
|
mnesia:create_table(reg_users_counter,
|
||||||
|
[{ram_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, reg_users_counter)}]),
|
||||||
|
update_reg_users_counter_table(Host),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
update_reg_users_counter_table(Server) ->
|
||||||
|
Set = get_vh_registered_users(Server),
|
||||||
|
Size = length(Set),
|
||||||
|
LServer = exmpp_jid:prep_domain(exmpp_jid:parse(Server)),
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:write(#reg_users_counter{vhost = LServer,
|
||||||
|
count = Size})
|
||||||
|
end,
|
||||||
|
mnesia:sync_dirty(F).
|
||||||
|
|
||||||
|
%% @spec () -> bool()
|
||||||
|
|
||||||
plain_password_required() ->
|
plain_password_required() ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> bool()
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
|
||||||
check_password(User, Server, Password) ->
|
check_password(User, Server, Password) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||||
[#passwd{password = Password}] ->
|
[#passwd{password = Password}] ->
|
||||||
@ -82,15 +134,22 @@ check_password(User, Server, Password) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_password(User, Server, Password, StreamID, Digest) ->
|
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
|
||||||
LUser = jlib:nodeprep(User),
|
%% User = string()
|
||||||
LServer = jlib:nameprep(Server),
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
%% Digest = string()
|
||||||
|
%% DigestGen = function()
|
||||||
|
|
||||||
|
check_password(User, Server, Password, Digest, DigestGen) ->
|
||||||
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||||
[#passwd{password = Passwd}] ->
|
[#passwd{password = Passwd}] ->
|
||||||
DigRes = if
|
DigRes = if
|
||||||
Digest /= "" ->
|
Digest /= "" ->
|
||||||
Digest == sha:sha(StreamID ++ Passwd);
|
Digest == DigestGen(Passwd);
|
||||||
true ->
|
true ->
|
||||||
false
|
false
|
||||||
end,
|
end,
|
||||||
@ -103,61 +162,101 @@ check_password(User, Server, Password, StreamID, Digest) ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
||||||
%% ok | {error, invalid_jid}
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = (catch exmpp_stringprep:nodeprep(User)),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = (catch exmpp_stringprep:nameprep(Server)),
|
||||||
US = {LUser, LServer},
|
case {LUser, LServer} of
|
||||||
if
|
{{stringprep, _, invalid_string, _}, _} ->
|
||||||
(LUser == error) or (LServer == error) ->
|
|
||||||
{error, invalid_jid};
|
{error, invalid_jid};
|
||||||
true ->
|
{_, {stringprep, _, invalid_string, _}} ->
|
||||||
|
{error, invalid_jid};
|
||||||
|
US ->
|
||||||
%% TODO: why is this a transaction?
|
%% TODO: why is this a transaction?
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#passwd{us = US,
|
#passwd{user_host = US,
|
||||||
password = Password})
|
password = Password})
|
||||||
end,
|
end,
|
||||||
{atomic, ok} = gen_storage:transaction(LServer, passwd, F),
|
{atomic, ok} = gen_storage:transaction(LServer, passwd, F),
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {aborted, Reason}
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
|
||||||
try_register(User, Server, Password) ->
|
try_register(User, Server, Password) ->
|
||||||
LUser = jlib:nodeprep(User),
|
LUser = (catch exmpp_stringprep:nodeprep(User)),
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = (catch exmpp_stringprep:nameprep(Server)),
|
||||||
US = {LUser, LServer},
|
case {LUser, LServer} of
|
||||||
if
|
{{stringprep, _, invalid_string, _}, _} ->
|
||||||
(LUser == error) or (LServer == error) ->
|
|
||||||
{error, invalid_jid};
|
{error, invalid_jid};
|
||||||
true ->
|
{_, {stringprep, _, invalid_string, _}} ->
|
||||||
|
{error, invalid_jid};
|
||||||
|
US ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case gen_storage:read(LServer, {passwd, US}) of
|
case gen_storage:read(LServer, {passwd, US}) of
|
||||||
[] ->
|
[] ->
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#passwd{us = US,
|
#passwd{user_host = US,
|
||||||
password = Password}),
|
password = Password}),
|
||||||
|
mnesia:dirty_update_counter(
|
||||||
|
reg_users_counter,
|
||||||
|
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), 1),
|
||||||
ok;
|
ok;
|
||||||
[_E] ->
|
[_E] ->
|
||||||
exists
|
exists
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
%% TODO: transaction retval?
|
%% TODO: transaction return value?
|
||||||
gen_storage:transaction(LServer, passwd, F)
|
gen_storage:transaction(LServer, passwd, F)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Get all registered users in Mnesia
|
%% @spec () -> [{LUser, LServer}]
|
||||||
|
%% LUser = string()
|
||||||
|
%% LServer = string()
|
||||||
|
%% @doc Get all registered users in Mnesia.
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
dirty_get_registered_users() ->
|
||||||
%% TODO:
|
%% TODO:
|
||||||
exit(not_implemented).
|
exit(not_implemented).
|
||||||
|
|
||||||
|
%% @spec (Server) -> [{LUser, LServer}]
|
||||||
|
%% Server = string()
|
||||||
|
%% LUser = string()
|
||||||
|
%% LServer = string()
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
get_vh_registered_users(Server) ->
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
lists:map(fun(#passwd{us = US}) ->
|
lists:map(fun(#passwd{user_host = US}) ->
|
||||||
US
|
US
|
||||||
end,
|
end,
|
||||||
gen_storage:dirty_select(LServer, passwd,
|
gen_storage:dirty_select(LServer, passwd,
|
||||||
[{'=', us, {'_', LServer}}])).
|
[{'=', user_host, {'_', LServer}}])).
|
||||||
|
|
||||||
|
%% @spec (Server, Opts) -> [{LUser, LServer}]
|
||||||
|
%% Server = string()
|
||||||
|
%% Opts = [{Opt, Val}]
|
||||||
|
%% Opt = atom()
|
||||||
|
%% Val = term()
|
||||||
|
%% LUser = string()
|
||||||
|
%% LServer = string()
|
||||||
|
%% @doc Return the registered users for the specified host.
|
||||||
|
%%
|
||||||
|
%% `Opts' can be one of the following:
|
||||||
|
%% <ul>
|
||||||
|
%% <li>`[{from, integer()}, {to, integer()}]'</li>
|
||||||
|
%% <li>`[{limit, integer()}, {offset, integer()}]'</li>
|
||||||
|
%% <li>`[{prefix, string()}]'</li>
|
||||||
|
%% <li>`[{prefix, string()}, {from, integer()}, {to, integer()}]'</li>
|
||||||
|
%% <li>`[{prefix, string()}, {limit, integer()}, {offset, integer()}]'</li>
|
||||||
|
%% </ul>
|
||||||
|
|
||||||
get_vh_registered_users(Server, [{from, Start}, {to, End}])
|
get_vh_registered_users(Server, [{from, Start}, {to, End}])
|
||||||
when is_integer(Start) and is_integer(End) ->
|
when is_integer(Start) and is_integer(End) ->
|
||||||
@ -205,9 +304,27 @@ get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offs
|
|||||||
get_vh_registered_users(Server, _) ->
|
get_vh_registered_users(Server, _) ->
|
||||||
get_vh_registered_users(Server).
|
get_vh_registered_users(Server).
|
||||||
|
|
||||||
|
%% @spec (Server) -> Users_Number
|
||||||
|
%% Server = string()
|
||||||
|
%% Users_Number = integer()
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
get_vh_registered_users_number(Server) ->
|
||||||
Set = get_vh_registered_users(Server),
|
LServer = exmpp_jid:prep_domain(exmpp_jid:parse(Server)),
|
||||||
length(Set).
|
Query = mnesia:dirty_select(
|
||||||
|
reg_users_counter,
|
||||||
|
[{#reg_users_counter{vhost = LServer, count = '$1'},
|
||||||
|
[],
|
||||||
|
['$1']}]),
|
||||||
|
case Query of
|
||||||
|
[Count] ->
|
||||||
|
Count;
|
||||||
|
_ -> 0
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @spec (Server, [{prefix, Prefix}]) -> Users_Number
|
||||||
|
%% Server = string()
|
||||||
|
%% Prefix = string()
|
||||||
|
%% Users_Number = integer()
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix) ->
|
get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix) ->
|
||||||
Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
|
Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
|
||||||
@ -216,59 +333,110 @@ get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix)
|
|||||||
get_vh_registered_users_number(Server, _) ->
|
get_vh_registered_users_number(Server, _) ->
|
||||||
get_vh_registered_users_number(Server).
|
get_vh_registered_users_number(Server).
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> Password | false
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
|
||||||
get_password(User, Server) ->
|
get_password(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
try
|
||||||
LServer = jlib:nameprep(Server),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||||
[#passwd{password = Password}] ->
|
[#passwd{password = Password}] ->
|
||||||
Password;
|
Password;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> Password | nil()
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
get_password_s(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
try
|
||||||
LServer = jlib:nameprep(Server),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||||
[#passwd{password = Password}] ->
|
[#passwd{password = Password}] ->
|
||||||
Password;
|
Password;
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> true | false | {error, Error}
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
try
|
||||||
LServer = jlib:nameprep(Server),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||||
[] ->
|
[] ->
|
||||||
false;
|
false;
|
||||||
[_] ->
|
[_] ->
|
||||||
true;
|
true;
|
||||||
|
Other ->
|
||||||
|
{error, Other}
|
||||||
|
end
|
||||||
|
catch
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server) -> ok
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% @doc Remove user.
|
||||||
|
%% Note: it returns ok even if there was some problem removing the user.
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jlib:nodeprep(User),
|
try
|
||||||
LServer = jlib:nameprep(Server),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
gen_storage:delete(LServer, {passwd, US})
|
gen_storage:delete(LServer, {passwd, US}),
|
||||||
|
mnesia:dirty_update_counter(reg_users_counter,
|
||||||
|
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1)
|
||||||
end,
|
end,
|
||||||
gen_storage:transaction(LServer, passwd, F),
|
gen_storage:transaction(LServer, passwd, F),
|
||||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
|
ok
|
||||||
|
catch
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
|
||||||
|
%% User = string()
|
||||||
|
%% Server = string()
|
||||||
|
%% Password = string()
|
||||||
|
%% @doc Remove user if the provided password is correct.
|
||||||
|
|
||||||
remove_user(User, Server, Password) ->
|
remove_user(User, Server, Password) ->
|
||||||
LUser = jlib:nodeprep(User),
|
try
|
||||||
LServer = jlib:nameprep(Server),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case gen_storage:read(LServer, {passwd, US}) of
|
case gen_storage:read(LServer, {passwd, US}) of
|
||||||
[#passwd{password = Password}] ->
|
[#passwd{password = Password}] ->
|
||||||
gen_storage:delete(LServer, {passwd, US}),
|
gen_storage:delete(LServer, {passwd, US}),
|
||||||
|
mnesia:dirty_update_counter(reg_users_counter,
|
||||||
|
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1),
|
||||||
ok;
|
ok;
|
||||||
[_] ->
|
[_] ->
|
||||||
not_allowed;
|
not_allowed;
|
||||||
@ -278,26 +446,32 @@ remove_user(User, Server, Password) ->
|
|||||||
end,
|
end,
|
||||||
case gen_storage:transaction(LServer, passwd, F) of
|
case gen_storage:transaction(LServer, passwd, F) of
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
|
|
||||||
ok;
|
ok;
|
||||||
{atomic, Res} ->
|
{atomic, Res} ->
|
||||||
Res;
|
Res;
|
||||||
_ ->
|
_ ->
|
||||||
bad_request
|
bad_request
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
_ ->
|
||||||
|
bad_request
|
||||||
end.
|
end.
|
||||||
|
|
||||||
update_table(Host) ->
|
%% @spec () -> term()
|
||||||
|
|
||||||
|
update_table(Host, mnesia) ->
|
||||||
gen_storage_migration:migrate_mnesia(
|
gen_storage_migration:migrate_mnesia(
|
||||||
Host, passwd,
|
Host, passwd,
|
||||||
[{passwd, [user, password],
|
[{passwd, [us, password],
|
||||||
fun({passwd, User, Password}) ->
|
fun({passwd, {User, _Host}, Password}) ->
|
||||||
#passwd{us = {User, Host},
|
#passwd{user_host = {User, Host},
|
||||||
password = Password}
|
password = Password}
|
||||||
end}]),
|
end}]);
|
||||||
|
update_table(Host, odbc) ->
|
||||||
gen_storage_migration:migrate_odbc(
|
gen_storage_migration:migrate_odbc(
|
||||||
Host, [passwd],
|
Host, [passwd],
|
||||||
[{"users", ["username", "password"],
|
[{"users", ["username", "password"],
|
||||||
fun(_, User, Password) ->
|
fun(_, User, Password) ->
|
||||||
#passwd{us = {User, Host},
|
#passwd{user_host = {User, Host},
|
||||||
password = Password}
|
password = Password}
|
||||||
end}]).
|
end}]).
|
||||||
|
104
src/mod_last.erl
104
src/mod_last.erl
@ -24,6 +24,37 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / last_activity
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% timestamp = now()
|
||||||
|
%%% status = string()
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / last
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% seconds = text
|
||||||
|
%%% state = text
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / last_activity
|
||||||
|
%%% us = {Username::binary(), Host::binary()}
|
||||||
|
%%% timestamp = now()
|
||||||
|
%%% status = binary()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / last
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / last_activity
|
||||||
|
%%% user_host = {Username::binary(), Host::binary()}
|
||||||
|
%%% timestamp = now()
|
||||||
|
%%% status = binary()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / last_activity
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% timestamp = bigint
|
||||||
|
%%% status = text
|
||||||
|
|
||||||
-module(mod_last).
|
-module(mod_last).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -43,7 +74,7 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
-record(last_activity, {user_server, timestamp, status}).
|
-record(last_activity, {user_host, timestamp, status}).
|
||||||
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
@ -54,9 +85,9 @@ start(Host, Opts) ->
|
|||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, last_activity)},
|
{attributes, record_info(fields, last_activity)},
|
||||||
{types, [{user_server, {text, text}},
|
{types, [{user_host, {text, text}},
|
||||||
{timestamp, bigint}]}]),
|
{timestamp, bigint}]}]),
|
||||||
update_table(Host),
|
update_table(Host, Backend),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY,
|
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY,
|
||||||
?MODULE, process_local_iq, IQDisc),
|
?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY,
|
||||||
@ -165,7 +196,7 @@ store_last_info(User, Server, TimeStamp, Status)
|
|||||||
US = {User, Server},
|
US = {User, Server},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
gen_storage:write(Server,
|
gen_storage:write(Server,
|
||||||
#last_activity{user_server = US,
|
#last_activity{user_host = US,
|
||||||
timestamp = TimeStamp,
|
timestamp = TimeStamp,
|
||||||
status = Status})
|
status = Status})
|
||||||
end,
|
end,
|
||||||
@ -203,66 +234,33 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
update_table(Host, mnesia) ->
|
||||||
update_table(Host) ->
|
|
||||||
Fields = record_info(fields, last_activity),
|
|
||||||
case mnesia:table_info(last_activity, attributes) of
|
|
||||||
Fields ->
|
|
||||||
convert_to_exmpp();
|
|
||||||
_ ->
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
gen_storage_migration:migrate_mnesia(
|
||||||
Host, last_activity,
|
Host, last_activity,
|
||||||
[{last_activity, [us, timestamp, status],
|
[{last_activity, [us, timestamp, status],
|
||||||
fun(#last_activity{} = LA) ->
|
fun({last_activity, {U, S}, Timestamp, Status}) ->
|
||||||
LA
|
U1 = case U of
|
||||||
end}]),
|
"" -> undefined;
|
||||||
|
V -> V
|
||||||
|
end,
|
||||||
|
#last_activity{user_host = {list_to_binary(U1),
|
||||||
|
list_to_binary(S)},
|
||||||
|
timestamp = Timestamp,
|
||||||
|
status = list_to_binary(Status)}
|
||||||
|
end}]);
|
||||||
|
update_table(Host, odbc) ->
|
||||||
gen_storage_migration:migrate_odbc(
|
gen_storage_migration:migrate_odbc(
|
||||||
Host, [last_activity],
|
Host, [last_activity],
|
||||||
[{"last", ["username", "seconds", "state"],
|
[{"last", ["username", "seconds", "state"],
|
||||||
fun(_, Username, STimeStamp, Status) ->
|
fun(_, Username, STimeStamp, Status) ->
|
||||||
case catch list_to_integer(STimeStamp) of
|
case catch list_to_integer(STimeStamp) of
|
||||||
TimeStamp when is_integer(TimeStamp) ->
|
TimeStamp when is_integer(TimeStamp) ->
|
||||||
[#last_activity{user_server = {Username, Host},
|
[#last_activity{user_host = {Username, Host},
|
||||||
timestamp = TimeStamp,
|
timestamp = TimeStamp,
|
||||||
status = Status}];
|
status = Status}];
|
||||||
_ ->
|
_ ->
|
||||||
?WARNING_MSG("Omitting last_activity migration item with timestamp=~p",
|
?WARNING_MSG("Omitting last_activity migration item"
|
||||||
|
" with timestamp=~p",
|
||||||
[STimeStamp])
|
[STimeStamp])
|
||||||
end
|
end
|
||||||
end}])
|
end}]).
|
||||||
end.
|
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:first(last_activity) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
none;
|
|
||||||
Key ->
|
|
||||||
case mnesia:read({last_activity, Key}) of
|
|
||||||
[#last_activity{status = Status}] when is_binary(Status) ->
|
|
||||||
none;
|
|
||||||
[#last_activity{}] ->
|
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2,
|
|
||||||
done, last_activity, write)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(Fun).
|
|
||||||
|
|
||||||
convert_to_exmpp2(#last_activity{user_server = {U, S} = Key, status = Status} = LA,
|
|
||||||
Acc) ->
|
|
||||||
% Remove old entry.
|
|
||||||
mnesia:delete({last_activity, Key}),
|
|
||||||
% Convert "" to undefined in JIDs.
|
|
||||||
U1 = convert_jid_to_exmpp(U),
|
|
||||||
% Convert status.
|
|
||||||
Status1 = list_to_binary(Status),
|
|
||||||
% Prepare the new record.
|
|
||||||
New_LA = LA#last_activity{user_server = {list_to_binary(U1), list_to_binary(S)},
|
|
||||||
status = Status1},
|
|
||||||
% Write the new record.
|
|
||||||
mnesia:write(New_LA),
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
convert_jid_to_exmpp("") -> undefined;
|
|
||||||
convert_jid_to_exmpp(V) -> V.
|
|
||||||
|
@ -24,6 +24,49 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / offline_msg
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% timestamp = now()
|
||||||
|
%%% expire = never | ???
|
||||||
|
%%% from = jid_old()
|
||||||
|
%%% to = jid_old()
|
||||||
|
%%% packet = xmlelement()
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / spool
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% xml = text
|
||||||
|
%%% seq = bigint unsigned
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / offline_msg
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% timestamp = now()
|
||||||
|
%%% expire = never | ???
|
||||||
|
%%% from = jid()
|
||||||
|
%%% to = jid()
|
||||||
|
%%% packet = #xmlel
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / spool
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / offline_msg
|
||||||
|
%%% user_host = {Username::string(), Host::string()}
|
||||||
|
%%% timestamp = integer()
|
||||||
|
%%% expire = 0 | integer()
|
||||||
|
%%% from = jid()
|
||||||
|
%%% to = jid()
|
||||||
|
%%% packet = string()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / offline_msg
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% timestamp = bigint
|
||||||
|
%%% expire = bigint
|
||||||
|
%%% from = varchar150
|
||||||
|
%%% to = varchar150
|
||||||
|
%%% packet = varchar150
|
||||||
|
|
||||||
-module(mod_offline).
|
-module(mod_offline).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -50,8 +93,9 @@
|
|||||||
-include("web/ejabberd_http.hrl").
|
-include("web/ejabberd_http.hrl").
|
||||||
-include("web/ejabberd_web_admin.hrl").
|
-include("web/ejabberd_web_admin.hrl").
|
||||||
|
|
||||||
|
%% The packet is stored serialized as a string
|
||||||
%% TODO: packet always handled serialized?
|
%% TODO: packet always handled serialized?
|
||||||
-record(offline_msg, {user_server, timestamp, expire, from, to, packet}).
|
-record(offline_msg, {user_host, timestamp, expire, from, to, packet}).
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_offline).
|
-define(PROCNAME, ejabberd_offline).
|
||||||
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||||
@ -72,12 +116,12 @@ start(Host, Opts) ->
|
|||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, offline_msg)},
|
{attributes, record_info(fields, offline_msg)},
|
||||||
{types, [{user_server, {text, text}},
|
{types, [{user_host, {text, text}},
|
||||||
{timestamp, bigint},
|
{timestamp, bigint},
|
||||||
{expire, bigint},
|
{expire, bigint},
|
||||||
{from, ljid},
|
{from, jid},
|
||||||
{to, ljid}]}]),
|
{to, jid}]}]),
|
||||||
update_table(),
|
update_table(Host, Backend),
|
||||||
ejabberd_hooks:add(offline_message_hook, HostB,
|
ejabberd_hooks:add(offline_message_hook, HostB,
|
||||||
?MODULE, store_packet, 50),
|
?MODULE, store_packet, 50),
|
||||||
ejabberd_hooks:add(resend_offline_messages_hook, HostB,
|
ejabberd_hooks:add(resend_offline_messages_hook, HostB,
|
||||||
@ -102,12 +146,12 @@ start(Host, Opts) ->
|
|||||||
|
|
||||||
loop(AccessMaxOfflineMsgs) ->
|
loop(AccessMaxOfflineMsgs) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{user_server=US} = Msg ->
|
#offline_msg{user_host=US} = Msg ->
|
||||||
Msgs = receive_all(US, [Msg]),
|
Msgs = receive_all(US, [Msg]),
|
||||||
Len = length(Msgs),
|
Len = length(Msgs),
|
||||||
%% TODO: is lower?
|
%% TODO: is lower?
|
||||||
{User, Host} = US,
|
{User, Host} = US,
|
||||||
MsgsSerialized = [M#offline_msg{packet = xml:element_to_string(P)}
|
MsgsSerialized = [M#offline_msg{packet = exmpp_xml:document_to_list(P)}
|
||||||
|| #offline_msg{packet = P} = M <- Msgs],
|
|| #offline_msg{packet = P} = M <- Msgs],
|
||||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
||||||
User, Host),
|
User, Host),
|
||||||
@ -115,8 +159,7 @@ loop(AccessMaxOfflineMsgs) ->
|
|||||||
%% Only count messages if needed:
|
%% Only count messages if needed:
|
||||||
Count =
|
Count =
|
||||||
if MaxOfflineMsgs =/= infinity ->
|
if MaxOfflineMsgs =/= infinity ->
|
||||||
Len + gen_storage:count_records(Host, offline_msg,
|
Len + get_queue_length(User, Host);
|
||||||
[{'=', user_server, US}]);
|
|
||||||
true ->
|
true ->
|
||||||
0
|
0
|
||||||
end,
|
end,
|
||||||
@ -152,7 +195,7 @@ get_max_user_messages(AccessRule, LUser, Host) ->
|
|||||||
|
|
||||||
receive_all(US, Msgs) ->
|
receive_all(US, Msgs) ->
|
||||||
receive
|
receive
|
||||||
#offline_msg{user_server=US} = Msg ->
|
#offline_msg{user_host=US} = Msg ->
|
||||||
receive_all(US, [Msg | Msgs])
|
receive_all(US, [Msg | Msgs])
|
||||||
after 0 ->
|
after 0 ->
|
||||||
Msgs
|
Msgs
|
||||||
@ -208,7 +251,7 @@ store_packet(From, To, Packet) ->
|
|||||||
TimeStamp = make_timestamp(),
|
TimeStamp = make_timestamp(),
|
||||||
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
|
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
|
||||||
gen_mod:get_module_proc(LServer, ?PROCNAME) !
|
gen_mod:get_module_proc(LServer, ?PROCNAME) !
|
||||||
#offline_msg{user_server = {LUser, LServer},
|
#offline_msg{user_host = {LUser, LServer},
|
||||||
timestamp = TimeStamp,
|
timestamp = TimeStamp,
|
||||||
expire = Expire,
|
expire = Expire,
|
||||||
from = From,
|
from = From,
|
||||||
@ -275,12 +318,12 @@ find_x_event_chatstates([_ | Els], {A, B, _}) ->
|
|||||||
find_x_event_chatstates(Els, {A, B, true}).
|
find_x_event_chatstates(Els, {A, B, true}).
|
||||||
|
|
||||||
find_x_expire(_, []) ->
|
find_x_expire(_, []) ->
|
||||||
never;
|
0;
|
||||||
find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
|
find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
|
||||||
Val = exmpp_xml:get_attribute_as_list(El, 'seconds', ""),
|
Val = exmpp_xml:get_attribute_as_list(El, 'seconds', ""),
|
||||||
case catch list_to_integer(Val) of
|
case catch list_to_integer(Val) of
|
||||||
{'EXIT', _} ->
|
{'EXIT', _} ->
|
||||||
never;
|
0;
|
||||||
Int when Int > 0 ->
|
Int when Int > 0 ->
|
||||||
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
||||||
S = MegaSecs * 1000000 + Secs + Int,
|
S = MegaSecs * 1000000 + Secs + Int,
|
||||||
@ -288,7 +331,7 @@ find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
|
|||||||
Secs1 = S rem 1000000,
|
Secs1 = S rem 1000000,
|
||||||
{MegaSecs1, Secs1, MicroSecs};
|
{MegaSecs1, Secs1, MicroSecs};
|
||||||
_ ->
|
_ ->
|
||||||
never
|
0
|
||||||
end;
|
end;
|
||||||
find_x_expire(TimeStamp, [_ | Els]) ->
|
find_x_expire(TimeStamp, [_ | Els]) ->
|
||||||
find_x_expire(TimeStamp, Els).
|
find_x_expire(TimeStamp, Els).
|
||||||
@ -352,7 +395,7 @@ pop_offline_messages(Ls, User, Server)
|
|||||||
TS = make_timestamp(),
|
TS = make_timestamp(),
|
||||||
Ls ++ lists:map(
|
Ls ++ lists:map(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
Packet = R#offline_msg.packet,
|
[Packet] = exmpp_xml:parse_document(R#offline_msg.packet),
|
||||||
{route,
|
{route,
|
||||||
R#offline_msg.from,
|
R#offline_msg.from,
|
||||||
R#offline_msg.to,
|
R#offline_msg.to,
|
||||||
@ -367,13 +410,13 @@ pop_offline_messages(Ls, User, Server)
|
|||||||
%% 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(
|
||||||
calendar:now_to_universal_time(
|
calendar:now_to_universal_time(
|
||||||
R#offline_msg.timestamp))]
|
timestamp_to_now(R#offline_msg.timestamp)))]
|
||||||
)}
|
)}
|
||||||
end,
|
end,
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
case R#offline_msg.expire of
|
case R#offline_msg.expire of
|
||||||
never ->
|
0 ->
|
||||||
true;
|
true;
|
||||||
TimeStamp ->
|
TimeStamp ->
|
||||||
TS < TimeStamp
|
TS < TimeStamp
|
||||||
@ -428,156 +471,65 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
update_table() ->
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
Fields = record_info(fields, offline_msg),
|
|
||||||
case mnesia:table_info(offline_msg, attributes) of
|
update_table(Host, mnesia) ->
|
||||||
Fields ->
|
gen_storage_migration:migrate_mnesia(
|
||||||
convert_to_exmpp();
|
Host, offline_msg,
|
||||||
[user, timestamp, expire, from, to, packet] ->
|
[{offline_msg, [us, timestamp, expire, from, to, packet],
|
||||||
?INFO_MSG("Converting offline_msg table from "
|
%% The field name 'us' changes to 'user_host',
|
||||||
"{user, timestamp, expire, from, to, packet} format", []),
|
%% but its position in the erlang record is the same,
|
||||||
Host = ?MYNAME,
|
%% so we can refer to it using the new field name 'user_host'.
|
||||||
{atomic, ok} = mnesia:create_table(
|
fun(#offline_msg{user_host = {US_U, US_S},
|
||||||
mod_offline_tmp_table,
|
timestamp = {TsMegaSecs, TsSecs, _TsMicroSecs},
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{type, bag},
|
|
||||||
{local_content, true},
|
|
||||||
{record_name, offline_msg},
|
|
||||||
{attributes, record_info(fields, offline_msg)}]),
|
|
||||||
mnesia:transform_table(offline_msg, ignore, Fields),
|
|
||||||
F1 = fun() ->
|
|
||||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(#offline_msg{user_server = U, from = F, to = T, packet = P} = R, _) ->
|
|
||||||
U1 = convert_jid_to_exmpp(U),
|
|
||||||
F1 = jlib:from_old_jid(F),
|
|
||||||
T1 = jlib:from_old_jid(T),
|
|
||||||
P1 = exmpp_xml:xmlelement_to_xmlel(
|
|
||||||
P,
|
|
||||||
[?DEFAULT_NS],
|
|
||||||
?PREFIXED_NS),
|
|
||||||
New_R = R#offline_msg{
|
|
||||||
user_server = {U1, Host},
|
|
||||||
from = F1,
|
|
||||||
to = T1,
|
|
||||||
packet = P1
|
|
||||||
},
|
|
||||||
mnesia:dirty_write(
|
|
||||||
mod_offline_tmp_table,
|
|
||||||
New_R)
|
|
||||||
end, ok, offline_msg)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F1),
|
|
||||||
mnesia:clear_table(offline_msg),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(offline_msg),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_offline_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_offline_tmp_table);
|
|
||||||
[user, timestamp, from, to, packet] ->
|
|
||||||
?INFO_MSG("Converting offline_msg table from "
|
|
||||||
"{user, timestamp, from, to, packet} format", []),
|
|
||||||
Host = ?MYNAME,
|
|
||||||
{atomic, ok} = mnesia:create_table(
|
|
||||||
mod_offline_tmp_table,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{type, bag},
|
|
||||||
{local_content, true},
|
|
||||||
{record_name, offline_msg},
|
|
||||||
{attributes, record_info(fields, offline_msg)}]),
|
|
||||||
mnesia:transform_table(
|
|
||||||
offline_msg,
|
|
||||||
fun({_, U, TS, F, T, P}) ->
|
|
||||||
Expire = find_x_expire(TS, P#xmlelement.children),
|
|
||||||
U1 = convert_jid_to_exmpp(U),
|
|
||||||
F1 = jlib:from_old_jid(F),
|
|
||||||
T1 = jlib:from_old_jid(T),
|
|
||||||
P1 = exmpp_xml:xmlelement_to_xmlel(
|
|
||||||
P,
|
|
||||||
[?DEFAULT_NS],
|
|
||||||
?PREFIXED_NS),
|
|
||||||
#offline_msg{user_server = U1,
|
|
||||||
timestamp = TS,
|
|
||||||
expire = Expire,
|
expire = Expire,
|
||||||
from = F1,
|
|
||||||
to = T1,
|
|
||||||
packet = P1}
|
|
||||||
end, Fields),
|
|
||||||
F1 = fun() ->
|
|
||||||
mnesia:write_lock_table(mod_offline_tmp_table),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(#offline_msg{user_server = U} = R, _) ->
|
|
||||||
mnesia:dirty_write(
|
|
||||||
mod_offline_tmp_table,
|
|
||||||
R#offline_msg{user_server = {U, Host}})
|
|
||||||
end, ok, offline_msg)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F1),
|
|
||||||
mnesia:clear_table(offline_msg),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(offline_msg),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_offline_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_offline_tmp_table);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating offline_msg table", []),
|
|
||||||
mnesia:transform_table(offline_msg, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:first(offline_msg) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
none;
|
|
||||||
Key ->
|
|
||||||
case mnesia:read({offline_msg, Key}) of
|
|
||||||
[#offline_msg{packet = #xmlel{}} | _] ->
|
|
||||||
none;
|
|
||||||
[#offline_msg{packet = #xmlelement{}} | _] ->
|
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2,
|
|
||||||
done, offline_msg, write)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(Fun).
|
|
||||||
|
|
||||||
convert_to_exmpp2(#offline_msg{
|
|
||||||
user_server = {US_U, US_S},
|
|
||||||
from = From,
|
from = From,
|
||||||
to = To,
|
to = To,
|
||||||
packet = Packet} = R, Acc) ->
|
packet = Packet} = OM) ->
|
||||||
% Remove old entry.
|
%% Convert "" to undefined in JIDs.
|
||||||
mnesia:delete_object(R),
|
|
||||||
% Convert "" to undefined in JIDs.
|
|
||||||
US_U1 = convert_jid_to_exmpp(US_U),
|
US_U1 = convert_jid_to_exmpp(US_U),
|
||||||
US_S1 = convert_jid_to_exmpp(US_S),
|
US_S1 = convert_jid_to_exmpp(US_S),
|
||||||
From1 = jlib:from_old_jid(From),
|
From1 = jlib:from_old_jid(From),
|
||||||
To1 = jlib:from_old_jid(To),
|
To1 = jlib:from_old_jid(To),
|
||||||
% Convert stanza.
|
Expire1 = case Expire of
|
||||||
Packet1 = exmpp_xml:xmlelement_to_xmlel(Packet,
|
never ->
|
||||||
[?DEFAULT_NS], ?PREFIXED_NS),
|
0;
|
||||||
% Prepare the new record.
|
{MegaSecs, Secs, _MicroSecs} ->
|
||||||
New_R = R#offline_msg{
|
MegaSecs * 1000000 + Secs
|
||||||
user_server = {US_U1, US_S1},
|
end,
|
||||||
|
PacketXmlel = exmpp_xml:xmlelement_to_xmlel(
|
||||||
|
Packet, [?DEFAULT_NS], ?PREFIXED_NS),
|
||||||
|
Packet1 = exmpp_xml:document_to_list(PacketXmlel),
|
||||||
|
OM#offline_msg{user_host = {US_U1, US_S1},
|
||||||
|
timestamp = TsMegaSecs * 1000000 + TsSecs,
|
||||||
|
expire = Expire1,
|
||||||
from = From1,
|
from = From1,
|
||||||
to = To1,
|
to = To1,
|
||||||
packet = Packet1},
|
packet = Packet1}
|
||||||
% Write the new record.
|
end}]);
|
||||||
mnesia:write(New_R),
|
|
||||||
Acc.
|
update_table(Host, odbc) ->
|
||||||
|
gen_storage_migration:migrate_odbc(
|
||||||
|
Host, [offline_msg],
|
||||||
|
[{"spool", ["username", "xml", "seq"],
|
||||||
|
fun(_, Username, XmlS, _Seq) ->
|
||||||
|
[Xmlel] = Els = exmpp_xml:parse_document(XmlS, [names_as_atom]),
|
||||||
|
From = jlib:short_prepd_jid(
|
||||||
|
exmpp_jid:parse(
|
||||||
|
exmpp_stanza:get_sender(Xmlel))),
|
||||||
|
Expire = find_x_expire(0, Els),
|
||||||
|
Timestamp = find_x_timestamp(Els),
|
||||||
|
[#offline_msg{user_host = {Username, Host},
|
||||||
|
timestamp = Timestamp,
|
||||||
|
expire = Expire,
|
||||||
|
from = From,
|
||||||
|
to = {Username, Host, ""},
|
||||||
|
packet = XmlS}]
|
||||||
|
end}]).
|
||||||
|
|
||||||
convert_jid_to_exmpp("") -> undefined;
|
convert_jid_to_exmpp("") -> undefined;
|
||||||
convert_jid_to_exmpp(V) -> V.
|
convert_jid_to_exmpp(V) -> V.
|
||||||
|
|
||||||
%% Helper functions:
|
|
||||||
make_timestamp() ->
|
make_timestamp() ->
|
||||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||||
MegaSecs * 1000000 + Secs.
|
MegaSecs * 1000000 + Secs.
|
||||||
@ -587,6 +539,22 @@ timestamp_to_now(Timestamp) ->
|
|||||||
Secs = Timestamp rem 1000000,
|
Secs = Timestamp rem 1000000,
|
||||||
{MegaSecs, Secs, 0}.
|
{MegaSecs, Secs, 0}.
|
||||||
|
|
||||||
|
find_x_timestamp([]) ->
|
||||||
|
make_timestamp();
|
||||||
|
find_x_timestamp([{xmlcdata, _} | Els]) ->
|
||||||
|
find_x_timestamp(Els);
|
||||||
|
|
||||||
|
find_x_timestamp([#xmlel{ns = ?NS_DELAY} = El | Els]) ->
|
||||||
|
Stamp = exmpp_xml:get_attribute_as_list(El, 'stamp', ""),
|
||||||
|
case jlib:datetime_string_to_timestamp(Stamp) of
|
||||||
|
undefined -> find_x_timestamp(Els);
|
||||||
|
{MegaSecs, Secs, _MicroSecs} -> MegaSecs * 1000000 + Secs
|
||||||
|
end;
|
||||||
|
find_x_timestamp([_ | Els]) ->
|
||||||
|
find_x_timestamp(Els).
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
%% Warn senders that their messages have been discarded:
|
%% Warn senders that their messages have been discarded:
|
||||||
discard_warn_sender(Msgs) ->
|
discard_warn_sender(Msgs) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
@ -616,7 +584,7 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
exmpp_stringprep:nodeprep(User),
|
exmpp_stringprep:nodeprep(User),
|
||||||
exmpp_stringprep:nameprep(Server)
|
exmpp_stringprep:nameprep(Server)
|
||||||
},
|
},
|
||||||
{user_server, MsgsAll, Res} = try
|
{US, MsgsAll, Res} = try
|
||||||
{
|
{
|
||||||
US0,
|
US0,
|
||||||
lists:keysort(#offline_msg.timestamp,
|
lists:keysort(#offline_msg.timestamp,
|
||||||
@ -631,7 +599,7 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
FMsgs =
|
FMsgs =
|
||||||
lists:map(
|
lists:map(
|
||||||
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
fun(#offline_msg{timestamp = TimeStamp, from = From, to = To,
|
||||||
packet = Packet} = Msg) ->
|
packet = PacketInitialString} = Msg) ->
|
||||||
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
|
ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))),
|
||||||
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||||
calendar:now_to_local_time(timestamp_to_now(TimeStamp)),
|
calendar:now_to_local_time(timestamp_to_now(TimeStamp)),
|
||||||
@ -641,6 +609,7 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
[Year, Month, Day, Hour, Minute, Second])),
|
[Year, Month, Day, Hour, Minute, Second])),
|
||||||
SFrom = exmpp_jid:to_list(From),
|
SFrom = exmpp_jid:to_list(From),
|
||||||
STo = exmpp_jid:to_list(To),
|
STo = exmpp_jid:to_list(To),
|
||||||
|
[Packet] = exmpp_xmlstream:parse_element(PacketInitialString),
|
||||||
Packet1 = exmpp_stanza:set_jids(Packet, SFrom, STo),
|
Packet1 = exmpp_stanza:set_jids(Packet, SFrom, STo),
|
||||||
FPacket = exmpp_xml:node_to_list(
|
FPacket = exmpp_xml:node_to_list(
|
||||||
exmpp_xml:indent_document(Packet1, <<" ">>),
|
exmpp_xml:indent_document(Packet1, <<" ">>),
|
||||||
@ -654,7 +623,7 @@ user_queue(User, Server, Query, Lang) ->
|
|||||||
)
|
)
|
||||||
end, Msgs),
|
end, Msgs),
|
||||||
[?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(US0)]))] ++
|
[us_to_list(US)]))] ++
|
||||||
case Res of
|
case Res of
|
||||||
ok -> [?CT("Submitted"), ?P];
|
ok -> [?CT("Submitted"), ?P];
|
||||||
nothing -> []
|
nothing -> []
|
||||||
@ -711,8 +680,8 @@ user_queue_parse_query(US, Query) ->
|
|||||||
us_to_list({User, Server}) ->
|
us_to_list({User, Server}) ->
|
||||||
exmpp_jid:to_list(User, Server).
|
exmpp_jid:to_list(User, Server).
|
||||||
|
|
||||||
get_queue_length(User, Server) ->
|
get_queue_length(User, Host) ->
|
||||||
length(mnesia:dirty_read({offline_msg, {User, Server}})).
|
gen_storage:dirty_count_records(Host, offline_msg, [{'=', user_host, {User, Host}}]).
|
||||||
|
|
||||||
get_messages_subset(User, Host, MsgsAll) ->
|
get_messages_subset(User, Host, MsgsAll) ->
|
||||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages,
|
||||||
@ -736,8 +705,9 @@ get_messages_subset2(Max, Length, MsgsAll) ->
|
|||||||
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
|
||||||
|
|
||||||
webadmin_user(Acc, User, Server, Lang) ->
|
webadmin_user(Acc, User, Server, Lang) ->
|
||||||
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
QueueLen = length(gen_storage:dirty_read(Server, {offline_msg, US})),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
|
QueueLen = get_queue_length(LUser, LServer),
|
||||||
FQueueLen = [?AC("queue/", integer_to_list(QueueLen))],
|
FQueueLen = [?AC("queue/", 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")].
|
||||||
|
|
||||||
@ -761,51 +731,3 @@ webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) ->
|
|||||||
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
|
|
||||||
update_table(Host) ->
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
|
||||||
Host, offline_msg,
|
|
||||||
[{offline_msg, [us, timestamp, expire, from, to, packet],
|
|
||||||
fun(#offline_msg{expire = never,
|
|
||||||
packet = Packet} = OM) ->
|
|
||||||
OM#offline_msg{expire = 0,
|
|
||||||
packet = xml:element_to_string(Packet)};
|
|
||||||
(#offline_msg{expire = {MegaSecs, Secs, _MicroSecs},
|
|
||||||
packet = Packet} = OM) ->
|
|
||||||
OM#offline_msg{expire = MegaSecs * 1000000 + Secs,
|
|
||||||
packet = xml:element_to_string(Packet)}
|
|
||||||
end}]),
|
|
||||||
gen_storage_migration:migrate_odbc(
|
|
||||||
Host, [offline_msg],
|
|
||||||
[{"spool", ["username", "xml", "seq"],
|
|
||||||
fun(_, Username, XmlS, _Seq) ->
|
|
||||||
{xmlelement, _, Attrs, Els} = xml_stream:parse_element(XmlS),
|
|
||||||
From = jlib:jid_tolower(
|
|
||||||
jlib:string_to_jid(
|
|
||||||
xml:get_attr_s("from", Attrs))),
|
|
||||||
Expire = find_x_expire(0, Els),
|
|
||||||
Timestamp = find_x_timestamp(Els),
|
|
||||||
[#offline_msg{user_server = {Username, Host},
|
|
||||||
timestamp = Timestamp,
|
|
||||||
expire = Expire,
|
|
||||||
from = From,
|
|
||||||
to = {Username, Host, ""},
|
|
||||||
packet = XmlS}]
|
|
||||||
end}]).
|
|
||||||
|
|
||||||
find_x_timestamp([]) ->
|
|
||||||
make_timestamp();
|
|
||||||
find_x_timestamp([{xmlcdata, _} | Els]) ->
|
|
||||||
find_x_timestamp(Els);
|
|
||||||
find_x_timestamp([El | Els]) ->
|
|
||||||
case xml:get_tag_attr_s("xmlns", El) of
|
|
||||||
?NS_DELAY ->
|
|
||||||
Stamp = xml:get_tag_attr_s("stamp", El),
|
|
||||||
case jlib:datetime_string_to_timestamp(Stamp) of
|
|
||||||
undefined -> find_x_timestamp(Els);
|
|
||||||
{MegaSecs, Secs, _MicroSecs} -> MegaSecs * 1000000 + Secs
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
find_x_timestamp(Els)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
@ -24,6 +24,81 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / privacy
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% default = none | ListName::string()
|
||||||
|
%%% lists = [ {ListName::string(), [listitem()]} ]
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / privacy_default_list
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% name = varchar250
|
||||||
|
%%% 2.1.x / odbc / privacy_list
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% name = varchar250
|
||||||
|
%%% id = bigint-unsigned
|
||||||
|
%%% 2.1.x / odbc / privacy_list_data
|
||||||
|
%%% id = bigint
|
||||||
|
%%% t = character(1)
|
||||||
|
%%% value = text
|
||||||
|
%%% action = character(1)
|
||||||
|
%%% ord = NUMERIC
|
||||||
|
%%% match_all = boolean
|
||||||
|
%%% match_iq = boolean
|
||||||
|
%%% match_message = boolean
|
||||||
|
%%% match_presence_in = boolean
|
||||||
|
%%% match_presence_out = boolean
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / privacy
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% default = none | ListName::string()
|
||||||
|
%%% lists = [ {ListName::string(), [listitem()]} ]
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / privacy
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / privacy_default_list
|
||||||
|
%%% user_host = {Username::string(), Server::string()}
|
||||||
|
%%% name = string()
|
||||||
|
%%% 3.0.0-alpha / mnesia / privacy_list
|
||||||
|
%%% user_host = {Username::string(), Server::string()}
|
||||||
|
%%% name = string()
|
||||||
|
%%% 3.0.0-alpha / mnesia / privacy_list_data
|
||||||
|
%%% user_host = {Username::string(), Server::string()}
|
||||||
|
%%% name = string()
|
||||||
|
%%% type = jid | group | subscription | none
|
||||||
|
%%% value = JID::binary() | Group::binary() | <<"none">> | <<"both">> | <<"from">> | <<"to">>
|
||||||
|
%%% action = allow | deny
|
||||||
|
%%% order = integer()
|
||||||
|
%%% match_all = boolean()
|
||||||
|
%%% match_iq = boolean()
|
||||||
|
%%% match_message = boolean()
|
||||||
|
%%% match_presence_in = boolean()
|
||||||
|
%%% match_presence_out = boolean()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / privacy_default_list
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% name = text
|
||||||
|
%%% 3.0.0-alpha / odbc / privacy_list
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% name = varchar150
|
||||||
|
%%% 3.0.0-alpha / odbc / privacy_list_data
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% name = varchar150
|
||||||
|
%%% type = varchar150
|
||||||
|
%%% value = varchar150
|
||||||
|
%%% action = varchar150
|
||||||
|
%%% order = int 11
|
||||||
|
%%% match_all = varchar150
|
||||||
|
%%% match_iq = varchar150
|
||||||
|
%%% match_message = varchar150
|
||||||
|
%%% match_presence_in = varchar150
|
||||||
|
%%% match_presence_out = varchar150
|
||||||
|
|
||||||
-module(mod_privacy).
|
-module(mod_privacy).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -43,9 +118,9 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
-record(privacy_list, {username_server, name}).
|
-record(privacy_list, {user_host, name}).
|
||||||
-record(privacy_default_list, {username_server, name}).
|
-record(privacy_default_list, {user_host, name}).
|
||||||
-record(privacy_list_data, {username_server, name,
|
-record(privacy_list_data, {user_host, name,
|
||||||
type, value, action, order,
|
type, value, action, order,
|
||||||
match_all, match_iq, match_message,
|
match_all, match_iq, match_message,
|
||||||
match_presence_in, match_presence_out}).
|
match_presence_in, match_presence_out}).
|
||||||
@ -59,21 +134,21 @@ start(Host, Opts) ->
|
|||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, privacy_list)},
|
{attributes, record_info(fields, privacy_list)},
|
||||||
{types, [{username_server, {text, text}}]}]),
|
{types, [{user_host, {text, text}}]}]),
|
||||||
gen_storage:create_table(Backend, HostB, privacy_default_list,
|
gen_storage:create_table(Backend, HostB, privacy_default_list,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, privacy_default_list)},
|
{attributes, record_info(fields, privacy_default_list)},
|
||||||
{types, [{username_server, {text, text}}]}]),
|
{types, [{user_host, {text, text}}]}]),
|
||||||
gen_storage:create_table(Backend, HostB, privacy_list_data,
|
gen_storage:create_table(Backend, HostB, privacy_list_data,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, privacy_list_data)},
|
{attributes, record_info(fields, privacy_list_data)},
|
||||||
{types, [{username_server, {text, text}},
|
{types, [{user_host, {text, text}},
|
||||||
{action, atom},
|
|
||||||
{type, atom},
|
{type, atom},
|
||||||
{value, atom},
|
{value, binary},
|
||||||
|
{action, atom},
|
||||||
{order, int},
|
{order, int},
|
||||||
{match_all, atom},
|
{match_all, atom},
|
||||||
{match_iq, atom},
|
{match_iq, atom},
|
||||||
@ -81,7 +156,7 @@ start(Host, Opts) ->
|
|||||||
{match_presence_in, atom},
|
{match_presence_in, atom},
|
||||||
{match_presence_out, atom}
|
{match_presence_out, atom}
|
||||||
]}]),
|
]}]),
|
||||||
update_tables(Host),
|
update_tables(Host, Backend),
|
||||||
gen_storage:add_table_index(Host, privacy_list, name),
|
gen_storage:add_table_index(Host, privacy_list, name),
|
||||||
gen_storage:add_table_index(Host, privacy_list_data, name),
|
gen_storage:add_table_index(Host, privacy_list_data, name),
|
||||||
ejabberd_hooks:add(privacy_iq_get, HostB,
|
ejabberd_hooks:add(privacy_iq_get, HostB,
|
||||||
@ -160,10 +235,7 @@ process_lists_get(LUser, LServer, Active) ->
|
|||||||
[] ->
|
[] ->
|
||||||
{result, #xmlel{ns = ?NS_PRIVACY, name = 'query'}};
|
{result, #xmlel{ns = ?NS_PRIVACY, name = 'query'}};
|
||||||
_ ->
|
_ ->
|
||||||
LItems = lists:map(
|
LItems = [exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = list}, name, N) || N <- Lists],
|
||||||
fun({N, _}) ->
|
|
||||||
exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = list}, name, N)
|
|
||||||
end, Lists),
|
|
||||||
DItems =
|
DItems =
|
||||||
case Default of
|
case Default of
|
||||||
none ->
|
none ->
|
||||||
@ -188,13 +260,13 @@ process_list_get(_LUser, _LServer, false) ->
|
|||||||
process_list_get(LUser, LServer, Name) ->
|
process_list_get(LUser, LServer, Name) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case gen_storage:select(LServer, privacy_list,
|
case gen_storage:select(LServer, privacy_list,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]) of
|
{'=', name, Name}]) of
|
||||||
[] ->
|
[] ->
|
||||||
none;
|
none;
|
||||||
[#privacy_list{}] ->
|
[#privacy_list{}] ->
|
||||||
gen_storage:select(LServer, privacy_list_data,
|
gen_storage:select(LServer, privacy_list_data,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}])
|
{'=', name, Name}])
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@ -217,8 +289,8 @@ item_to_xml(Item) ->
|
|||||||
none ->
|
none ->
|
||||||
Attrs1;
|
Attrs1;
|
||||||
Type ->
|
Type ->
|
||||||
[?XMLATTR('type', type_to_binary(Item#privacy_list_data.type)),
|
[?XMLATTR('type', type_to_binary(Type)),
|
||||||
?XMLATTR('value', value_to_binary(Type, Item#privacy_list_data.value)) |
|
?XMLATTR('value', Item#privacy_list_data.value) |
|
||||||
Attrs1]
|
Attrs1]
|
||||||
end,
|
end,
|
||||||
SubEls = case Item#privacy_list_data.match_all of
|
SubEls = case Item#privacy_list_data.match_all of
|
||||||
@ -270,21 +342,6 @@ type_to_binary(Type) ->
|
|||||||
subscription -> <<"subscription">>
|
subscription -> <<"subscription">>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
value_to_binary(Type, Val) ->
|
|
||||||
case Type of
|
|
||||||
jid ->
|
|
||||||
{N, D, R} = Val,
|
|
||||||
exmpp_jid:to_binary(N, D, R);
|
|
||||||
group -> Val;
|
|
||||||
subscription ->
|
|
||||||
case Val of
|
|
||||||
both -> <<"both">>;
|
|
||||||
to -> <<"to">>;
|
|
||||||
from -> <<"from">>;
|
|
||||||
none -> <<"none">>
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
list_to_action(S) ->
|
list_to_action(S) ->
|
||||||
@ -331,13 +388,13 @@ process_default_set(LUser, LServer, false) ->
|
|||||||
process_default_set(LUser, LServer, Name) ->
|
process_default_set(LUser, LServer, Name) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case gen_storage:select(LServer, privacy_list,
|
case gen_storage:select(LServer, privacy_list,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]) of
|
{'=', name, Name}]) of
|
||||||
[] ->
|
[] ->
|
||||||
{error, 'item-not-found'};
|
{error, 'item-not-found'};
|
||||||
[#privacy_list{}] ->
|
[#privacy_list{}] ->
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#privacy_default_list{username_server = {LUser, LServer},
|
#privacy_default_list{user_host = {LUser, LServer},
|
||||||
name = Name}),
|
name = Name}),
|
||||||
{result, []}
|
{result, []}
|
||||||
end
|
end
|
||||||
@ -358,11 +415,11 @@ process_active_set(_LUser, _LServer, false) ->
|
|||||||
process_active_set(LUser, LServer, Name) ->
|
process_active_set(LUser, LServer, Name) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case gen_storage:select(LServer, privacy_list,
|
case gen_storage:select(LServer, privacy_list,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]) of
|
{'=', name, Name}]) of
|
||||||
[#privacy_list{}] ->
|
[#privacy_list{}] ->
|
||||||
List = gen_storage:select(LServer, privacy_list_data,
|
List = gen_storage:select(LServer, privacy_list_data,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]),
|
{'=', name, Name}]),
|
||||||
{result, [], #userlist{name = Name,
|
{result, [], #userlist{name = Name,
|
||||||
list = list_data_to_items(List)}};
|
list = list_data_to_items(List)}};
|
||||||
@ -394,10 +451,10 @@ process_list_set(LUser, LServer, Name, Els) ->
|
|||||||
{error, 'conflict'};
|
{error, 'conflict'};
|
||||||
_ ->
|
_ ->
|
||||||
gen_storage:delete_where(LServer, privacy_list,
|
gen_storage:delete_where(LServer, privacy_list,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]),
|
{'=', name, Name}]),
|
||||||
gen_storage:delete_where(LServer, privacy_list_data,
|
gen_storage:delete_where(LServer, privacy_list_data,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]),
|
{'=', name, Name}]),
|
||||||
{result, []}
|
{result, []}
|
||||||
end
|
end
|
||||||
@ -421,14 +478,14 @@ process_list_set(LUser, LServer, Name, Els) ->
|
|||||||
F = fun() ->
|
F = fun() ->
|
||||||
OldData =
|
OldData =
|
||||||
gen_storage:select(LServer, privacy_list_data,
|
gen_storage:select(LServer, privacy_list_data,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Name}]),
|
{'=', name, Name}]),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Data1) ->
|
fun(Data1) ->
|
||||||
gen_storage:delete_object(LServer, Data1)
|
gen_storage:delete_object(LServer, Data1)
|
||||||
end, OldData),
|
end, OldData),
|
||||||
|
|
||||||
gen_storage:write(LServer, #privacy_list{username_server = {LUser, LServer},
|
gen_storage:write(LServer, #privacy_list{user_host = {LUser, LServer},
|
||||||
name = Name}),
|
name = Name}),
|
||||||
NewData = list_items_to_data(LUser, LServer, Name, List),
|
NewData = list_items_to_data(LUser, LServer, Name, List),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
@ -467,7 +524,7 @@ parse_items([], Res) ->
|
|||||||
lists:reverse(Res);
|
lists:reverse(Res);
|
||||||
parse_items([El = #xmlel{name = item} | Els], Res) ->
|
parse_items([El = #xmlel{name = item} | Els], Res) ->
|
||||||
Type = exmpp_xml:get_attribute_as_list(El, type, false),
|
Type = exmpp_xml:get_attribute_as_list(El, type, false),
|
||||||
Value = exmpp_xml:get_attribute_as_list(El, value, false),
|
Value = exmpp_xml:get_attribute_as_binary(El, value, false),
|
||||||
SAction =exmpp_xml:get_attribute_as_list(El, action, false),
|
SAction =exmpp_xml:get_attribute_as_list(El, action, false),
|
||||||
SOrder = exmpp_xml:get_attribute_as_list(El, order, false),
|
SOrder = exmpp_xml:get_attribute_as_list(El, order, false),
|
||||||
Action = case catch list_to_action(SAction) of
|
Action = case catch list_to_action(SAction) of
|
||||||
@ -489,38 +546,17 @@ parse_items([El = #xmlel{name = item} | Els], Res) ->
|
|||||||
(Action /= false) and (Order /= false) ->
|
(Action /= false) and (Order /= false) ->
|
||||||
I1 = #listitem{action = Action, order = Order},
|
I1 = #listitem{action = Action, order = Order},
|
||||||
I2 = case {Type, Value} of
|
I2 = case {Type, Value} of
|
||||||
{T, V} when is_list(T), is_list(V) ->
|
{T, V} when is_list(T), is_binary(V) ->
|
||||||
case T of
|
case T of
|
||||||
"jid" ->
|
"jid" ->
|
||||||
try
|
I1#listitem{type = jid,
|
||||||
JID = exmpp_jid:parse(V),
|
value = V};
|
||||||
I1#listitem{
|
|
||||||
type = jid,
|
|
||||||
value = jlib:short_prepd_jid(JID)}
|
|
||||||
catch
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
"group" ->
|
"group" ->
|
||||||
I1#listitem{type = group,
|
I1#listitem{type = group,
|
||||||
value = V};
|
value = V};
|
||||||
"subscription" ->
|
"subscription" ->
|
||||||
case V of
|
|
||||||
"none" ->
|
|
||||||
I1#listitem{type = subscription,
|
I1#listitem{type = subscription,
|
||||||
value = none};
|
value = V}
|
||||||
"both" ->
|
|
||||||
I1#listitem{type = subscription,
|
|
||||||
value = both};
|
|
||||||
"from" ->
|
|
||||||
I1#listitem{type = subscription,
|
|
||||||
value = from};
|
|
||||||
"to" ->
|
|
||||||
I1#listitem{type = subscription,
|
|
||||||
value = to};
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
{T, false} when is_list(T) ->
|
{T, false} when is_list(T) ->
|
||||||
false;
|
false;
|
||||||
@ -566,10 +602,6 @@ parse_matches1(_Item, [#xmlel{} | _Els]) ->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% storage representation to ejabberd representation
|
%% storage representation to ejabberd representation
|
||||||
list_data_to_items(Data) ->
|
list_data_to_items(Data) ->
|
||||||
List =
|
List =
|
||||||
@ -592,7 +624,7 @@ list_data_to_items(Data) ->
|
|||||||
list_items_to_data(LUser, LServer, Name, List) ->
|
list_items_to_data(LUser, LServer, Name, List) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun(Item) ->
|
fun(Item) ->
|
||||||
#privacy_list_data{username_server = {LUser, LServer},
|
#privacy_list_data{user_host = {LUser, LServer},
|
||||||
name = Name,
|
name = Name,
|
||||||
type = Item#listitem.type,
|
type = Item#listitem.type,
|
||||||
value = Item#listitem.value,
|
value = Item#listitem.value,
|
||||||
@ -626,7 +658,7 @@ get_user_list(_, User, Server)
|
|||||||
#userlist{};
|
#userlist{};
|
||||||
[#privacy_default_list{name = Default}] ->
|
[#privacy_default_list{name = Default}] ->
|
||||||
Data = gen_storage:select(LServer, privacy_list_data,
|
Data = gen_storage:select(LServer, privacy_list_data,
|
||||||
[{'=', username_server, {LUser, LServer}},
|
[{'=', user_host, {LUser, LServer}},
|
||||||
{'=', name, Default}]),
|
{'=', name, Default}]),
|
||||||
List = list_data_to_items(Data),
|
List = list_data_to_items(Data),
|
||||||
NeedDb = is_list_needdb(List),
|
NeedDb = is_list_needdb(List),
|
||||||
@ -734,18 +766,15 @@ is_ptype_match(Item, PType) ->
|
|||||||
|
|
||||||
|
|
||||||
%% TODO: Investigate this: sometimes Value has binaries, other times has strings
|
%% TODO: Investigate this: sometimes Value has binaries, other times has strings
|
||||||
is_type_match(Type, Value, JID, Subscription, Groups) ->
|
is_type_match(jid, Value, JID, _Subscription, _Groups) ->
|
||||||
case Type of
|
{User, Server, Resource} = exmpp_jid:to_lower(exmpp_jid:parse(Value)),
|
||||||
jid ->
|
|
||||||
{User, Server, Resource} = Value,
|
|
||||||
((User == undefined) orelse (User == []) orelse (User == exmpp_jid:prep_node(JID)))
|
((User == undefined) orelse (User == []) orelse (User == exmpp_jid:prep_node(JID)))
|
||||||
andalso ((Server == undefined) orelse (Server == []) orelse (Server == exmpp_jid:prep_domain(JID)))
|
andalso ((Server == undefined) orelse (Server == []) orelse (Server == exmpp_jid:prep_domain(JID)))
|
||||||
andalso ((Resource == undefined) orelse (Resource == []) orelse (Resource == exmpp_jid:prep_resource(JID)));
|
andalso ((Resource == undefined) orelse (Resource == []) orelse (Resource == exmpp_jid:prep_resource(JID)));
|
||||||
subscription ->
|
is_type_match(subscription, Value, _JID, Subscription, _Groups) ->
|
||||||
Value == Subscription;
|
Value == Subscription;
|
||||||
group ->
|
is_type_match(group, Value, _JID, _Subscription, Groups) ->
|
||||||
lists:member(Value, Groups)
|
lists:member(Value, Groups).
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
@ -772,14 +801,9 @@ updated_list(_,
|
|||||||
Old
|
Old
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
update_tables(Host) ->
|
update_tables(Host, mnesia) ->
|
||||||
Fields = record_info(fields, privacy_list),
|
|
||||||
case mnesia:table_info(privacy_list, attributes) of
|
|
||||||
Fields ->
|
|
||||||
convert_to_exmpp();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
gen_storage_migration:migrate_mnesia(
|
||||||
Host, privacy_default_list,
|
Host, privacy_default_list,
|
||||||
[{privacy, [us, default, lists],
|
[{privacy, [us, default, lists],
|
||||||
@ -796,11 +820,12 @@ update_tables(Host) ->
|
|||||||
match_message = MatchMessage,
|
match_message = MatchMessage,
|
||||||
match_presence_in = MatchPresenceIn,
|
match_presence_in = MatchPresenceIn,
|
||||||
match_presence_out = MatchPresenceOut}) ->
|
match_presence_out = MatchPresenceOut}) ->
|
||||||
|
ValueBin = convert_value_to_binary(Value),
|
||||||
gen_storage:write(Host,
|
gen_storage:write(Host,
|
||||||
#privacy_list_data{username_server = US,
|
#privacy_list_data{user_host = US,
|
||||||
name = Name,
|
name = Name,
|
||||||
type = Type,
|
type = Type,
|
||||||
value = Value,
|
value = ValueBin,
|
||||||
action = Action,
|
action = Action,
|
||||||
order = Order,
|
order = Order,
|
||||||
match_all = MatchAll,
|
match_all = MatchAll,
|
||||||
@ -810,23 +835,26 @@ update_tables(Host) ->
|
|||||||
match_presence_out = MatchPresenceOut})
|
match_presence_out = MatchPresenceOut})
|
||||||
end, List),
|
end, List),
|
||||||
gen_storage:write(Host,
|
gen_storage:write(Host,
|
||||||
#privacy_list{username_server = US,
|
#privacy_list{user_host = US,
|
||||||
name = Name})
|
name = Name})
|
||||||
end, Lists),
|
end, Lists),
|
||||||
if
|
if
|
||||||
is_list(Default) ->
|
is_list(Default) ->
|
||||||
#privacy_default_list{username_server = US,
|
#privacy_default_list{user_host = US,
|
||||||
name = Default};
|
name = Default};
|
||||||
true -> null
|
true -> null
|
||||||
end
|
end
|
||||||
end}]),
|
end}]);
|
||||||
|
|
||||||
|
update_tables(Host, odbc) ->
|
||||||
gen_storage_migration:migrate_odbc(
|
gen_storage_migration:migrate_odbc(
|
||||||
Host, [privacy_default_list, privacy_list, privacy_list_data],
|
Host, [privacy_default_list, privacy_list, privacy_list_data],
|
||||||
[{[{"privacy_list", ["username", "name", "id"]},
|
[{[{"privacy_list", ["username", "name", "id"]},
|
||||||
{"privacy_list_data", []}],
|
{"privacy_list_data", ["id","t","value","action","ord","match_all","match_iq","match_message",
|
||||||
|
"match_presence_in","match_presence_out"]}],
|
||||||
fun(SELECT, Username, Name, Id) ->
|
fun(SELECT, Username, Name, Id) ->
|
||||||
US = {Username, Host},
|
US = {Username, Host},
|
||||||
DefaultLists = [#privacy_default_list{username_server = US,
|
DefaultLists = [#privacy_default_list{user_host = US,
|
||||||
name = Name}
|
name = Name}
|
||||||
|| [_, _] <- SELECT(["username", "name"],
|
|| [_, _] <- SELECT(["username", "name"],
|
||||||
"privacy_default_list",
|
"privacy_default_list",
|
||||||
@ -859,22 +887,22 @@ update_tables(Host) ->
|
|||||||
{subscription, to}
|
{subscription, to}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
ValueBin = convert_value_to_binary(Value),
|
||||||
Action =
|
Action =
|
||||||
case SAction of
|
case SAction of
|
||||||
"a" -> allow;
|
"a" -> allow;
|
||||||
"d" -> deny
|
"d" -> deny
|
||||||
end,
|
end,
|
||||||
Order = list_to_integer(SOrder),
|
Order = list_to_integer(SOrder),
|
||||||
MatchAll = SMatchAll == "1" orelse SMatchAll == "t",
|
MatchAll = ejabberd_odbc:to_bool(SMatchAll),
|
||||||
MatchIQ = SMatchIQ == "1" orelse SMatchIQ == "t" ,
|
MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
|
||||||
MatchMessage = SMatchMessage == "1" orelse SMatchMessage == "t",
|
MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
|
||||||
MatchPresenceIn = SMatchPresenceIn == "1" orelse SMatchPresenceIn == "t",
|
MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
|
||||||
MatchPresenceOut = SMatchPresenceOut == "1" orelse SMatchPresenceOut == "t",
|
MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
|
||||||
|
#privacy_list_data{user_host = US,
|
||||||
#privacy_list_data{username_server = US,
|
|
||||||
name = Name,
|
name = Name,
|
||||||
type = Type,
|
type = Type,
|
||||||
value = Value,
|
value = ValueBin,
|
||||||
action = Action,
|
action = Action,
|
||||||
order = Order,
|
order = Order,
|
||||||
match_all = MatchAll,
|
match_all = MatchAll,
|
||||||
@ -887,53 +915,26 @@ update_tables(Host) ->
|
|||||||
"match_presence_out"],
|
"match_presence_out"],
|
||||||
"privacy_list_data",
|
"privacy_list_data",
|
||||||
[{"id", Id}])),
|
[{"id", Id}])),
|
||||||
[#privacy_list{username_server = US,
|
[#privacy_list{user_host = US,
|
||||||
name = Name} | DefaultLists ++ ListData]
|
name = Name} | DefaultLists ++ ListData]
|
||||||
end},
|
end},
|
||||||
{"privacy_default_list", ["username", "name"],
|
{"privacy_default_list", ["username", "name"],
|
||||||
fun(_, Username, Name) ->
|
fun(_, Username, Name) ->
|
||||||
US = {Username, Host},
|
US = {Username, Host},
|
||||||
[#privacy_default_list{username_server = US,
|
[#privacy_default_list{user_host = US,
|
||||||
name = Name}]
|
name = Name}]
|
||||||
end}
|
end}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
convert_value_to_binary({U, H, R}) ->
|
||||||
Fun = fun() ->
|
exmpp_jid:to_binary(U, H, R);
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2, done, privacy, write)
|
convert_value_to_binary(Value) when is_list(Value) ->
|
||||||
end,
|
list_to_binary(Value);
|
||||||
mnesia:transaction(Fun).
|
convert_value_to_binary(none) ->
|
||||||
|
<<"none">>;
|
||||||
convert_to_exmpp2(#privacy{us = {U, S} = Key, lists = L} = P, Acc) ->
|
convert_value_to_binary(both) ->
|
||||||
U1 = convert_jid_to_exmpp(U),
|
<<"both">>;
|
||||||
L1 = convert_lists_to_exmpp(L),
|
convert_value_to_binary(from) ->
|
||||||
New_P = P#privacy{
|
<<"from">>;
|
||||||
us = {U1, S},
|
convert_value_to_binary(to) ->
|
||||||
lists = L1
|
<<"to">>.
|
||||||
},
|
|
||||||
if
|
|
||||||
New_P /= P -> mnesia:delete({privacy, Key}), mnesia:write(New_P);
|
|
||||||
true -> ok
|
|
||||||
end,
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
convert_jid_to_exmpp("") -> undefined;
|
|
||||||
convert_jid_to_exmpp(V) -> V.
|
|
||||||
|
|
||||||
convert_lists_to_exmpp(L) ->
|
|
||||||
convert_lists_to_exmpp2(L, []).
|
|
||||||
|
|
||||||
convert_lists_to_exmpp2([{Name, List} | Rest], Result) ->
|
|
||||||
convert_lists_to_exmpp2(Rest,
|
|
||||||
[{Name, convert_list_to_exmpp(List, [])} | Result]);
|
|
||||||
convert_lists_to_exmpp2([], Result) ->
|
|
||||||
lists:reverse(Result).
|
|
||||||
|
|
||||||
convert_list_to_exmpp([#listitem{type = jid, value = {U, S, R}} = I | Rest],
|
|
||||||
Result) ->
|
|
||||||
U1 = convert_jid_to_exmpp(U),
|
|
||||||
R1 = convert_jid_to_exmpp(R),
|
|
||||||
New_I = I#listitem{value = {U1, S, R1}},
|
|
||||||
convert_list_to_exmpp(Rest, [New_I | Result]);
|
|
||||||
convert_list_to_exmpp([], Result) ->
|
|
||||||
lists:reverse(Result).
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-record(privacy, {us,
|
-record(privacy, {user_host,
|
||||||
default = none,
|
default = none,
|
||||||
lists = []}).
|
lists = []}).
|
||||||
|
|
||||||
|
@ -24,6 +24,34 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / private_storage
|
||||||
|
%%% usns = {Username::string(), Host::string(), Namespace::string()}
|
||||||
|
%%% xml = xmlelement()
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / private_storage
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% namespace = varchar250
|
||||||
|
%%% data = text
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / private_storage
|
||||||
|
%%% usns = {Username::binary(), Host::binary(), Namespace::atom()}
|
||||||
|
%%% xml = xmlel()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / private_storage
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / private_storage
|
||||||
|
%%% user_host_ns = {Username::binary(), Host::binary(), Namespace::atom()}
|
||||||
|
%%% xml = xmlel()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / private_storage
|
||||||
|
%%% user = varchar
|
||||||
|
%%% host = varchar
|
||||||
|
%%% ns = varchar250
|
||||||
|
%%% xml = text
|
||||||
|
|
||||||
-module(mod_private).
|
-module(mod_private).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -38,8 +66,8 @@
|
|||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
%% TODO: usns instead of user_server_ns requires no migration
|
%% TODO: usns instead of user_host_ns requires no migration
|
||||||
-record(private_storage, {user_server_ns, xml}).
|
-record(private_storage, {user_host_ns, xml}).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
HostB = list_to_binary(Host),
|
HostB = list_to_binary(Host),
|
||||||
@ -49,8 +77,8 @@ start(Host, Opts) ->
|
|||||||
[{disc_only_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, private_storage)},
|
{attributes, record_info(fields, private_storage)},
|
||||||
{types, [{user_server_ns, {text, text, text}}]}]),
|
{types, [{user_host_ns, {binary, binary, atom}}, {xml, xmlel}]}]),
|
||||||
update_table(Host),
|
update_table(Host, Backend),
|
||||||
ejabberd_hooks:add(remove_user, HostB,
|
ejabberd_hooks:add(remove_user, HostB,
|
||||||
?MODULE, remove_user, 50),
|
?MODULE, remove_user, 50),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE,
|
||||||
@ -146,10 +174,11 @@ check_ns(_From, _To, #iq{payload = SubEl}) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% The xml is stored as xmlel() in mnesia, but as text in odbc
|
||||||
set_data(LUser, LServer, El) ->
|
set_data(LUser, LServer, El) ->
|
||||||
XMLNS = exmpp_xml:get_ns_as_atom(El),
|
XMLNS = exmpp_xml:get_ns_as_atom(El),
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#private_storage{user_server_ns = {LUser, LServer, XMLNS},
|
#private_storage{user_host_ns = {LUser, LServer, XMLNS},
|
||||||
xml = El}).
|
xml = El}).
|
||||||
|
|
||||||
get_data(LUser, LServer, Els) ->
|
get_data(LUser, LServer, Els) ->
|
||||||
@ -175,9 +204,9 @@ remove_user(User, Server)
|
|||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
Records = gen_storage:select(LServer, private_storage,
|
Records = gen_storage:select(LServer, private_storage,
|
||||||
[{'=', user_server_ns, {LUser, LServer, '_'}}]),
|
[{'=', user_host_ns, {LUser, LServer, '_'}}]),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(#private_storage{user_server_ns = USNS}) ->
|
fun(#private_storage{user_host_ns = USNS}) ->
|
||||||
gen_storage:delete(LServer, {private_storage, USNS})
|
gen_storage:delete(LServer, {private_storage, USNS})
|
||||||
end, Records)
|
end, Records)
|
||||||
end,
|
end,
|
||||||
@ -187,97 +216,27 @@ remove_user(User, Server)
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
update_table(Host) ->
|
update_table(Host, mnesia) ->
|
||||||
Fields = record_info(fields, private_storage),
|
|
||||||
case mnesia:table_info(private_storage, attributes) of
|
|
||||||
Fields ->
|
|
||||||
convert_to_exmpp(),
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
gen_storage_migration:migrate_mnesia(
|
||||||
Host, private_storage,
|
Host, private_storage,
|
||||||
[{private_storage, [userns, xml],
|
[{private_storage, [usns, xml],
|
||||||
fun({private_storage, {User, NS}, Xml}) ->
|
|
||||||
#private_storage{user_server_ns = {User, Host, NS},
|
|
||||||
xml = lists:flatten(xml:element_to_string(Xml))}
|
|
||||||
end},
|
|
||||||
{private_storage, [user_server_ns, xml],
|
|
||||||
fun({private_storage, {User, Server, NS}, Xml}) ->
|
fun({private_storage, {User, Server, NS}, Xml}) ->
|
||||||
#private_storage{user_server_ns = {User, Server, NS},
|
U1 = list_to_binary(User),
|
||||||
xml = lists:flatten(xml:element_to_string(Xml))}
|
S1 = list_to_binary(Server),
|
||||||
end}]),
|
NS1 = list_to_atom(NS),
|
||||||
|
El1 = exmpp_xml:xmlelement_to_xmlel(Xml, [?NS_PRIVATE],
|
||||||
|
[{?NS_XMPP, ?NS_XMPP_pfx}]),
|
||||||
|
#private_storage{user_host_ns = {U1, S1, NS1},
|
||||||
|
xml = El1}
|
||||||
|
end}]);
|
||||||
|
|
||||||
|
update_table(Host, odbc) ->
|
||||||
gen_storage_migration:migrate_odbc(
|
gen_storage_migration:migrate_odbc(
|
||||||
Host, [private_storage],
|
Host, [private_storage],
|
||||||
[{"private_storage", ["username", "namespace", "data"],
|
[{"private_storage", ["username", "namespace", "data"],
|
||||||
fun(_, Username, Namespace, Data) ->
|
fun(_, Username, Namespace, Data) ->
|
||||||
[#private_storage{user_server_ns = {Username, Host, Namespace},
|
[#private_storage{user_host_ns = {Username, Host, Namespace},
|
||||||
xml = Data}]
|
xml = Data}]
|
||||||
end}]);
|
end}]).
|
||||||
[userns, xml] ->
|
|
||||||
?INFO_MSG("Converting private_storage table from "
|
|
||||||
"{user, default, lists} format", []),
|
|
||||||
Host = ?MYNAME,
|
|
||||||
{atomic, ok} = mnesia:create_table(
|
|
||||||
mod_private_tmp_table,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{type, bag},
|
|
||||||
{local_content, true},
|
|
||||||
{record_name, private_storage},
|
|
||||||
{attributes, record_info(fields, private_storage)}]),
|
|
||||||
mnesia:transform_table(private_storage, ignore, Fields),
|
|
||||||
F1 = fun() ->
|
|
||||||
mnesia:write_lock_table(mod_private_tmp_table),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(#private_storage{user_server_ns = {U, NS}, xml = El} = R, _) ->
|
|
||||||
NS1 = list_to_atom(NS),
|
|
||||||
El0 = exmpp_xml:xmlelement_to_xmlel(El,
|
|
||||||
[?NS_PRIVATE], [{?NS_XMPP, ?NS_XMPP_pfx}]),
|
|
||||||
El1 = exmpp_xml:remove_whitespaces_deeply(El0),
|
|
||||||
mnesia:dirty_write(
|
|
||||||
mod_private_tmp_table,
|
|
||||||
R#private_storage{user_server_ns = {U, Host, NS1}, xml = El1})
|
|
||||||
end, ok, private_storage)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F1),
|
|
||||||
mnesia:clear_table(private_storage),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(private_storage),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_private_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_private_tmp_table);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating private_storage table", []),
|
|
||||||
mnesia:transform_table(private_storage, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:first(private_storage) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
none;
|
|
||||||
{U, _S, _NS} when is_binary(U) ->
|
|
||||||
none;
|
|
||||||
{U, _S, _NS} when is_list(U) ->
|
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2,
|
|
||||||
done, private_storage, write)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(Fun).
|
|
||||||
|
|
||||||
convert_to_exmpp2(#private_storage{user_server_ns = {U, S, NS} = Key, xml = El} = R,
|
|
||||||
Acc) ->
|
|
||||||
mnesia:delete({private_storage, Key}),
|
|
||||||
U1 = list_to_binary(U),
|
|
||||||
S1 = list_to_binary(S),
|
|
||||||
NS1 = list_to_atom(NS),
|
|
||||||
El1 = exmpp_xml:xmlelement_to_xmlel(El,
|
|
||||||
[?NS_PRIVATE], [{?NS_XMPP, ?NS_XMPP_pfx}]),
|
|
||||||
New_R = R#private_storage{
|
|
||||||
user_server_ns = {U1, S1, NS1},
|
|
||||||
xml = El1},
|
|
||||||
mnesia:write(New_R),
|
|
||||||
Acc.
|
|
||||||
|
@ -33,6 +33,73 @@
|
|||||||
%%% Roster version is a hash digest of the entire roster.
|
%%% Roster version is a hash digest of the entire roster.
|
||||||
%%% No additional data is stored in DB.
|
%%% No additional data is stored in DB.
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / roster
|
||||||
|
%%% usj = {Username::string(), Host::string(), {ContactUsername::string(), ContactServer::string(), ""}}
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% jid = {ContactUsername::string(), ContactServer::string(), ""}
|
||||||
|
%%% name = ContactRosterName::string()
|
||||||
|
%%% subscription = none | from | to | both
|
||||||
|
%%% ask = none | out | in
|
||||||
|
%%% groups = [GroupName::string()]
|
||||||
|
%%% askmessage = binary()
|
||||||
|
%%% xs = [xmlelement()]
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / rosterusers
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% jid = varchar250
|
||||||
|
%%% nick = text
|
||||||
|
%%% subscription = character1
|
||||||
|
%%% ask = character1
|
||||||
|
%%% askmessage = text
|
||||||
|
%%% server = character1
|
||||||
|
%%% subscribe = text
|
||||||
|
%%% type = text
|
||||||
|
%%% 2.1.x / odbc / rostergroups
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% jid = varchar250
|
||||||
|
%%% grp = text
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / roster
|
||||||
|
%%% usj = {Username::binary(), Host::binary(), {ContactUsername::binary(), ContactServer::binary(), undefined}}
|
||||||
|
%%% us = {Userma,e::binary(), Host::binary()}
|
||||||
|
%%% jid = {ContactUsername::binary(), ContactServer::binary(), undefined}
|
||||||
|
%%% name = ContactRosterName::binary()
|
||||||
|
%%% subscription = none | from | to | both
|
||||||
|
%%% ask = none | out | in
|
||||||
|
%%% groups = [GroupName::binary()]
|
||||||
|
%%% askmessage = binary()
|
||||||
|
%%% xs = [xmlel()]
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / rosterusers
|
||||||
|
%%% 3.0.0-prealpha / odbc / rostergroups
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / rosteritem
|
||||||
|
%%% user_host_jid = {Username::binary(), Host::binary(), {ContactUsername::binary(), ContactServer::binary(), undefined}}
|
||||||
|
%%% name = ContactRosterName::binary()
|
||||||
|
%%% subscription = none | from | to | both
|
||||||
|
%%% ask = none | out | in
|
||||||
|
%%% askmessage = binary()
|
||||||
|
%%% 3.0.0-alpha / mnesia / rostergroup
|
||||||
|
%%% user_host_jid = {Username::binary(), Host::binary(), {ContactUsername::binary(), ContactServer::binary(), undefined}}
|
||||||
|
%%% grp = GroupName::binary()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / rosteritem
|
||||||
|
%%% user = varchar250
|
||||||
|
%%% host = varchar250
|
||||||
|
%%% jid = varchar250
|
||||||
|
%%% name = text
|
||||||
|
%%% subscription = text = none | from | to | both
|
||||||
|
%%% ask = text = none | out | in
|
||||||
|
%%% askmessage = text
|
||||||
|
%%% 3.0.0-alpha / odbc / rostergroup
|
||||||
|
%%% user = varchar250
|
||||||
|
%%% host = varchar250
|
||||||
|
%%% jid = varchar250
|
||||||
|
%%% grp = varchar250
|
||||||
|
|
||||||
-module(mod_roster).
|
-module(mod_roster).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -62,29 +129,6 @@
|
|||||||
-include("web/ejabberd_http.hrl").
|
-include("web/ejabberd_http.hrl").
|
||||||
-include("web/ejabberd_web_admin.hrl").
|
-include("web/ejabberd_web_admin.hrl").
|
||||||
|
|
||||||
%% @type rosteritem() = {roster, USJ, US, Contact_JID, Name, Subscription, Ask, Groups, Askmessage, Xs}
|
|
||||||
%% USJ = {LUser, LServer, Prepd_Contact_JID}
|
|
||||||
%% LUser = binary()
|
|
||||||
%% LServer = binary()
|
|
||||||
%% Prepd_Contact_JID = jlib:shortjid()
|
|
||||||
%% US = {LUser, LServer}
|
|
||||||
%% Contact_JID = jlib:shortjid()
|
|
||||||
%% Name = binary()
|
|
||||||
%% Subscription = none | to | from | both
|
|
||||||
%% Ask = none | out | in | both
|
|
||||||
%% Groups = [binary()]
|
|
||||||
%% Askmessage = binary()
|
|
||||||
%% Xs = [exmpp_xml:xmlel()]
|
|
||||||
|
|
||||||
%% TODO: keep a non-prepped jid like in mnesia mod_roster?
|
|
||||||
-record(rosteritem, {user_server_jid,
|
|
||||||
name = "",
|
|
||||||
subscription = none,
|
|
||||||
ask = none,
|
|
||||||
askmessage = ""}).
|
|
||||||
-record(rostergroup, {user_server_jid,
|
|
||||||
grp}).
|
|
||||||
|
|
||||||
%% @spec (Host, Opts) -> term()
|
%% @spec (Host, Opts) -> term()
|
||||||
%% Host = string()
|
%% Host = string()
|
||||||
%% Opts = list()
|
%% Opts = list()
|
||||||
@ -98,7 +142,7 @@ start(Host, Opts) when is_list(Host) ->
|
|||||||
rosteritem, [{disc_copies, [node()]},
|
rosteritem, [{disc_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, rosteritem)},
|
{attributes, record_info(fields, rosteritem)},
|
||||||
{types, [{user_server_jid, {text, text, ljid}},
|
{types, [{user_host_jid, {text, text, ljid}},
|
||||||
{subscription, atom},
|
{subscription, atom},
|
||||||
{ask, atom}]}]),
|
{ask, atom}]}]),
|
||||||
gen_storage:create_table(Backend, HostB,
|
gen_storage:create_table(Backend, HostB,
|
||||||
@ -106,10 +150,10 @@ start(Host, Opts) when is_list(Host) ->
|
|||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, rostergroup)},
|
{attributes, record_info(fields, rostergroup)},
|
||||||
{types, [{user_server_jid, {text, text, ljid}}]}]),
|
{types, [{user_host_jid, {text, text, ljid}}]}]),
|
||||||
mnesia:create_table(roster_version, [{disc_copies, [node()]},
|
mnesia:create_table(roster_version, [{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, roster_version)}]),
|
{attributes, record_info(fields, roster_version)}]),
|
||||||
update_tables(Host),
|
update_table(Host, Backend),
|
||||||
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),
|
||||||
ejabberd_hooks:add(roster_get, HostB,
|
ejabberd_hooks:add(roster_get, HostB,
|
||||||
@ -315,19 +359,20 @@ get_user_roster(Acc, {U, S}) when is_binary(U), is_binary(S) ->
|
|||||||
true
|
true
|
||||||
end, Items) ++ Acc.
|
end, Items) ++ Acc.
|
||||||
|
|
||||||
|
%% Reads the roster information from the database, and returns a list of #roster records.
|
||||||
get_roster(LUser, LServer) ->
|
get_roster(LUser, LServer) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
U = gen_storage:select(LServer, rosteritem,
|
U = gen_storage:select(LServer, rosteritem,
|
||||||
[{'=', user_server_jid, {LUser, LServer, '_'}}]),
|
[{'=', user_host_jid, {LUser, LServer, '_'}}]),
|
||||||
G = gen_storage:select(LServer, rostergroup,
|
G = gen_storage:select(LServer, rostergroup,
|
||||||
[{'=', user_server_jid, {LUser, LServer, '_'}}]),
|
[{'=', user_host_jid, {LUser, LServer, '_'}}]),
|
||||||
[storageroster_to_roster(Rosteritem, G) ||
|
[storageroster_to_roster(Rosteritem, G) ||
|
||||||
Rosteritem <- U]
|
Rosteritem <- U]
|
||||||
end,
|
end,
|
||||||
{atomic, Rs} = gen_storage:transaction(LServer, rosteritem, F),
|
{atomic, Rs} = gen_storage:transaction(LServer, rosteritem, F),
|
||||||
Rs.
|
Rs.
|
||||||
|
|
||||||
storageroster_to_roster(#rosteritem{user_server_jid = {U, S, JID} = USJ,
|
storageroster_to_roster(#rosteritem{user_host_jid = {U, S, JID} = USJ,
|
||||||
name = Name,
|
name = Name,
|
||||||
subscription = Subscription,
|
subscription = Subscription,
|
||||||
ask = Ask,
|
ask = Ask,
|
||||||
@ -336,21 +381,26 @@ storageroster_to_roster(#rosteritem{user_server_jid = {U, S, JID} = USJ,
|
|||||||
US = {U, S},
|
US = {U, S},
|
||||||
Groups =
|
Groups =
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(#rostergroup{user_server_jid = USJ1, grp = G}, R)
|
fun(#rostergroup{user_host_jid = USJ1, grp = G}, R)
|
||||||
when USJ =:= USJ1 ->
|
when USJ =:= USJ1 ->
|
||||||
[G | R];
|
%% G is a string when using odbc beckend, and a binary when using mnesia
|
||||||
|
GString = convert_to_string(G),
|
||||||
|
[GString | R];
|
||||||
(_, R) ->
|
(_, R) ->
|
||||||
R
|
R
|
||||||
end, [], Rostergroups),
|
end, [], Rostergroups),
|
||||||
#roster{usj = {US, JID},
|
#roster{usj = {US, JID},
|
||||||
us = US,
|
us = US,
|
||||||
jid = JID,
|
jid = JID,
|
||||||
name = Name,
|
name = convert_to_string(Name),
|
||||||
subscription = Subscription,
|
subscription = Subscription,
|
||||||
ask = Ask,
|
ask = Ask,
|
||||||
askmessage = AskMessage,
|
askmessage = AskMessage,
|
||||||
groups = Groups}.
|
groups = Groups}.
|
||||||
|
|
||||||
|
convert_to_string(A) when is_binary(A) -> binary_to_list(A);
|
||||||
|
convert_to_string(A) when is_list(A) -> A.
|
||||||
|
|
||||||
%% @spec (Item) -> XML
|
%% @spec (Item) -> XML
|
||||||
%% Item = rosteritem()
|
%% Item = rosteritem()
|
||||||
%% XML = exmpp_xml:xmlel()
|
%% XML = exmpp_xml:xmlel()
|
||||||
@ -441,7 +491,7 @@ process_item_set(From, To, #xmlel{} = El) ->
|
|||||||
ask = Ask2,
|
ask = Ask2,
|
||||||
askmessage = AskMessage2,
|
askmessage = AskMessage2,
|
||||||
groups = Groups} ->
|
groups = Groups} ->
|
||||||
I2 = #rosteritem{user_server_jid = {LUser, LServer, LJID},
|
I2 = #rosteritem{user_host_jid = {LUser, LServer, LJID},
|
||||||
name = Name,
|
name = Name,
|
||||||
subscription = Subscription2,
|
subscription = Subscription2,
|
||||||
ask = Ask2,
|
ask = Ask2,
|
||||||
@ -451,7 +501,7 @@ process_item_set(From, To, #xmlel{} = El) ->
|
|||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Group) ->
|
fun(Group) ->
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#rostergroup{user_server_jid = {LUser, LServer, LJID},
|
#rostergroup{user_host_jid = {LUser, LServer, LJID},
|
||||||
grp = Group})
|
grp = Group})
|
||||||
end, Groups)
|
end, Groups)
|
||||||
end,
|
end,
|
||||||
@ -635,7 +685,7 @@ get_subscription_lists(_, User, Server)
|
|||||||
try
|
try
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
case gen_storage:dirty_select(LServer, rosteritem, [{'=', user_server_jid, {LUser, LServer, '_'}}]) of
|
case gen_storage:dirty_select(LServer, rosteritem, [{'=', user_host_jid, {LUser, LServer, '_'}}]) of
|
||||||
Items when is_list(Items) ->
|
Items when is_list(Items) ->
|
||||||
fill_subscription_lists(Items, [], []);
|
fill_subscription_lists(Items, [], []);
|
||||||
_ ->
|
_ ->
|
||||||
@ -653,7 +703,7 @@ get_subscription_lists(_, User, Server)
|
|||||||
%% New_F = [jlib:shortjid()]
|
%% New_F = [jlib:shortjid()]
|
||||||
%% New_T = [jlib:shortjid()]
|
%% New_T = [jlib:shortjid()]
|
||||||
|
|
||||||
fill_subscription_lists([#rosteritem{user_server_jid = {_, _, LJ},
|
fill_subscription_lists([#rosteritem{user_host_jid = {_, _, LJ},
|
||||||
subscription = Subscription} | Is],
|
subscription = Subscription} | Is],
|
||||||
F, T) ->
|
F, T) ->
|
||||||
case Subscription of
|
case Subscription of
|
||||||
@ -714,7 +764,7 @@ process_subscription(Direction, User, Server, JID1, Type, Reason)
|
|||||||
F = fun() ->
|
F = fun() ->
|
||||||
Item = case gen_storage:read(LServer, {rosteritem, {LUser, LServer, LJID}}) of
|
Item = case gen_storage:read(LServer, {rosteritem, {LUser, LServer, LJID}}) of
|
||||||
[] ->
|
[] ->
|
||||||
#rosteritem{user_server_jid = {LUser, LServer, LJID}
|
#rosteritem{user_host_jid = {LUser, LServer, LJID}
|
||||||
};
|
};
|
||||||
[I] ->
|
[I] ->
|
||||||
I
|
I
|
||||||
@ -911,12 +961,12 @@ remove_user(User, Server)
|
|||||||
fun(R) ->
|
fun(R) ->
|
||||||
gen_storage:delete_object(LServer, R)
|
gen_storage:delete_object(LServer, R)
|
||||||
end,
|
end,
|
||||||
gen_storage:select(LServer, rosteritem, [{'=', user_server_jid, {LUser, LServer, '_'}}])),
|
gen_storage:select(LServer, rosteritem, [{'=', user_host_jid, {LUser, LServer, '_'}}])),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
gen_storage:delete_object(LServer, R)
|
gen_storage:delete_object(LServer, R)
|
||||||
end,
|
end,
|
||||||
gen_storage:select(LServer, rostergroup, [{'=', user_server_jid, {LUser, LServer, '_'}}]))
|
gen_storage:select(LServer, rostergroup, [{'=', user_host_jid, {LUser, LServer, '_'}}]))
|
||||||
end,
|
end,
|
||||||
gen_storage:transaction(LServer, rosteritem, F)
|
gen_storage:transaction(LServer, rosteritem, F)
|
||||||
catch
|
catch
|
||||||
@ -1014,7 +1064,7 @@ process_item_set_t(LUser, LServer, #xmlel{} = El) ->
|
|||||||
ask = Ask,
|
ask = Ask,
|
||||||
askmessage = AskMessage,
|
askmessage = AskMessage,
|
||||||
groups = Groups} ->
|
groups = Groups} ->
|
||||||
gen_storage:write(LServer, #rosteritem{user_server_jid = {LUser, LServer, LJID},
|
gen_storage:write(LServer, #rosteritem{user_host_jid = {LUser, LServer, LJID},
|
||||||
name = Name,
|
name = Name,
|
||||||
subscription = Subscription,
|
subscription = Subscription,
|
||||||
ask = Ask,
|
ask = Ask,
|
||||||
@ -1023,7 +1073,7 @@ process_item_set_t(LUser, LServer, #xmlel{} = El) ->
|
|||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Group) ->
|
fun(Group) ->
|
||||||
gen_storage:write(LServer,
|
gen_storage:write(LServer,
|
||||||
#rostergroup{user_server_jid = {LUser, LServer, LJID},
|
#rostergroup{user_host_jid = {LUser, LServer, LJID},
|
||||||
grp = Group})
|
grp = Group})
|
||||||
end, Groups)
|
end, Groups)
|
||||||
end
|
end
|
||||||
@ -1082,10 +1132,10 @@ get_in_pending_subscriptions(Ls, User, Server)
|
|||||||
JID = exmpp_jid:make(User, Server),
|
JID = exmpp_jid:make(User, Server),
|
||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
case gen_storage:dirty_select(LServer, rosteritem, [{'=', user_server_jid, {LUser, LServer, '_'}}]) of
|
case gen_storage:dirty_select(LServer, rosteritem, [{'=', user_host_jid, {LUser, LServer, '_'}}]) of
|
||||||
Result when is_list(Result) ->
|
Result when is_list(Result) ->
|
||||||
Ls ++ lists:map(
|
Ls ++ lists:map(
|
||||||
fun(#rosteritem{user_server_jid = {_, _, RJID},
|
fun(#rosteritem{user_host_jid = {_, _, RJID},
|
||||||
askmessage = Message}) ->
|
askmessage = Message}) ->
|
||||||
Status = if is_binary(Message) ->
|
Status = if is_binary(Message) ->
|
||||||
binary_to_list(Message);
|
binary_to_list(Message);
|
||||||
@ -1165,169 +1215,92 @@ get_jid_info(_, User, Server, JID)
|
|||||||
{none, []}
|
{none, []}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
%% @hidden
|
%% Only supports migration from ejabberd 1.1.2 or higher.
|
||||||
|
|
||||||
update_table() ->
|
update_table(Host, mnesia) ->
|
||||||
Fields = record_info(fields, roster),
|
gen_storage_migration:migrate_mnesia(
|
||||||
case mnesia:table_info(roster, attributes) of
|
Host, rosteritem,
|
||||||
Fields ->
|
[{roster, [usj, us, jid, name, subscription, ask, groups, askmessage, xs],
|
||||||
convert_to_exmpp();
|
fun({roster, USJ, _, _, Name, Subscription, Ask, Groups, AskMessage, _Xs}) ->
|
||||||
[uj, user, jid, name, subscription, ask, groups, xattrs, xs] ->
|
%% Convert "" to undefined in JIDs and string() to binary().
|
||||||
convert_table1(Fields);
|
{USJ_U, USJ_S, {USJ_JU, USJ_JS, USJ_JR}} = USJ,
|
||||||
[usj, us, jid, name, subscription, ask, groups, xattrs, xs] ->
|
|
||||||
convert_table2(Fields);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating roster table", []),
|
|
||||||
mnesia:transform_table(roster, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
%% Convert roster table to support virtual host
|
|
||||||
convert_table1(Fields) ->
|
|
||||||
?INFO_MSG("Virtual host support: converting roster table from "
|
|
||||||
"{uj, user, jid, name, subscription, ask, groups, xattrs, xs} format", []),
|
|
||||||
Host = ?MYNAME,
|
|
||||||
{atomic, ok} = mnesia:create_table(
|
|
||||||
mod_roster_tmp_table,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{type, bag},
|
|
||||||
{local_content, true},
|
|
||||||
{record_name, roster},
|
|
||||||
{attributes, record_info(fields, roster)}]),
|
|
||||||
mnesia:del_table_index(roster, user),
|
|
||||||
mnesia:transform_table(roster, ignore, Fields),
|
|
||||||
F1 = fun() ->
|
|
||||||
mnesia:write_lock_table(mod_roster_tmp_table),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(#roster{usj = {U, {JID_U, JID_S, JID_R}}, us = U, xs = XS, askmessage = AM} = R, _) ->
|
|
||||||
U1 = convert_jid_to_exmpp(U),
|
|
||||||
JID_U1 = convert_jid_to_exmpp(JID_U),
|
|
||||||
JID_R1 = convert_jid_to_exmpp(JID_R),
|
|
||||||
JID1 = {JID_U1, JID_S, JID_R1},
|
|
||||||
XS1 = convert_xs_to_exmpp(XS),
|
|
||||||
AM1 = convert_askmessage_to_exmpp(AM),
|
|
||||||
mnesia:dirty_write(
|
|
||||||
mod_roster_tmp_table,
|
|
||||||
R#roster{usj = {U1, Host, JID1},
|
|
||||||
us = {U1, Host}, xs = XS1,
|
|
||||||
askmessage = AM1})
|
|
||||||
end, ok, roster)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F1),
|
|
||||||
mnesia:clear_table(roster),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(roster),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_roster_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_roster_tmp_table).
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
%% Convert roster table: xattrs fields become
|
|
||||||
convert_table2(Fields) ->
|
|
||||||
?INFO_MSG("Converting roster table from "
|
|
||||||
"{usj, us, jid, name, subscription, ask, groups, xattrs, xs} format", []),
|
|
||||||
mnesia:transform_table(roster, ignore, Fields),
|
|
||||||
convert_to_exmpp().
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:first(roster) of
|
|
||||||
{_User, Server, _JID} when is_binary(Server) ->
|
|
||||||
none;
|
|
||||||
{_User, Server, _JID} when is_list(Server) ->
|
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2,
|
|
||||||
done, roster, write);
|
|
||||||
'$end_of_table' ->
|
|
||||||
none
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(Fun).
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_to_exmpp2(#roster{
|
|
||||||
usj = {USJ_U, USJ_S, {USJ_JU, USJ_JS, USJ_JR}} = Key,
|
|
||||||
us = {US_U, US_S},
|
|
||||||
jid = {JID_U, JID_S, JID_R},
|
|
||||||
name = N, xs = XS, groups = G, askmessage = AM} = R, Acc) ->
|
|
||||||
% Remove old entry.
|
|
||||||
mnesia:delete({roster, Key}),
|
|
||||||
% Convert "" to undefined in JIDs and string() to binary().
|
|
||||||
USJ_U1 = convert_jid_to_exmpp(USJ_U),
|
USJ_U1 = convert_jid_to_exmpp(USJ_U),
|
||||||
USJ_S1 = convert_jid_to_exmpp(USJ_S),
|
USJ_S1 = convert_jid_to_exmpp(USJ_S),
|
||||||
USJ_JU1 = convert_jid_to_exmpp(USJ_JU),
|
USJ_JU1 = convert_jid_to_exmpp(USJ_JU),
|
||||||
USJ_JS1 = convert_jid_to_exmpp(USJ_JS),
|
USJ_JS1 = convert_jid_to_exmpp(USJ_JS),
|
||||||
USJ_JR1 = convert_jid_to_exmpp(USJ_JR),
|
USJ_JR1 = convert_jid_to_exmpp(USJ_JR),
|
||||||
US_U1 = convert_jid_to_exmpp(US_U),
|
USJ1 = {USJ_U1, USJ_S1, {USJ_JU1, USJ_JS1, USJ_JR1}},
|
||||||
US_S1 = convert_jid_to_exmpp(US_S),
|
lists:foreach(
|
||||||
JID_U1 = convert_jid_to_exmpp(JID_U),
|
fun(Group) ->
|
||||||
JID_S1 = convert_jid_to_exmpp(JID_S),
|
Group2 = list_to_binary(Group),
|
||||||
JID_R1 = convert_jid_to_exmpp(JID_R),
|
gen_storage:write(Host,
|
||||||
% Convert name.
|
#rostergroup{user_host_jid = USJ1,
|
||||||
N1 = convert_name_to_exmpp(N),
|
grp = Group2})
|
||||||
% Convert groups.
|
end, Groups),
|
||||||
G1 = convert_groups_to_exmpp(G, []),
|
Name2 = convert_name_to_exmpp(Name),
|
||||||
% Convert xs.
|
AskMessage2 = convert_askmessage_to_exmpp(AskMessage),
|
||||||
XS1 = convert_xs_to_exmpp(XS),
|
#rosteritem{user_host_jid = USJ1,
|
||||||
% Convert askmessage.
|
name = Name2,
|
||||||
AM1 = convert_askmessage_to_exmpp(AM),
|
subscription = Subscription,
|
||||||
% Prepare the new record.
|
ask = Ask,
|
||||||
New_R = R#roster{
|
askmessage = AskMessage2}
|
||||||
usj = {USJ_U1, USJ_S1, {USJ_JU1, USJ_JS1, USJ_JR1}},
|
end}
|
||||||
us = {US_U1, US_S1},
|
]);
|
||||||
jid = {JID_U1, JID_S1, JID_R1},
|
|
||||||
name = N1, groups = G1, xs = XS1, askmessage = AM1},
|
|
||||||
% Write the new record.
|
|
||||||
mnesia:write(New_R),
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
%% @hidden
|
update_table(Host, odbc) ->
|
||||||
|
gen_storage_migration:migrate_odbc(
|
||||||
|
Host, [rosteritem, rostergroup],
|
||||||
|
[{"rosterusers", ["username", "jid", "nick",
|
||||||
|
"subscription", "ask", "askmessage",
|
||||||
|
"server", "subscribe", "type"],
|
||||||
|
fun(SELECT,
|
||||||
|
Username, JID, Nick,
|
||||||
|
Subscription, Ask, AskMessage,
|
||||||
|
Server, Subscribe, Type) ->
|
||||||
|
USJ = {Username, Host, JID},
|
||||||
|
[#rosteritem{user_host_jid = USJ,
|
||||||
|
name = Nick,
|
||||||
|
subscription = case Subscription of
|
||||||
|
"B" -> both;
|
||||||
|
"T" -> to;
|
||||||
|
"F" -> from;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
ask = case Ask of
|
||||||
|
"S" -> subscribe;
|
||||||
|
"U" -> unsubscribe;
|
||||||
|
"B" -> both;
|
||||||
|
"O" -> out;
|
||||||
|
"I" -> in;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
askmessage = AskMessage}
|
||||||
|
| [#rostergroup{user_host_jid = USJ,
|
||||||
|
grp = Group}
|
||||||
|
|| [Group] <- SELECT(["grp"], "rostergroups", [{"username", Username},
|
||||||
|
{"jid", JID}])]]
|
||||||
|
end}]),
|
||||||
|
ejabberd_odbc:sql_query(Host, "DROP TABLE rostergroups").
|
||||||
|
|
||||||
convert_jid_to_exmpp("") -> undefined;
|
convert_jid_to_exmpp("") -> undefined;
|
||||||
convert_jid_to_exmpp(V) when is_list(V) -> list_to_binary(V).
|
convert_jid_to_exmpp(V) when is_list(V) -> list_to_binary(V).
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_name_to_exmpp(N) when is_list(N) -> list_to_binary(N).
|
convert_name_to_exmpp(N) when is_list(N) -> list_to_binary(N).
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_groups_to_exmpp([G | Rest], New_G) ->
|
|
||||||
convert_groups_to_exmpp(Rest, [list_to_binary(G) | New_G]);
|
|
||||||
convert_groups_to_exmpp([], New_G) ->
|
|
||||||
lists:reverse(New_G).
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_xs_to_exmpp(Els) ->
|
|
||||||
convert_xs_to_exmpp(Els, []).
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_xs_to_exmpp([El | Rest], Result) ->
|
|
||||||
New_El = exmpp_xml:xmlelement_to_xmlel(El,
|
|
||||||
[?NS_JABBER_CLIENT], [{?NS_XMPP, ?NS_XMPP_pfx}]),
|
|
||||||
convert_xs_to_exmpp(Rest, [New_El | Result]);
|
|
||||||
convert_xs_to_exmpp([], Result) ->
|
|
||||||
lists:reverse(Result).
|
|
||||||
|
|
||||||
%% @hidden
|
|
||||||
|
|
||||||
convert_askmessage_to_exmpp(AM) when is_binary(AM) ->
|
convert_askmessage_to_exmpp(AM) when is_binary(AM) ->
|
||||||
AM;
|
AM;
|
||||||
convert_askmessage_to_exmpp(AM) ->
|
convert_askmessage_to_exmpp(AM) ->
|
||||||
list_to_binary(AM).
|
list_to_binary(AM).
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
%% @spec (Acc, Host, Request) -> {stop, Result} | Acc
|
%% @spec (Acc, Host, Request) -> {stop, Result} | Acc
|
||||||
%% Acc = term()
|
%% Acc = term()
|
||||||
%% Host = string()
|
%% Host = string()
|
||||||
@ -1381,14 +1354,14 @@ user_roster(User, Server, Query, Lang) ->
|
|||||||
Groups =
|
Groups =
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun(Group) ->
|
fun(Group) ->
|
||||||
[?C(binary_to_list(Group)), ?BR]
|
[?C(Group), ?BR]
|
||||||
end, R#roster.groups),
|
end, R#roster.groups),
|
||||||
Pending = ask_to_pending(R#roster.ask),
|
Pending = ask_to_pending(R#roster.ask),
|
||||||
TDJID = build_contact_jid_td(R#roster.jid),
|
TDJID = build_contact_jid_td(R#roster.jid),
|
||||||
?XE("tr",
|
?XE("tr",
|
||||||
[TDJID,
|
[TDJID,
|
||||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||||
binary_to_list(R#roster.name)),
|
R#roster.name),
|
||||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||||
atom_to_list(R#roster.subscription)),
|
atom_to_list(R#roster.subscription)),
|
||||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||||
@ -1571,83 +1544,3 @@ us_to_list({User, Server}) ->
|
|||||||
webadmin_user(Acc, _User, _Server, Lang) ->
|
webadmin_user(Acc, _User, _Server, Lang) ->
|
||||||
% `Lang' is used by the `T' macro, called from the `ACT' macro.
|
% `Lang' is used by the `T' macro, called from the `ACT' macro.
|
||||||
Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])].
|
Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])].
|
||||||
|
|
||||||
|
|
||||||
update_tables(Host) ->
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
|
||||||
Host, rosteritem,
|
|
||||||
[{roster, [uj, user, jid, name, subscription, ask, groups, xattrs, xs],
|
|
||||||
fun({roster, {User, JID}, _, _, Name, Subscription, Ask, Groups, _Xattrs, _Xs}) ->
|
|
||||||
USJ = {User, Host, JID},
|
|
||||||
lists:foreach(
|
|
||||||
fun(Group) ->
|
|
||||||
gen_storage:write(Host,
|
|
||||||
#rostergroup{user_server_jid = USJ,
|
|
||||||
grp = Group})
|
|
||||||
end, Groups),
|
|
||||||
#rosteritem{user_server_jid = USJ,
|
|
||||||
name = Name,
|
|
||||||
subscription = Subscription,
|
|
||||||
ask = Ask}
|
|
||||||
end},
|
|
||||||
{roster, [usj, us, jid, name, subscription, ask, groups, xattrs, xs],
|
|
||||||
fun({roster, USJ, _, _, Name, Subscription, Ask, Groups, _Xattrs, _Xs}) ->
|
|
||||||
lists:foreach(
|
|
||||||
fun(Group) ->
|
|
||||||
gen_storage:write(Host,
|
|
||||||
#rostergroup{user_server_jid = USJ,
|
|
||||||
grp = Group})
|
|
||||||
end, Groups),
|
|
||||||
#rosteritem{user_server_jid = USJ,
|
|
||||||
name = Name,
|
|
||||||
subscription = Subscription,
|
|
||||||
ask = Ask}
|
|
||||||
end},
|
|
||||||
{roster, [usj, us, jid, name, subscription, ask, groups, askmessage, xs],
|
|
||||||
fun({roster, USJ, _, _, Name, Subscription, Ask, Groups, AskMessage, _Xs}) ->
|
|
||||||
lists:foreach(
|
|
||||||
fun(Group) ->
|
|
||||||
gen_storage:write(Host,
|
|
||||||
#rostergroup{user_server_jid = USJ,
|
|
||||||
grp = Group})
|
|
||||||
end, Groups),
|
|
||||||
#rosteritem{user_server_jid = USJ,
|
|
||||||
name = Name,
|
|
||||||
subscription = Subscription,
|
|
||||||
ask = Ask,
|
|
||||||
askmessage = AskMessage}
|
|
||||||
end}
|
|
||||||
]),
|
|
||||||
gen_storage_migration:migrate_odbc(
|
|
||||||
Host, [rosteritem, rostergroup],
|
|
||||||
[{"rosterusers", ["username", "jid", "nick",
|
|
||||||
"subscription", "ask", "askmessage",
|
|
||||||
"server", "subscribe", "type"],
|
|
||||||
fun(SELECT,
|
|
||||||
Username, JID, Nick,
|
|
||||||
Subscription, Ask, AskMessage,
|
|
||||||
Server, Subscribe, Type) ->
|
|
||||||
USJ = {Username, Host, JID},
|
|
||||||
[#rosteritem{user_server_jid = USJ,
|
|
||||||
name = Nick,
|
|
||||||
subscription = case Subscription of
|
|
||||||
"B" -> both;
|
|
||||||
"T" -> to;
|
|
||||||
"F" -> from;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
ask = case Ask of
|
|
||||||
"S" -> subscribe;
|
|
||||||
"U" -> unsubscribe;
|
|
||||||
"B" -> both;
|
|
||||||
"O" -> out;
|
|
||||||
"I" -> in;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
askmessage = AskMessage}
|
|
||||||
| [#rostergroup{user_server_jid = USJ,
|
|
||||||
grp = Group}
|
|
||||||
|| [Group] <- SELECT(["grp"], "rostergroups", [{"username", Username},
|
|
||||||
{"jid", JID}])]]
|
|
||||||
end}]).
|
|
||||||
|
|
||||||
|
@ -31,3 +31,26 @@
|
|||||||
|
|
||||||
-record(roster_version, {us,
|
-record(roster_version, {us,
|
||||||
version}).
|
version}).
|
||||||
|
|
||||||
|
%% @type rosteritem() = {roster, USJ, US, Contact_JID, Name, Subscription, Ask, Groups, Askmessage, Xs}
|
||||||
|
%% USJ = {LUser, LServer, Prepd_Contact_JID}
|
||||||
|
%% LUser = binary()
|
||||||
|
%% LServer = binary()
|
||||||
|
%% Prepd_Contact_JID = jlib:shortjid()
|
||||||
|
%% US = {LUser, LServer}
|
||||||
|
%% Contact_JID = jlib:shortjid()
|
||||||
|
%% Name = binary()
|
||||||
|
%% Subscription = none | to | from | both
|
||||||
|
%% Ask = none | out | in | both
|
||||||
|
%% Groups = [binary()]
|
||||||
|
%% Askmessage = binary()
|
||||||
|
%% Xs = [exmpp_xml:xmlel()]
|
||||||
|
|
||||||
|
%% TODO: keep a non-prepped jid like in mnesia mod_roster?
|
||||||
|
-record(rosteritem, {user_host_jid,
|
||||||
|
name = <<"">>,
|
||||||
|
subscription = none,
|
||||||
|
ask = none,
|
||||||
|
askmessage = <<"">>}).
|
||||||
|
-record(rostergroup, {user_host_jid,
|
||||||
|
grp}).
|
||||||
|
@ -24,6 +24,32 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%%% Database schema (version / storage / table)
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / mnesia / vcard
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% vcard = xmlelement()
|
||||||
|
%%%
|
||||||
|
%%% 2.1.x / odbc / vcard
|
||||||
|
%%% username = varchar250
|
||||||
|
%%% vcard = text
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / mnesia / vcard
|
||||||
|
%%% us = {Username::string(), Host::string()}
|
||||||
|
%%% vcard = xmlel()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-prealpha / odbc / vcard
|
||||||
|
%%% Same as 2.1.x
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / mnesia / vcard
|
||||||
|
%%% user_host = {Username::string(), Host::string()}
|
||||||
|
%%% vcard = xmlel()
|
||||||
|
%%%
|
||||||
|
%%% 3.0.0-alpha / odbc / vcard
|
||||||
|
%%% user = varchar150
|
||||||
|
%%% host = varchar150
|
||||||
|
%%% vcard = text
|
||||||
|
|
||||||
-module(mod_vcard).
|
-module(mod_vcard).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -48,8 +74,8 @@
|
|||||||
|
|
||||||
-define(JUD_MATCHES, 30).
|
-define(JUD_MATCHES, 30).
|
||||||
|
|
||||||
-record(vcard_search, {user_server,
|
-record(vcard_search, {user_host,
|
||||||
user, luser,
|
username, lusername,
|
||||||
fn, lfn,
|
fn, lfn,
|
||||||
family, lfamily,
|
family, lfamily,
|
||||||
given, lgiven,
|
given, lgiven,
|
||||||
@ -62,7 +88,7 @@
|
|||||||
orgname, lorgname,
|
orgname, lorgname,
|
||||||
orgunit, lorgunit
|
orgunit, lorgunit
|
||||||
}).
|
}).
|
||||||
-record(vcard, {user_server, vcard}).
|
-record(vcard, {user_host, vcard}).
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_mod_vcard).
|
-define(PROCNAME, ejabberd_mod_vcard).
|
||||||
|
|
||||||
@ -73,15 +99,14 @@ start(Host, Opts) ->
|
|||||||
[{disc_only_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, vcard)},
|
{attributes, record_info(fields, vcard)},
|
||||||
{types, [{user_server, {text, text}}]}]),
|
{types, [{user_host, {text, text}}]}]),
|
||||||
gen_storage:create_table(Backend, HostB, vcard_search,
|
gen_storage:create_table(Backend, HostB, vcard_search,
|
||||||
[{disc_copies, [node()]},
|
[{disc_copies, [node()]},
|
||||||
{odbc_host, Host},
|
{odbc_host, Host},
|
||||||
{attributes, record_info(fields, vcard_search)},
|
{attributes, record_info(fields, vcard_search)},
|
||||||
{types, [{user_server, {text, text}},
|
{types, [{user_host, {text, text}}]}]),
|
||||||
{user, {text, text}}]}]),
|
update_tables(Host, Backend),
|
||||||
update_tables(Host),
|
gen_storage:add_table_index(Host, vcard_search, lusername),
|
||||||
gen_storage:add_table_index(Host, vcard_search, luser),
|
|
||||||
gen_storage:add_table_index(Host, vcard_search, lfn),
|
gen_storage:add_table_index(Host, vcard_search, lfn),
|
||||||
gen_storage:add_table_index(Host, vcard_search, lfamily),
|
gen_storage:add_table_index(Host, vcard_search, lfamily),
|
||||||
gen_storage:add_table_index(Host, vcard_search, lgiven),
|
gen_storage:add_table_index(Host, vcard_search, lgiven),
|
||||||
@ -195,24 +220,7 @@ process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) ->
|
|||||||
process_sm_iq(_From, To, #iq{type = get} = IQ_Rec) ->
|
process_sm_iq(_From, To, #iq{type = get} = IQ_Rec) ->
|
||||||
LUser = exmpp_jid:prep_node_as_list(To),
|
LUser = exmpp_jid:prep_node_as_list(To),
|
||||||
LServer = exmpp_jid:prep_domain_as_list(To),
|
LServer = exmpp_jid:prep_domain_as_list(To),
|
||||||
US = {LUser, LServer},
|
case get_vcard(LUser, LServer) of
|
||||||
F = fun() ->
|
|
||||||
gen_storage:read(LServer, {vcard, US})
|
|
||||||
end,
|
|
||||||
Els = case gen_storage:transaction(LServer, vcard, F) of
|
|
||||||
{atomic, [#vcard{vcard = SVCARD} | _]} ->
|
|
||||||
case xml_stream:parse_element(SVCARD) of
|
|
||||||
{error, _Reason} ->
|
|
||||||
novcard;
|
|
||||||
VCARD ->
|
|
||||||
{vcard, VCARD}
|
|
||||||
end;
|
|
||||||
{atomic, []} ->
|
|
||||||
novcard;
|
|
||||||
{aborted, _Reason} ->
|
|
||||||
novcard
|
|
||||||
end,
|
|
||||||
case Els of
|
|
||||||
{vcard, VCard} ->
|
{vcard, VCard} ->
|
||||||
exmpp_iq:result(IQ_Rec, VCard);
|
exmpp_iq:result(IQ_Rec, VCard);
|
||||||
novcard ->
|
novcard ->
|
||||||
@ -229,6 +237,22 @@ process_sm_iq(From, _To, #iq{type = set, payload = Request} = IQ_Rec) ->
|
|||||||
exmpp_iq:error(IQ_Rec, 'not-allowed')
|
exmpp_iq:error(IQ_Rec, 'not-allowed')
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @spec (User::string(), Host::string()) -> {vcard, xmlel()} | novcard
|
||||||
|
get_vcard(User, Host) ->
|
||||||
|
US = {User, Host},
|
||||||
|
case gen_storage:dirty_read(Host, {vcard, US}) of
|
||||||
|
%% The vcard is stored as xmlel() in Mnesia:
|
||||||
|
[#vcard{vcard = VCARD}] when is_tuple(VCARD) ->
|
||||||
|
{vcard, VCARD};
|
||||||
|
%% The vcard is stored as a text string in ODBC:
|
||||||
|
[#vcard{vcard = SVCARD}] when is_list(SVCARD) ->
|
||||||
|
[VCARD] = exmpp_xml:parse_document(SVCARD, [names_as_atom]),
|
||||||
|
{vcard, VCARD};
|
||||||
|
[] ->
|
||||||
|
novcard
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
set_vcard(User, LServer, VCARD) ->
|
set_vcard(User, LServer, VCARD) ->
|
||||||
FN = exmpp_xml:get_path(VCARD,
|
FN = exmpp_xml:get_path(VCARD,
|
||||||
[{element, 'FN'}, cdata_as_list]),
|
[{element, 'FN'}, cdata_as_list]),
|
||||||
@ -277,12 +301,16 @@ set_vcard(User, LServer, VCARD) ->
|
|||||||
|
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
|
|
||||||
|
VcardToStore = case gen_storage:table_info(LServer, vcard, backend) of
|
||||||
|
mnesia -> VCARD;
|
||||||
|
odbc -> lists:flatten(exmpp_xml:document_to_list(VCARD))
|
||||||
|
end,
|
||||||
|
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
gen_storage:write(LServer, #vcard{user_server = US, vcard = VCARD}),
|
gen_storage:write(LServer, #vcard{user_host = US, vcard = VcardToStore}),
|
||||||
gen_storage:write(
|
gen_storage:write(LServer,
|
||||||
#vcard_search{user_server=US,
|
#vcard_search{user_host=US,
|
||||||
user = {User, LServer},
|
username = User, lusername = LUser,
|
||||||
luser = LUser,
|
|
||||||
fn = FN, lfn = LFN,
|
fn = FN, lfn = LFN,
|
||||||
family = Family, lfamily = LFamily,
|
family = Family, lfamily = LFamily,
|
||||||
given = Given, lgiven = LGiven,
|
given = Given, lgiven = LGiven,
|
||||||
@ -501,7 +529,7 @@ search_result(Lang, JID, ServerHost, Data) ->
|
|||||||
[#xmlcdata{cdata = Val}]}]}).
|
[#xmlcdata{cdata = Val}]}]}).
|
||||||
|
|
||||||
record_to_item(R) ->
|
record_to_item(R) ->
|
||||||
{User, Server} = R#vcard_search.user,
|
{User, Server} = R#vcard_search.user_host,
|
||||||
#xmlel{ns = ?NS_DATA_FORMS, name = 'item', children =
|
#xmlel{ns = ?NS_DATA_FORMS, name = 'item', children =
|
||||||
[
|
[
|
||||||
?FIELD(<<"jid">>, list_to_binary(User ++ "@" ++ Server)),
|
?FIELD(<<"jid">>, list_to_binary(User ++ "@" ++ Server)),
|
||||||
@ -568,10 +596,10 @@ filter_fields([{SVar, [Val]} | Ds], Rules, LServer)
|
|||||||
case gen_mod:get_module_opt(LServer, ?MODULE,
|
case gen_mod:get_module_opt(LServer, ?MODULE,
|
||||||
search_all_hosts, true) of
|
search_all_hosts, true) of
|
||||||
true ->
|
true ->
|
||||||
{like, luser, make_val(LVal)};
|
{like, lusername, make_val(LVal)};
|
||||||
false ->
|
false ->
|
||||||
Host = find_my_host(LServer),
|
Host = find_my_host(LServer),
|
||||||
{like, user_server, {make_val(LVal), Host}}
|
{like, user_host, {make_val(LVal), Host}}
|
||||||
end;
|
end;
|
||||||
"fn" -> {like, lfn, make_val(LVal)};
|
"fn" -> {like, lfn, make_val(LVal)};
|
||||||
"last" -> {like, lfamily, make_val(LVal)};
|
"last" -> {like, lfamily, make_val(LVal)};
|
||||||
@ -628,7 +656,7 @@ parts_to_string(Parts) ->
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
set_vcard_t(R, _) ->
|
set_vcard_t(R, _) ->
|
||||||
US = R#vcard.user_server,
|
US = R#vcard.user_host,
|
||||||
User = US,
|
User = US,
|
||||||
VCARD = R#vcard.vcard,
|
VCARD = R#vcard.vcard,
|
||||||
|
|
||||||
@ -669,8 +697,8 @@ set_vcard_t(R, _) ->
|
|||||||
LOrgName = exmpp_stringprep:to_lower(OrgName),
|
LOrgName = exmpp_stringprep:to_lower(OrgName),
|
||||||
LOrgUnit = exmpp_stringprep:to_lower(OrgUnit),
|
LOrgUnit = exmpp_stringprep:to_lower(OrgUnit),
|
||||||
mnesia:write(
|
mnesia:write(
|
||||||
#vcard_search{user_server= US,
|
#vcard_search{user_host= US,
|
||||||
user = User, luser = LUser,
|
username = User, lusername = LUser,
|
||||||
fn = FN, lfn = LFN,
|
fn = FN, lfn = LFN,
|
||||||
family = Family, lfamily = LFamily,
|
family = Family, lfamily = LFamily,
|
||||||
given = Given, lgiven = LGiven,
|
given = Given, lgiven = LGiven,
|
||||||
@ -707,83 +735,22 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
|||||||
gen_storage:transaction(LServer, vcard, F).
|
gen_storage:transaction(LServer, vcard, F).
|
||||||
|
|
||||||
|
|
||||||
update_tables(Host) ->
|
%%%
|
||||||
update_vcard_table(),
|
%%% Update tables
|
||||||
update_vcard_search_table(),
|
%%%
|
||||||
update_vcard_storage(Host).
|
|
||||||
|
|
||||||
update_vcard_table() ->
|
update_tables(Host, mnesia) ->
|
||||||
Fields = record_info(fields, vcard),
|
gen_storage_migration:migrate_mnesia(
|
||||||
case mnesia:table_info(vcard, attributes) of
|
Host, vcard,
|
||||||
Fields ->
|
[{vcard, [us, vcard],
|
||||||
convert_to_exmpp();
|
fun({vcard, US, Vcard}) ->
|
||||||
[user, vcard] ->
|
#vcard{user_host = US,
|
||||||
?INFO_MSG("Converting vcard table from "
|
vcard = convert_vcard_element(Vcard)}
|
||||||
"{user, vcard} format", []),
|
end}]),
|
||||||
Host = ?MYNAME,
|
gen_storage_migration:migrate_mnesia(
|
||||||
{atomic, ok} = mnesia:create_table(
|
Host, vcard_search,
|
||||||
mod_vcard_tmp_table,
|
[{vcard_search, [us,
|
||||||
[{disc_only_copies, [node()]},
|
username, lusername,
|
||||||
{type, bag},
|
|
||||||
{local_content, true},
|
|
||||||
{record_name, vcard},
|
|
||||||
{attributes, record_info(fields, vcard)}]),
|
|
||||||
mnesia:transform_table(vcard, ignore, Fields),
|
|
||||||
F1 = fun() ->
|
|
||||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(#vcard{user_server = U} = R, _) ->
|
|
||||||
mnesia:dirty_write(
|
|
||||||
mod_vcard_tmp_table,
|
|
||||||
R#vcard{user_server = {U, Host}})
|
|
||||||
end, ok, vcard)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F1),
|
|
||||||
mnesia:clear_table(vcard),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(vcard),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_vcard_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_vcard_tmp_table);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating vcard table", []),
|
|
||||||
mnesia:transform_table(vcard, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
convert_to_exmpp() ->
|
|
||||||
Fun = fun() ->
|
|
||||||
case mnesia:first(vcard) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
none;
|
|
||||||
Key ->
|
|
||||||
case mnesia:read({vcard, Key}) of
|
|
||||||
[#vcard{vcard = #xmlel{}}] ->
|
|
||||||
none;
|
|
||||||
[#vcard{vcard = #xmlelement{}}] ->
|
|
||||||
mnesia:foldl(fun convert_to_exmpp2/2,
|
|
||||||
done, vcard, write)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(Fun).
|
|
||||||
|
|
||||||
convert_to_exmpp2(#vcard{vcard = ElOld} = VCard, Acc) ->
|
|
||||||
El0 = exmpp_xml:xmlelement_to_xmlel(ElOld, [?NS_VCARD], []),
|
|
||||||
El = exmpp_xml:remove_whitespaces_deeply(El0),
|
|
||||||
mnesia:write(VCard#vcard{vcard = El}),
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
update_vcard_search_table() ->
|
|
||||||
Fields = record_info(fields, vcard_search),
|
|
||||||
case mnesia:table_info(vcard_search, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ok;
|
|
||||||
[user, luser,
|
|
||||||
fn, lfn,
|
fn, lfn,
|
||||||
family, lfamily,
|
family, lfamily,
|
||||||
given, lgiven,
|
given, lgiven,
|
||||||
@ -794,22 +761,34 @@ update_vcard_search_table() ->
|
|||||||
locality, llocality,
|
locality, llocality,
|
||||||
email, lemail,
|
email, lemail,
|
||||||
orgname, lorgname,
|
orgname, lorgname,
|
||||||
orgunit, lorgunit] ->
|
orgunit, lorgunit],
|
||||||
?INFO_MSG("Converting vcard_search table from "
|
fun(Record) ->
|
||||||
"{user, luser, fn, lfn, family, lfamily, given, lgiven, middle, lmiddle, nickname, lnickname, bday, lbday, ctry, lctry, locality, llocality, email, lemail, orgname, lorgname, orgunit, lorgunit} format", []),
|
Record
|
||||||
Host = ?MYNAME,
|
end}]);
|
||||||
{atomic, ok} = mnesia:create_table(
|
|
||||||
mod_vcard_tmp_table,
|
update_tables(Host, odbc) ->
|
||||||
[{disc_only_copies, [node()]},
|
gen_storage_migration:migrate_odbc(
|
||||||
{type, bag},
|
Host, [vcard],
|
||||||
{local_content, true},
|
[{"vcard", ["username", "vcard"],
|
||||||
{record_name, vcard_search},
|
fun(_, Username, Vcard) ->
|
||||||
{attributes, record_info(fields, vcard_search)}]),
|
[#vcard{user_host = {Username, Host},
|
||||||
F1 = fun() ->
|
vcard = Vcard}]
|
||||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
end}]),
|
||||||
mnesia:foldl(
|
gen_storage_migration:migrate_odbc(
|
||||||
fun({vcard_search,
|
Host, [vcard_search],
|
||||||
User, LUser,
|
[{"vcard_search", ["username", "lusername",
|
||||||
|
"fn", "lfn",
|
||||||
|
"family", "lfamily",
|
||||||
|
"given", "lgiven",
|
||||||
|
"middle", "lmiddle",
|
||||||
|
"nickname", "lnickname",
|
||||||
|
"bday", "lbday",
|
||||||
|
"ctry", "lctry",
|
||||||
|
"locality", "llocality",
|
||||||
|
"email", "lemail",
|
||||||
|
"orgname", "lorgname",
|
||||||
|
"orgunit", "lorgunit"],
|
||||||
|
fun(_, User, LUser,
|
||||||
FN, LFN,
|
FN, LFN,
|
||||||
Family, LFamily,
|
Family, LFamily,
|
||||||
Given, LGiven,
|
Given, LGiven,
|
||||||
@ -820,14 +799,9 @@ update_vcard_search_table() ->
|
|||||||
Locality, LLocality,
|
Locality, LLocality,
|
||||||
EMail, LEMail,
|
EMail, LEMail,
|
||||||
OrgName, LOrgName,
|
OrgName, LOrgName,
|
||||||
OrgUnit, LOrgUnit
|
OrgUnit, LOrgUnit) ->
|
||||||
}, _) ->
|
[#vcard_search{user_host = {LUser, Host},
|
||||||
mnesia:dirty_write(
|
username = User, lusername = LUser,
|
||||||
mod_vcard_tmp_table,
|
|
||||||
#vcard_search{
|
|
||||||
user_server= {LUser, Host},
|
|
||||||
user = {User, Host},
|
|
||||||
luser = LUser,
|
|
||||||
fn = FN, lfn = LFN,
|
fn = FN, lfn = LFN,
|
||||||
family = Family, lfamily = LFamily,
|
family = Family, lfamily = LFamily,
|
||||||
given = Given, lgiven = LGiven,
|
given = Given, lgiven = LGiven,
|
||||||
@ -838,43 +812,12 @@ update_vcard_search_table() ->
|
|||||||
locality = Locality, llocality = LLocality,
|
locality = Locality, llocality = LLocality,
|
||||||
email = EMail, lemail = LEMail,
|
email = EMail, lemail = LEMail,
|
||||||
orgname = OrgName, lorgname = LOrgName,
|
orgname = OrgName, lorgname = LOrgName,
|
||||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
orgunit = OrgUnit, lorgunit = LOrgUnit}]
|
||||||
})
|
end}]).
|
||||||
end, ok, vcard_search)
|
|
||||||
end,
|
convert_vcard_element(Xmlelement) ->
|
||||||
mnesia:transaction(F1),
|
Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement, [?NS_VCARD], []),
|
||||||
lists:foreach(fun(I) ->
|
exmpp_xml:remove_whitespaces_deeply(Xmlel).
|
||||||
mnesia:del_table_index(
|
|
||||||
vcard_search,
|
|
||||||
element(I, {vcard_search,
|
|
||||||
user, luser,
|
|
||||||
fn, lfn,
|
|
||||||
family, lfamily,
|
|
||||||
given, lgiven,
|
|
||||||
middle, lmiddle,
|
|
||||||
nickname, lnickname,
|
|
||||||
bday, lbday,
|
|
||||||
ctry, lctry,
|
|
||||||
locality, llocality,
|
|
||||||
email, lemail,
|
|
||||||
orgname, lorgname,
|
|
||||||
orgunit, lorgunit}))
|
|
||||||
end, mnesia:table_info(vcard_search, index)),
|
|
||||||
mnesia:clear_table(vcard_search),
|
|
||||||
mnesia:transform_table(vcard_search, ignore, Fields),
|
|
||||||
F2 = fun() ->
|
|
||||||
mnesia:write_lock_table(vcard_search),
|
|
||||||
mnesia:foldl(
|
|
||||||
fun(R, _) ->
|
|
||||||
mnesia:dirty_write(R)
|
|
||||||
end, ok, mod_vcard_tmp_table)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F2),
|
|
||||||
mnesia:delete_table(mod_vcard_tmp_table);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating vcard_search table", []),
|
|
||||||
mnesia:transform_table(vcard_search, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% WebAdmin
|
%%% WebAdmin
|
||||||
@ -898,16 +841,14 @@ webadmin_page(_, Host,
|
|||||||
webadmin_page(Acc, _, _) -> Acc.
|
webadmin_page(Acc, _, _) -> Acc.
|
||||||
|
|
||||||
user_vcard(User, Server, Query, Lang) ->
|
user_vcard(User, Server, Query, Lang) ->
|
||||||
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
US = {LUser, LServer} = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
||||||
Res = user_queue_parse_query(US, Query),
|
Res = user_queue_parse_query(US, Query),
|
||||||
VcardString = case mnesia:dirty_read({vcard, US}) of
|
VcardString = case get_vcard(LUser, LServer) of
|
||||||
[Vcard] ->
|
{vcard, Xml} ->
|
||||||
Xml = Vcard#vcard.vcard,
|
|
||||||
XmlString = lists:flatten(exmpp_xml:document_to_list(Xml)),
|
XmlString = lists:flatten(exmpp_xml:document_to_list(Xml)),
|
||||||
Reduced = re:replace(XmlString, "<BINVAL>[^<]*", "<BINVAL>...", [global, {return, list}]),
|
Reduced = re:replace(XmlString, "<BINVAL>[^<]*", "<BINVAL>...", [global, {return, list}]),
|
||||||
try_indent(Reduced);
|
try_indent(Reduced);
|
||||||
[] -> "no vcard";
|
novcard -> "no vcard"
|
||||||
_ -> "error getting vcard"
|
|
||||||
end,
|
end,
|
||||||
[?XE('h1', [?CT("vCard")]),
|
[?XE('h1', [?CT("vCard")]),
|
||||||
?XE('h2', [?AC("../", us_to_list(US))])
|
?XE('h2', [?AC("../", us_to_list(US))])
|
||||||
@ -937,18 +878,16 @@ try_indent(String) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_user_photo(User, Host) ->
|
get_user_photo(User, Host) ->
|
||||||
US = {User, Host},
|
case get_vcard(User, Host) of
|
||||||
case mnesia:dirty_read({vcard, US}) of
|
{vcard, VCard} ->
|
||||||
[VCard] ->
|
case exmpp_xml:get_path(VCard, [{element, "PHOTO"}, {element, "BINVAL"}, cdata_as_list]) of
|
||||||
case exmpp_xml:get_path(VCard#vcard.vcard, [{element, "PHOTO"}, {element, "BINVAL"}, cdata_as_list]) of
|
|
||||||
[] -> "no avatar";
|
[] -> "no avatar";
|
||||||
BinVal -> case catch jlib:decode_base64(BinVal) of
|
BinVal -> case catch jlib:decode_base64(BinVal) of
|
||||||
{'EXIT', _} -> "error";
|
{'EXIT', _} -> "error";
|
||||||
Decoded -> Decoded
|
Decoded -> Decoded
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
[] -> "no vcard";
|
novcard -> "no vcard"
|
||||||
_ -> "error getting vcard"
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
user_queue_parse_query(US, Query) ->
|
user_queue_parse_query(US, Query) ->
|
||||||
@ -971,11 +910,11 @@ us_to_list({User, Server}) ->
|
|||||||
exmpp_jid:to_list(User, Server).
|
exmpp_jid:to_list(User, Server).
|
||||||
|
|
||||||
webadmin_user(Acc, User, Server, Lang) ->
|
webadmin_user(Acc, User, Server, Lang) ->
|
||||||
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
VcardSize = case mnesia:dirty_read({vcard, US}) of
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
[Vcard] -> get_vcard_size(Vcard);
|
VcardSize = case get_vcard(LUser, LServer) of
|
||||||
[] -> 0;
|
{vcard, Vcard} -> get_vcard_size(Vcard);
|
||||||
_ -> -1
|
novcard -> 0
|
||||||
end,
|
end,
|
||||||
FVcardSize = case VcardSize > 0 of
|
FVcardSize = case VcardSize > 0 of
|
||||||
true -> [?AC("vcard/", integer_to_list(VcardSize))];
|
true -> [?AC("vcard/", integer_to_list(VcardSize))];
|
||||||
@ -988,7 +927,7 @@ webadmin_user(Acc, User, Server, Lang) ->
|
|||||||
Acc ++ [?XCT('h3', "vCard size:")] ++ FVcardSize ++ [?CT(" characters. ")] ++ RemoveEl.
|
Acc ++ [?XCT('h3', "vCard size:")] ++ FVcardSize ++ [?CT(" characters. ")] ++ RemoveEl.
|
||||||
|
|
||||||
get_vcard_size(Vcard) ->
|
get_vcard_size(Vcard) ->
|
||||||
String = lists:flatten(exmpp_xml:document_to_list(Vcard#vcard.vcard)),
|
String = lists:flatten(exmpp_xml:document_to_list(Vcard)),
|
||||||
length(String).
|
length(String).
|
||||||
|
|
||||||
webadmin_user_parse_query(_, "removevcard", User, Server, _Query) ->
|
webadmin_user_parse_query(_, "removevcard", User, Server, _Query) ->
|
||||||
@ -1002,86 +941,3 @@ webadmin_user_parse_query(_, "removevcard", 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
update_vcard_storage(Host) ->
|
|
||||||
%% TODO: vcard_search
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
|
||||||
Host, vcard,
|
|
||||||
[{vcard, [user, vcard],
|
|
||||||
fun({vcard, U, Vcard}) ->
|
|
||||||
#vcard{user_server = {U, Host},
|
|
||||||
vcard = lists:flatten(xml:element_to_string(Vcard))}
|
|
||||||
end},
|
|
||||||
{vcard, [us, vcard],
|
|
||||||
fun({vcard, US, Vcard}) ->
|
|
||||||
#vcard{user_server = US,
|
|
||||||
vcard = lists:flatten(xml:element_to_string(Vcard))}
|
|
||||||
end}]),
|
|
||||||
gen_storage_migration:migrate_mnesia(
|
|
||||||
Host, vcard_search,
|
|
||||||
[{vcard_search, [us,
|
|
||||||
user, luser,
|
|
||||||
fn, lfn,
|
|
||||||
family, lfamily,
|
|
||||||
given, lgiven,
|
|
||||||
middle, lmiddle,
|
|
||||||
nickname, lnickname,
|
|
||||||
bday, lbday,
|
|
||||||
ctry, lctry,
|
|
||||||
locality, llocality,
|
|
||||||
email, lemail,
|
|
||||||
orgname, lorgname,
|
|
||||||
orgunit, lorgunit],
|
|
||||||
fun(Record) ->
|
|
||||||
Record
|
|
||||||
end}]),
|
|
||||||
gen_storage_migration:migrate_odbc(
|
|
||||||
Host, [vcard],
|
|
||||||
[{"vcard", ["username", "vcard"],
|
|
||||||
fun(_, Username, Vcard) ->
|
|
||||||
[#vcard{user_server = {Username, Host},
|
|
||||||
vcard = Vcard}]
|
|
||||||
end}]),
|
|
||||||
gen_storage_migration:migrate_odbc(
|
|
||||||
Host, [vcard_search],
|
|
||||||
[{"vcard_search", ["username", "lusername",
|
|
||||||
"fn", "lfn",
|
|
||||||
"family", "lfamily",
|
|
||||||
"given", "lgiven",
|
|
||||||
"middle", "lmiddle",
|
|
||||||
"nickname", "lnickname",
|
|
||||||
"bday", "lbday",
|
|
||||||
"ctry", "lctry",
|
|
||||||
"locality", "llocality",
|
|
||||||
"email", "lemail",
|
|
||||||
"orgname", "lorgname",
|
|
||||||
"orgunit", "lorgunit"],
|
|
||||||
fun(_, User, LUser,
|
|
||||||
FN, LFN,
|
|
||||||
Family, LFamily,
|
|
||||||
Given, LGiven,
|
|
||||||
Middle, LMiddle,
|
|
||||||
Nickname, LNickname,
|
|
||||||
BDay, LBDay,
|
|
||||||
CTRY, LCTRY,
|
|
||||||
Locality, LLocality,
|
|
||||||
EMail, LEMail,
|
|
||||||
OrgName, LOrgName,
|
|
||||||
OrgUnit, LOrgUnit) ->
|
|
||||||
[#vcard_search{user_server = {LUser, Host},
|
|
||||||
user = {User, Host},
|
|
||||||
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}]).
|
|
||||||
|
Loading…
Reference in New Issue
Block a user