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
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>, Stephan Maka
|
||||
%%% 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
|
||||
%%% 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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -48,32 +73,59 @@
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(passwd, {us, password}).
|
||||
-record(passwd, {user_host, password}).
|
||||
-record(reg_users_counter, {vhost, count}).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec (Host) -> ok
|
||||
%% Host = string()
|
||||
|
||||
start(Host) ->
|
||||
Backend =
|
||||
case ejabberd_config:get_local_option({auth_storage, Host}) of
|
||||
undefined -> mnesia;
|
||||
B -> B
|
||||
end,
|
||||
gen_storage:create_table(Backend, Host, passwd,
|
||||
HostB = list_to_binary(Host),
|
||||
gen_storage:create_table(Backend, HostB, passwd,
|
||||
[{odbc_host, Host},
|
||||
{disc_copies, [node()]},
|
||||
{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.
|
||||
|
||||
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() ->
|
||||
false.
|
||||
|
||||
%% @spec (User, Server, Password) -> bool()
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Password = string()
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
@ -82,15 +134,22 @@ check_password(User, Server, Password) ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, StreamID, Digest) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
%% @spec (User, Server, Password, Digest, DigestGen) -> bool()
|
||||
%% User = string()
|
||||
%% 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},
|
||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||
[#passwd{password = Passwd}] ->
|
||||
DigRes = if
|
||||
Digest /= "" ->
|
||||
Digest == sha:sha(StreamID ++ Passwd);
|
||||
Digest == DigestGen(Passwd);
|
||||
true ->
|
||||
false
|
||||
end,
|
||||
@ -103,201 +162,316 @@ check_password(User, Server, Password, StreamID, Digest) ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
||||
%% ok | {error, invalid_jid}
|
||||
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Password = string()
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
LUser = (catch exmpp_stringprep:nodeprep(User)),
|
||||
LServer = (catch exmpp_stringprep:nameprep(Server)),
|
||||
case {LUser, LServer} of
|
||||
{{stringprep, _, invalid_string, _}, _} ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
{_, {stringprep, _, invalid_string, _}} ->
|
||||
{error, invalid_jid};
|
||||
US ->
|
||||
%% TODO: why is this a transaction?
|
||||
F = fun() ->
|
||||
gen_storage:write(LServer,
|
||||
#passwd{us = US,
|
||||
#passwd{user_host = US,
|
||||
password = Password})
|
||||
end,
|
||||
{atomic, ok} = gen_storage:transaction(LServer, passwd, F),
|
||||
ok
|
||||
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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if
|
||||
(LUser == error) or (LServer == error) ->
|
||||
LUser = (catch exmpp_stringprep:nodeprep(User)),
|
||||
LServer = (catch exmpp_stringprep:nameprep(Server)),
|
||||
case {LUser, LServer} of
|
||||
{{stringprep, _, invalid_string, _}, _} ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
{_, {stringprep, _, invalid_string, _}} ->
|
||||
{error, invalid_jid};
|
||||
US ->
|
||||
F = fun() ->
|
||||
case gen_storage:read(LServer, {passwd, US}) of
|
||||
[] ->
|
||||
gen_storage:write(LServer,
|
||||
#passwd{us = US,
|
||||
#passwd{user_host = US,
|
||||
password = Password}),
|
||||
mnesia:dirty_update_counter(
|
||||
reg_users_counter,
|
||||
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), 1),
|
||||
ok;
|
||||
[_E] ->
|
||||
exists
|
||||
end
|
||||
end,
|
||||
%% TODO: transaction retval?
|
||||
%% TODO: transaction return value?
|
||||
gen_storage:transaction(LServer, passwd, F)
|
||||
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() ->
|
||||
%% TODO:
|
||||
exit(not_implemented).
|
||||
|
||||
%% @spec (Server) -> [{LUser, LServer}]
|
||||
%% Server = string()
|
||||
%% LUser = string()
|
||||
%% LServer = string()
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
lists:map(fun(#passwd{us = US}) ->
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
lists:map(fun(#passwd{user_host = US}) ->
|
||||
US
|
||||
end,
|
||||
gen_storage:dirty_select(LServer, passwd,
|
||||
[{'=', us, {'_', LServer}}])).
|
||||
[{'=', user_host, {'_', LServer}}])).
|
||||
|
||||
get_vh_registered_users(Server, [{from, Start}, {to, End}])
|
||||
when is_integer(Start) and is_integer(End) ->
|
||||
%% @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}])
|
||||
when is_integer(Start) and is_integer(End) ->
|
||||
get_vh_registered_users(Server, [{limit, End-Start+1}, {offset, Start}]);
|
||||
|
||||
get_vh_registered_users(Server, [{limit, Limit}, {offset, Offset}])
|
||||
when is_integer(Limit) and is_integer(Offset) ->
|
||||
get_vh_registered_users(Server, [{limit, Limit}, {offset, Offset}])
|
||||
when is_integer(Limit) and is_integer(Offset) ->
|
||||
case get_vh_registered_users(Server) of
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}])
|
||||
when is_list(Prefix) ->
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}])
|
||||
when is_list(Prefix) ->
|
||||
Set = [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
|
||||
lists:keysort(1, Set);
|
||||
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
|
||||
when is_list(Prefix) and is_integer(Start) and is_integer(End) ->
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
|
||||
when is_list(Prefix) and is_integer(Start) and is_integer(End) ->
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, End-Start+1}, {offset, Start}]);
|
||||
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||
when is_list(Prefix) and is_integer(Limit) and is_integer(Offset) ->
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||
when is_list(Prefix) and is_integer(Limit) and is_integer(Offset) ->
|
||||
case [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)] of
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
|
||||
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) ->
|
||||
Set = get_vh_registered_users(Server),
|
||||
length(Set).
|
||||
LServer = exmpp_jid:prep_domain(exmpp_jid:parse(Server)),
|
||||
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) ->
|
||||
Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
|
||||
length(Set);
|
||||
|
||||
|
||||
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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
catch
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> Password | nil()
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
%% Password = string()
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, passwd, US) of
|
||||
[#passwd{password = Password}] ->
|
||||
Password;
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
catch
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
%% User = string()
|
||||
%% Server = string()
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_] ->
|
||||
true;
|
||||
Other ->
|
||||
{error, Other}
|
||||
end
|
||||
catch
|
||||
_ ->
|
||||
false
|
||||
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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
gen_storage:delete(LServer, {passwd, US})
|
||||
end,
|
||||
gen_storage:transaction(LServer, passwd, F),
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]).
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
gen_storage:delete(LServer, {passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter,
|
||||
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1)
|
||||
end,
|
||||
gen_storage:transaction(LServer, passwd, F),
|
||||
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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
case gen_storage:read(LServer, {passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
gen_storage:delete(LServer, {passwd, US}),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServer, passwd, F) of
|
||||
{atomic, ok} ->
|
||||
ejabberd_hooks:run(remove_user, LServer, [User, Server]),
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
F = fun() ->
|
||||
case gen_storage:read(LServer, {passwd, US}) of
|
||||
[#passwd{password = Password}] ->
|
||||
gen_storage:delete(LServer, {passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter,
|
||||
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1),
|
||||
ok;
|
||||
[_] ->
|
||||
not_allowed;
|
||||
_ ->
|
||||
not_exists
|
||||
end
|
||||
end,
|
||||
case gen_storage:transaction(LServer, passwd, F) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
_ ->
|
||||
bad_request
|
||||
end
|
||||
catch
|
||||
_ ->
|
||||
bad_request
|
||||
end.
|
||||
|
||||
update_table(Host) ->
|
||||
%% @spec () -> term()
|
||||
|
||||
update_table(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, passwd,
|
||||
[{passwd, [user, password],
|
||||
fun({passwd, User, Password}) ->
|
||||
#passwd{us = {User, Host},
|
||||
[{passwd, [us, password],
|
||||
fun({passwd, {User, _Host}, Password}) ->
|
||||
#passwd{user_host = {User, Host},
|
||||
password = Password}
|
||||
end}]),
|
||||
end}]);
|
||||
update_table(Host, odbc) ->
|
||||
gen_storage_migration:migrate_odbc(
|
||||
Host, [passwd],
|
||||
[{"users", ["username", "password"],
|
||||
fun(_, User, Password) ->
|
||||
#passwd{us = {User, Host},
|
||||
#passwd{user_host = {User, Host},
|
||||
password = Password}
|
||||
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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -43,7 +74,7 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
-record(last_activity, {user_server, timestamp, status}).
|
||||
-record(last_activity, {user_host, timestamp, status}).
|
||||
|
||||
|
||||
start(Host, Opts) ->
|
||||
@ -54,9 +85,9 @@ start(Host, Opts) ->
|
||||
[{disc_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{attributes, record_info(fields, last_activity)},
|
||||
{types, [{user_server, {text, text}},
|
||||
{types, [{user_host, {text, text}},
|
||||
{timestamp, bigint}]}]),
|
||||
update_table(Host),
|
||||
update_table(Host, Backend),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
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},
|
||||
F = fun() ->
|
||||
gen_storage:write(Server,
|
||||
#last_activity{user_server = US,
|
||||
#last_activity{user_host = US,
|
||||
timestamp = TimeStamp,
|
||||
status = Status})
|
||||
end,
|
||||
@ -203,66 +234,33 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
||||
update_table(Host) ->
|
||||
Fields = record_info(fields, last_activity),
|
||||
case mnesia:table_info(last_activity, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp();
|
||||
_ ->
|
||||
update_table(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, last_activity,
|
||||
[{last_activity, [us, timestamp, status],
|
||||
fun(#last_activity{} = LA) ->
|
||||
LA
|
||||
end}]),
|
||||
fun({last_activity, {U, S}, Timestamp, Status}) ->
|
||||
U1 = case U of
|
||||
"" -> 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(
|
||||
Host, [last_activity],
|
||||
[{"last", ["username", "seconds", "state"],
|
||||
fun(_, Username, STimeStamp, Status) ->
|
||||
case catch list_to_integer(STimeStamp) of
|
||||
TimeStamp when is_integer(TimeStamp) ->
|
||||
[#last_activity{user_server = {Username, Host},
|
||||
[#last_activity{user_host = {Username, Host},
|
||||
timestamp = TimeStamp,
|
||||
status = Status}];
|
||||
_ ->
|
||||
?WARNING_MSG("Omitting last_activity migration item with timestamp=~p",
|
||||
?WARNING_MSG("Omitting last_activity migration item"
|
||||
" with timestamp=~p",
|
||||
[STimeStamp])
|
||||
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.
|
||||
end}]).
|
||||
|
@ -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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -50,8 +93,9 @@
|
||||
-include("web/ejabberd_http.hrl").
|
||||
-include("web/ejabberd_web_admin.hrl").
|
||||
|
||||
%% The packet is stored serialized as a string
|
||||
%% 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(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||
@ -72,12 +116,12 @@ start(Host, Opts) ->
|
||||
{odbc_host, Host},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, offline_msg)},
|
||||
{types, [{user_server, {text, text}},
|
||||
{types, [{user_host, {text, text}},
|
||||
{timestamp, bigint},
|
||||
{expire, bigint},
|
||||
{from, ljid},
|
||||
{to, ljid}]}]),
|
||||
update_table(),
|
||||
{from, jid},
|
||||
{to, jid}]}]),
|
||||
update_table(Host, Backend),
|
||||
ejabberd_hooks:add(offline_message_hook, HostB,
|
||||
?MODULE, store_packet, 50),
|
||||
ejabberd_hooks:add(resend_offline_messages_hook, HostB,
|
||||
@ -102,12 +146,12 @@ start(Host, Opts) ->
|
||||
|
||||
loop(AccessMaxOfflineMsgs) ->
|
||||
receive
|
||||
#offline_msg{user_server=US} = Msg ->
|
||||
#offline_msg{user_host=US} = Msg ->
|
||||
Msgs = receive_all(US, [Msg]),
|
||||
Len = length(Msgs),
|
||||
%% TODO: is lower?
|
||||
{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],
|
||||
MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs,
|
||||
User, Host),
|
||||
@ -115,8 +159,7 @@ loop(AccessMaxOfflineMsgs) ->
|
||||
%% Only count messages if needed:
|
||||
Count =
|
||||
if MaxOfflineMsgs =/= infinity ->
|
||||
Len + gen_storage:count_records(Host, offline_msg,
|
||||
[{'=', user_server, US}]);
|
||||
Len + get_queue_length(User, Host);
|
||||
true ->
|
||||
0
|
||||
end,
|
||||
@ -152,7 +195,7 @@ get_max_user_messages(AccessRule, LUser, Host) ->
|
||||
|
||||
receive_all(US, Msgs) ->
|
||||
receive
|
||||
#offline_msg{user_server=US} = Msg ->
|
||||
#offline_msg{user_host=US} = Msg ->
|
||||
receive_all(US, [Msg | Msgs])
|
||||
after 0 ->
|
||||
Msgs
|
||||
@ -208,7 +251,7 @@ store_packet(From, To, Packet) ->
|
||||
TimeStamp = make_timestamp(),
|
||||
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
|
||||
gen_mod:get_module_proc(LServer, ?PROCNAME) !
|
||||
#offline_msg{user_server = {LUser, LServer},
|
||||
#offline_msg{user_host = {LUser, LServer},
|
||||
timestamp = TimeStamp,
|
||||
expire = Expire,
|
||||
from = From,
|
||||
@ -275,12 +318,12 @@ find_x_event_chatstates([_ | Els], {A, B, _}) ->
|
||||
find_x_event_chatstates(Els, {A, B, true}).
|
||||
|
||||
find_x_expire(_, []) ->
|
||||
never;
|
||||
0;
|
||||
find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
|
||||
Val = exmpp_xml:get_attribute_as_list(El, 'seconds', ""),
|
||||
case catch list_to_integer(Val) of
|
||||
{'EXIT', _} ->
|
||||
never;
|
||||
0;
|
||||
Int when Int > 0 ->
|
||||
{MegaSecs, Secs, MicroSecs} = TimeStamp,
|
||||
S = MegaSecs * 1000000 + Secs + Int,
|
||||
@ -288,7 +331,7 @@ find_x_expire(TimeStamp, [#xmlel{ns = ?NS_MESSAGE_EXPIRE} = El | _Els]) ->
|
||||
Secs1 = S rem 1000000,
|
||||
{MegaSecs1, Secs1, MicroSecs};
|
||||
_ ->
|
||||
never
|
||||
0
|
||||
end;
|
||||
find_x_expire(TimeStamp, [_ | Els]) ->
|
||||
find_x_expire(TimeStamp, Els).
|
||||
@ -352,7 +395,7 @@ pop_offline_messages(Ls, User, Server)
|
||||
TS = make_timestamp(),
|
||||
Ls ++ lists:map(
|
||||
fun(R) ->
|
||||
Packet = R#offline_msg.packet,
|
||||
[Packet] = exmpp_xml:parse_document(R#offline_msg.packet),
|
||||
{route,
|
||||
R#offline_msg.from,
|
||||
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
|
||||
jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(
|
||||
R#offline_msg.timestamp))]
|
||||
timestamp_to_now(R#offline_msg.timestamp)))]
|
||||
)}
|
||||
end,
|
||||
lists:filter(
|
||||
fun(R) ->
|
||||
case R#offline_msg.expire of
|
||||
never ->
|
||||
0 ->
|
||||
true;
|
||||
TimeStamp ->
|
||||
TS < TimeStamp
|
||||
@ -428,156 +471,65 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, offline_msg),
|
||||
case mnesia:table_info(offline_msg, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp();
|
||||
[user, timestamp, expire, from, to, packet] ->
|
||||
?INFO_MSG("Converting offline_msg table from "
|
||||
"{user, timestamp, expire, 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, 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,
|
||||
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).
|
||||
update_table(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, offline_msg,
|
||||
[{offline_msg, [us, timestamp, expire, from, to, packet],
|
||||
%% The field name 'us' changes to 'user_host',
|
||||
%% but its position in the erlang record is the same,
|
||||
%% so we can refer to it using the new field name 'user_host'.
|
||||
fun(#offline_msg{user_host = {US_U, US_S},
|
||||
timestamp = {TsMegaSecs, TsSecs, _TsMicroSecs},
|
||||
expire = Expire,
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet} = OM) ->
|
||||
%% Convert "" to undefined in JIDs.
|
||||
US_U1 = convert_jid_to_exmpp(US_U),
|
||||
US_S1 = convert_jid_to_exmpp(US_S),
|
||||
From1 = jlib:from_old_jid(From),
|
||||
To1 = jlib:from_old_jid(To),
|
||||
Expire1 = case Expire of
|
||||
never ->
|
||||
0;
|
||||
{MegaSecs, Secs, _MicroSecs} ->
|
||||
MegaSecs * 1000000 + Secs
|
||||
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,
|
||||
to = To1,
|
||||
packet = Packet1}
|
||||
end}]);
|
||||
|
||||
convert_to_exmpp2(#offline_msg{
|
||||
user_server = {US_U, US_S},
|
||||
from = From,
|
||||
to = To,
|
||||
packet = Packet} = R, Acc) ->
|
||||
% Remove old entry.
|
||||
mnesia:delete_object(R),
|
||||
% Convert "" to undefined in JIDs.
|
||||
US_U1 = convert_jid_to_exmpp(US_U),
|
||||
US_S1 = convert_jid_to_exmpp(US_S),
|
||||
From1 = jlib:from_old_jid(From),
|
||||
To1 = jlib:from_old_jid(To),
|
||||
% Convert stanza.
|
||||
Packet1 = exmpp_xml:xmlelement_to_xmlel(Packet,
|
||||
[?DEFAULT_NS], ?PREFIXED_NS),
|
||||
% Prepare the new record.
|
||||
New_R = R#offline_msg{
|
||||
user_server = {US_U1, US_S1},
|
||||
from = From1,
|
||||
to = To1,
|
||||
packet = Packet1},
|
||||
% Write the new record.
|
||||
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(V) -> V.
|
||||
|
||||
%% Helper functions:
|
||||
make_timestamp() ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
MegaSecs * 1000000 + Secs.
|
||||
@ -587,6 +539,22 @@ timestamp_to_now(Timestamp) ->
|
||||
Secs = Timestamp rem 1000000,
|
||||
{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:
|
||||
discard_warn_sender(Msgs) ->
|
||||
lists:foreach(
|
||||
@ -616,7 +584,7 @@ user_queue(User, Server, Query, Lang) ->
|
||||
exmpp_stringprep:nodeprep(User),
|
||||
exmpp_stringprep:nameprep(Server)
|
||||
},
|
||||
{user_server, MsgsAll, Res} = try
|
||||
{US, MsgsAll, Res} = try
|
||||
{
|
||||
US0,
|
||||
lists:keysort(#offline_msg.timestamp,
|
||||
@ -631,7 +599,7 @@ user_queue(User, Server, Query, Lang) ->
|
||||
FMsgs =
|
||||
lists:map(
|
||||
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))),
|
||||
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||
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])),
|
||||
SFrom = exmpp_jid:to_list(From),
|
||||
STo = exmpp_jid:to_list(To),
|
||||
[Packet] = exmpp_xmlstream:parse_element(PacketInitialString),
|
||||
Packet1 = exmpp_stanza:set_jids(Packet, SFrom, STo),
|
||||
FPacket = exmpp_xml:node_to_list(
|
||||
exmpp_xml:indent_document(Packet1, <<" ">>),
|
||||
@ -654,7 +623,7 @@ user_queue(User, Server, Query, Lang) ->
|
||||
)
|
||||
end, Msgs),
|
||||
[?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"),
|
||||
[us_to_list(US0)]))] ++
|
||||
[us_to_list(US)]))] ++
|
||||
case Res of
|
||||
ok -> [?CT("Submitted"), ?P];
|
||||
nothing -> []
|
||||
@ -711,8 +680,8 @@ user_queue_parse_query(US, Query) ->
|
||||
us_to_list({User, Server}) ->
|
||||
exmpp_jid:to_list(User, Server).
|
||||
|
||||
get_queue_length(User, Server) ->
|
||||
length(mnesia:dirty_read({offline_msg, {User, Server}})).
|
||||
get_queue_length(User, Host) ->
|
||||
gen_storage:dirty_count_records(Host, offline_msg, [{'=', user_host, {User, Host}}]).
|
||||
|
||||
get_messages_subset(User, Host, MsgsAll) ->
|
||||
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.
|
||||
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
||||
QueueLen = length(gen_storage:dirty_read(Server, {offline_msg, US})),
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
QueueLen = get_queue_length(LUser, LServer),
|
||||
FQueueLen = [?AC("queue/", integer_to_list(QueueLen))],
|
||||
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) ->
|
||||
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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -43,9 +118,9 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
-record(privacy_list, {username_server, name}).
|
||||
-record(privacy_default_list, {username_server, name}).
|
||||
-record(privacy_list_data, {username_server, name,
|
||||
-record(privacy_list, {user_host, name}).
|
||||
-record(privacy_default_list, {user_host, name}).
|
||||
-record(privacy_list_data, {user_host, name,
|
||||
type, value, action, order,
|
||||
match_all, match_iq, match_message,
|
||||
match_presence_in, match_presence_out}).
|
||||
@ -59,21 +134,21 @@ start(Host, Opts) ->
|
||||
{odbc_host, Host},
|
||||
{type, bag},
|
||||
{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,
|
||||
[{disc_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{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,
|
||||
[{disc_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, privacy_list_data)},
|
||||
{types, [{username_server, {text, text}},
|
||||
{action, atom},
|
||||
{types, [{user_host, {text, text}},
|
||||
{type, atom},
|
||||
{value, atom},
|
||||
{value, binary},
|
||||
{action, atom},
|
||||
{order, int},
|
||||
{match_all, atom},
|
||||
{match_iq, atom},
|
||||
@ -81,7 +156,7 @@ start(Host, Opts) ->
|
||||
{match_presence_in, 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_data, name),
|
||||
ejabberd_hooks:add(privacy_iq_get, HostB,
|
||||
@ -160,10 +235,7 @@ process_lists_get(LUser, LServer, Active) ->
|
||||
[] ->
|
||||
{result, #xmlel{ns = ?NS_PRIVACY, name = 'query'}};
|
||||
_ ->
|
||||
LItems = lists:map(
|
||||
fun({N, _}) ->
|
||||
exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = list}, name, N)
|
||||
end, Lists),
|
||||
LItems = [exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = list}, name, N) || N <- Lists],
|
||||
DItems =
|
||||
case Default of
|
||||
none ->
|
||||
@ -188,13 +260,13 @@ process_list_get(_LUser, _LServer, false) ->
|
||||
process_list_get(LUser, LServer, Name) ->
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[] ->
|
||||
none;
|
||||
[#privacy_list{}] ->
|
||||
gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}])
|
||||
end
|
||||
end,
|
||||
@ -217,8 +289,8 @@ item_to_xml(Item) ->
|
||||
none ->
|
||||
Attrs1;
|
||||
Type ->
|
||||
[?XMLATTR('type', type_to_binary(Item#privacy_list_data.type)),
|
||||
?XMLATTR('value', value_to_binary(Type, Item#privacy_list_data.value)) |
|
||||
[?XMLATTR('type', type_to_binary(Type)),
|
||||
?XMLATTR('value', Item#privacy_list_data.value) |
|
||||
Attrs1]
|
||||
end,
|
||||
SubEls = case Item#privacy_list_data.match_all of
|
||||
@ -270,21 +342,6 @@ type_to_binary(Type) ->
|
||||
subscription -> <<"subscription">>
|
||||
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) ->
|
||||
@ -331,13 +388,13 @@ process_default_set(LUser, LServer, false) ->
|
||||
process_default_set(LUser, LServer, Name) ->
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[] ->
|
||||
{error, 'item-not-found'};
|
||||
[#privacy_list{}] ->
|
||||
gen_storage:write(LServer,
|
||||
#privacy_default_list{username_server = {LUser, LServer},
|
||||
#privacy_default_list{user_host = {LUser, LServer},
|
||||
name = Name}),
|
||||
{result, []}
|
||||
end
|
||||
@ -358,11 +415,11 @@ process_active_set(_LUser, _LServer, false) ->
|
||||
process_active_set(LUser, LServer, Name) ->
|
||||
F = fun() ->
|
||||
case gen_storage:select(LServer, privacy_list,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]) of
|
||||
[#privacy_list{}] ->
|
||||
List = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
{result, [], #userlist{name = Name,
|
||||
list = list_data_to_items(List)}};
|
||||
@ -394,10 +451,10 @@ process_list_set(LUser, LServer, Name, Els) ->
|
||||
{error, 'conflict'};
|
||||
_ ->
|
||||
gen_storage:delete_where(LServer, privacy_list,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
gen_storage:delete_where(LServer, privacy_list_data,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
{result, []}
|
||||
end
|
||||
@ -421,14 +478,14 @@ process_list_set(LUser, LServer, Name, Els) ->
|
||||
F = fun() ->
|
||||
OldData =
|
||||
gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Name}]),
|
||||
lists:foreach(
|
||||
fun(Data1) ->
|
||||
gen_storage:delete_object(LServer, Data1)
|
||||
end, OldData),
|
||||
|
||||
gen_storage:write(LServer, #privacy_list{username_server = {LUser, LServer},
|
||||
gen_storage:write(LServer, #privacy_list{user_host = {LUser, LServer},
|
||||
name = Name}),
|
||||
NewData = list_items_to_data(LUser, LServer, Name, List),
|
||||
lists:foreach(
|
||||
@ -467,7 +524,7 @@ parse_items([], Res) ->
|
||||
lists:reverse(Res);
|
||||
parse_items([El = #xmlel{name = item} | Els], Res) ->
|
||||
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),
|
||||
SOrder = exmpp_xml:get_attribute_as_list(El, order, false),
|
||||
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) ->
|
||||
I1 = #listitem{action = Action, order = Order},
|
||||
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
|
||||
"jid" ->
|
||||
try
|
||||
JID = exmpp_jid:parse(V),
|
||||
I1#listitem{
|
||||
type = jid,
|
||||
value = jlib:short_prepd_jid(JID)}
|
||||
catch
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
I1#listitem{type = jid,
|
||||
value = V};
|
||||
"group" ->
|
||||
I1#listitem{type = group,
|
||||
value = V};
|
||||
"subscription" ->
|
||||
case V of
|
||||
"none" ->
|
||||
I1#listitem{type = subscription,
|
||||
value = none};
|
||||
"both" ->
|
||||
I1#listitem{type = subscription,
|
||||
value = both};
|
||||
"from" ->
|
||||
I1#listitem{type = subscription,
|
||||
value = from};
|
||||
"to" ->
|
||||
I1#listitem{type = subscription,
|
||||
value = to};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
I1#listitem{type = subscription,
|
||||
value = V}
|
||||
end;
|
||||
{T, false} when is_list(T) ->
|
||||
false;
|
||||
@ -566,10 +602,6 @@ parse_matches1(_Item, [#xmlel{} | _Els]) ->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%% storage representation to ejabberd representation
|
||||
list_data_to_items(Data) ->
|
||||
List =
|
||||
@ -592,7 +624,7 @@ list_data_to_items(Data) ->
|
||||
list_items_to_data(LUser, LServer, Name, List) ->
|
||||
lists:map(
|
||||
fun(Item) ->
|
||||
#privacy_list_data{username_server = {LUser, LServer},
|
||||
#privacy_list_data{user_host = {LUser, LServer},
|
||||
name = Name,
|
||||
type = Item#listitem.type,
|
||||
value = Item#listitem.value,
|
||||
@ -626,7 +658,7 @@ get_user_list(_, User, Server)
|
||||
#userlist{};
|
||||
[#privacy_default_list{name = Default}] ->
|
||||
Data = gen_storage:select(LServer, privacy_list_data,
|
||||
[{'=', username_server, {LUser, LServer}},
|
||||
[{'=', user_host, {LUser, LServer}},
|
||||
{'=', name, Default}]),
|
||||
List = list_data_to_items(Data),
|
||||
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
|
||||
is_type_match(Type, Value, JID, Subscription, Groups) ->
|
||||
case Type of
|
||||
jid ->
|
||||
{User, Server, Resource} = Value,
|
||||
((User == undefined) orelse (User == []) orelse (User == exmpp_jid:prep_node(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)));
|
||||
subscription ->
|
||||
Value == Subscription;
|
||||
group ->
|
||||
lists:member(Value, Groups)
|
||||
end.
|
||||
is_type_match(jid, Value, JID, _Subscription, _Groups) ->
|
||||
{User, Server, Resource} = exmpp_jid:to_lower(exmpp_jid:parse(Value)),
|
||||
((User == undefined) orelse (User == []) orelse (User == exmpp_jid:prep_node(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)));
|
||||
is_type_match(subscription, Value, _JID, Subscription, _Groups) ->
|
||||
Value == Subscription;
|
||||
is_type_match(group, Value, _JID, _Subscription, Groups) ->
|
||||
lists:member(Value, Groups).
|
||||
|
||||
|
||||
remove_user(User, Server) ->
|
||||
@ -772,14 +801,9 @@ updated_list(_,
|
||||
Old
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
update_tables(Host) ->
|
||||
Fields = record_info(fields, privacy_list),
|
||||
case mnesia:table_info(privacy_list, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp();
|
||||
_ -> ok
|
||||
end,
|
||||
update_tables(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, privacy_default_list,
|
||||
[{privacy, [us, default, lists],
|
||||
@ -796,11 +820,12 @@ update_tables(Host) ->
|
||||
match_message = MatchMessage,
|
||||
match_presence_in = MatchPresenceIn,
|
||||
match_presence_out = MatchPresenceOut}) ->
|
||||
ValueBin = convert_value_to_binary(Value),
|
||||
gen_storage:write(Host,
|
||||
#privacy_list_data{username_server = US,
|
||||
#privacy_list_data{user_host = US,
|
||||
name = Name,
|
||||
type = Type,
|
||||
value = Value,
|
||||
value = ValueBin,
|
||||
action = Action,
|
||||
order = Order,
|
||||
match_all = MatchAll,
|
||||
@ -810,23 +835,26 @@ update_tables(Host) ->
|
||||
match_presence_out = MatchPresenceOut})
|
||||
end, List),
|
||||
gen_storage:write(Host,
|
||||
#privacy_list{username_server = US,
|
||||
#privacy_list{user_host = US,
|
||||
name = Name})
|
||||
end, Lists),
|
||||
if
|
||||
is_list(Default) ->
|
||||
#privacy_default_list{username_server = US,
|
||||
#privacy_default_list{user_host = US,
|
||||
name = Default};
|
||||
true -> null
|
||||
end
|
||||
end}]),
|
||||
end}]);
|
||||
|
||||
update_tables(Host, odbc) ->
|
||||
gen_storage_migration:migrate_odbc(
|
||||
Host, [privacy_default_list, privacy_list, privacy_list_data],
|
||||
[{[{"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) ->
|
||||
US = {Username, Host},
|
||||
DefaultLists = [#privacy_default_list{username_server = US,
|
||||
DefaultLists = [#privacy_default_list{user_host = US,
|
||||
name = Name}
|
||||
|| [_, _] <- SELECT(["username", "name"],
|
||||
"privacy_default_list",
|
||||
@ -859,22 +887,22 @@ update_tables(Host) ->
|
||||
{subscription, to}
|
||||
end
|
||||
end,
|
||||
ValueBin = convert_value_to_binary(Value),
|
||||
Action =
|
||||
case SAction of
|
||||
"a" -> allow;
|
||||
"d" -> deny
|
||||
end,
|
||||
Order = list_to_integer(SOrder),
|
||||
MatchAll = SMatchAll == "1" orelse SMatchAll == "t",
|
||||
MatchIQ = SMatchIQ == "1" orelse SMatchIQ == "t" ,
|
||||
MatchMessage = SMatchMessage == "1" orelse SMatchMessage == "t",
|
||||
MatchPresenceIn = SMatchPresenceIn == "1" orelse SMatchPresenceIn == "t",
|
||||
MatchPresenceOut = SMatchPresenceOut == "1" orelse SMatchPresenceOut == "t",
|
||||
|
||||
#privacy_list_data{username_server = US,
|
||||
MatchAll = ejabberd_odbc:to_bool(SMatchAll),
|
||||
MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
|
||||
MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
|
||||
MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
|
||||
MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
|
||||
#privacy_list_data{user_host = US,
|
||||
name = Name,
|
||||
type = Type,
|
||||
value = Value,
|
||||
value = ValueBin,
|
||||
action = Action,
|
||||
order = Order,
|
||||
match_all = MatchAll,
|
||||
@ -887,53 +915,26 @@ update_tables(Host) ->
|
||||
"match_presence_out"],
|
||||
"privacy_list_data",
|
||||
[{"id", Id}])),
|
||||
[#privacy_list{username_server = US,
|
||||
[#privacy_list{user_host = US,
|
||||
name = Name} | DefaultLists ++ ListData]
|
||||
end},
|
||||
end},
|
||||
{"privacy_default_list", ["username", "name"],
|
||||
fun(_, Username, Name) ->
|
||||
US = {Username, Host},
|
||||
[#privacy_default_list{username_server = US,
|
||||
name = Name}]
|
||||
fun(_, Username, Name) ->
|
||||
US = {Username, Host},
|
||||
[#privacy_default_list{user_host = US,
|
||||
name = Name}]
|
||||
end}
|
||||
]).
|
||||
|
||||
convert_to_exmpp() ->
|
||||
Fun = fun() ->
|
||||
mnesia:foldl(fun convert_to_exmpp2/2, done, privacy, write)
|
||||
end,
|
||||
mnesia:transaction(Fun).
|
||||
|
||||
convert_to_exmpp2(#privacy{us = {U, S} = Key, lists = L} = P, Acc) ->
|
||||
U1 = convert_jid_to_exmpp(U),
|
||||
L1 = convert_lists_to_exmpp(L),
|
||||
New_P = P#privacy{
|
||||
us = {U1, S},
|
||||
lists = L1
|
||||
},
|
||||
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).
|
||||
convert_value_to_binary({U, H, R}) ->
|
||||
exmpp_jid:to_binary(U, H, R);
|
||||
convert_value_to_binary(Value) when is_list(Value) ->
|
||||
list_to_binary(Value);
|
||||
convert_value_to_binary(none) ->
|
||||
<<"none">>;
|
||||
convert_value_to_binary(both) ->
|
||||
<<"both">>;
|
||||
convert_value_to_binary(from) ->
|
||||
<<"from">>;
|
||||
convert_value_to_binary(to) ->
|
||||
<<"to">>.
|
||||
|
@ -19,7 +19,7 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(privacy, {us,
|
||||
-record(privacy, {user_host,
|
||||
default = none,
|
||||
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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -38,8 +66,8 @@
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%% TODO: usns instead of user_server_ns requires no migration
|
||||
-record(private_storage, {user_server_ns, xml}).
|
||||
%% TODO: usns instead of user_host_ns requires no migration
|
||||
-record(private_storage, {user_host_ns, xml}).
|
||||
|
||||
start(Host, Opts) ->
|
||||
HostB = list_to_binary(Host),
|
||||
@ -49,8 +77,8 @@ start(Host, Opts) ->
|
||||
[{disc_only_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{attributes, record_info(fields, private_storage)},
|
||||
{types, [{user_server_ns, {text, text, text}}]}]),
|
||||
update_table(Host),
|
||||
{types, [{user_host_ns, {binary, binary, atom}}, {xml, xmlel}]}]),
|
||||
update_table(Host, Backend),
|
||||
ejabberd_hooks:add(remove_user, HostB,
|
||||
?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE,
|
||||
@ -146,10 +174,11 @@ check_ns(_From, _To, #iq{payload = SubEl}) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%% The xml is stored as xmlel() in mnesia, but as text in odbc
|
||||
set_data(LUser, LServer, El) ->
|
||||
XMLNS = exmpp_xml:get_ns_as_atom(El),
|
||||
gen_storage:write(LServer,
|
||||
#private_storage{user_server_ns = {LUser, LServer, XMLNS},
|
||||
#private_storage{user_host_ns = {LUser, LServer, XMLNS},
|
||||
xml = El}).
|
||||
|
||||
get_data(LUser, LServer, Els) ->
|
||||
@ -175,9 +204,9 @@ remove_user(User, Server)
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
F = fun() ->
|
||||
Records = gen_storage:select(LServer, private_storage,
|
||||
[{'=', user_server_ns, {LUser, LServer, '_'}}]),
|
||||
[{'=', user_host_ns, {LUser, LServer, '_'}}]),
|
||||
lists:foreach(
|
||||
fun(#private_storage{user_server_ns = USNS}) ->
|
||||
fun(#private_storage{user_host_ns = USNS}) ->
|
||||
gen_storage:delete(LServer, {private_storage, USNS})
|
||||
end, Records)
|
||||
end,
|
||||
@ -187,97 +216,27 @@ remove_user(User, Server)
|
||||
ok
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
update_table(Host) ->
|
||||
Fields = record_info(fields, private_storage),
|
||||
case mnesia:table_info(private_storage, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp(),
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, private_storage,
|
||||
[{private_storage, [userns, 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}) ->
|
||||
#private_storage{user_server_ns = {User, Server, NS},
|
||||
xml = lists:flatten(xml:element_to_string(Xml))}
|
||||
end}]),
|
||||
gen_storage_migration:migrate_odbc(
|
||||
Host, [private_storage],
|
||||
[{"private_storage", ["username", "namespace", "data"],
|
||||
fun(_, Username, Namespace, Data) ->
|
||||
[#private_storage{user_server_ns = {Username, Host, Namespace},
|
||||
xml = Data}]
|
||||
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.
|
||||
update_table(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, private_storage,
|
||||
[{private_storage, [usns, xml],
|
||||
fun({private_storage, {User, Server, NS}, Xml}) ->
|
||||
U1 = list_to_binary(User),
|
||||
S1 = list_to_binary(Server),
|
||||
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}]);
|
||||
|
||||
|
||||
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.
|
||||
update_table(Host, odbc) ->
|
||||
gen_storage_migration:migrate_odbc(
|
||||
Host, [private_storage],
|
||||
[{"private_storage", ["username", "namespace", "data"],
|
||||
fun(_, Username, Namespace, Data) ->
|
||||
[#private_storage{user_host_ns = {Username, Host, Namespace},
|
||||
xml = Data}]
|
||||
end}]).
|
||||
|
@ -33,6 +33,73 @@
|
||||
%%% Roster version is a hash digest of the entire roster.
|
||||
%%% 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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -61,29 +128,6 @@
|
||||
-include("mod_roster.hrl").
|
||||
-include("web/ejabberd_http.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()
|
||||
%% Host = string()
|
||||
@ -98,7 +142,7 @@ start(Host, Opts) when is_list(Host) ->
|
||||
rosteritem, [{disc_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{attributes, record_info(fields, rosteritem)},
|
||||
{types, [{user_server_jid, {text, text, ljid}},
|
||||
{types, [{user_host_jid, {text, text, ljid}},
|
||||
{subscription, atom},
|
||||
{ask, atom}]}]),
|
||||
gen_storage:create_table(Backend, HostB,
|
||||
@ -106,10 +150,10 @@ start(Host, Opts) when is_list(Host) ->
|
||||
{odbc_host, Host},
|
||||
{type, bag},
|
||||
{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()]},
|
||||
{attributes, record_info(fields, roster_version)}]),
|
||||
update_tables(Host),
|
||||
update_table(Host, Backend),
|
||||
mnesia:add_table_index(roster, us),
|
||||
mnesia:add_table_index(roster_version, us),
|
||||
ejabberd_hooks:add(roster_get, HostB,
|
||||
@ -315,19 +359,20 @@ get_user_roster(Acc, {U, S}) when is_binary(U), is_binary(S) ->
|
||||
true
|
||||
end, Items) ++ Acc.
|
||||
|
||||
%% Reads the roster information from the database, and returns a list of #roster records.
|
||||
get_roster(LUser, LServer) ->
|
||||
F = fun() ->
|
||||
U = gen_storage:select(LServer, rosteritem,
|
||||
[{'=', user_server_jid, {LUser, LServer, '_'}}]),
|
||||
[{'=', user_host_jid, {LUser, LServer, '_'}}]),
|
||||
G = gen_storage:select(LServer, rostergroup,
|
||||
[{'=', user_server_jid, {LUser, LServer, '_'}}]),
|
||||
[{'=', user_host_jid, {LUser, LServer, '_'}}]),
|
||||
[storageroster_to_roster(Rosteritem, G) ||
|
||||
Rosteritem <- U]
|
||||
end,
|
||||
{atomic, Rs} = gen_storage:transaction(LServer, rosteritem, F),
|
||||
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,
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
@ -336,21 +381,26 @@ storageroster_to_roster(#rosteritem{user_server_jid = {U, S, JID} = USJ,
|
||||
US = {U, S},
|
||||
Groups =
|
||||
lists:foldl(
|
||||
fun(#rostergroup{user_server_jid = USJ1, grp = G}, R)
|
||||
fun(#rostergroup{user_host_jid = USJ1, grp = G}, R)
|
||||
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
|
||||
end, [], Rostergroups),
|
||||
#roster{usj = {US, JID},
|
||||
us = US,
|
||||
jid = JID,
|
||||
name = Name,
|
||||
name = convert_to_string(Name),
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
askmessage = AskMessage,
|
||||
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
|
||||
%% Item = rosteritem()
|
||||
%% XML = exmpp_xml:xmlel()
|
||||
@ -441,7 +491,7 @@ process_item_set(From, To, #xmlel{} = El) ->
|
||||
ask = Ask2,
|
||||
askmessage = AskMessage2,
|
||||
groups = Groups} ->
|
||||
I2 = #rosteritem{user_server_jid = {LUser, LServer, LJID},
|
||||
I2 = #rosteritem{user_host_jid = {LUser, LServer, LJID},
|
||||
name = Name,
|
||||
subscription = Subscription2,
|
||||
ask = Ask2,
|
||||
@ -451,7 +501,7 @@ process_item_set(From, To, #xmlel{} = El) ->
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
gen_storage:write(LServer,
|
||||
#rostergroup{user_server_jid = {LUser, LServer, LJID},
|
||||
#rostergroup{user_host_jid = {LUser, LServer, LJID},
|
||||
grp = Group})
|
||||
end, Groups)
|
||||
end,
|
||||
@ -635,7 +685,7 @@ get_subscription_lists(_, User, Server)
|
||||
try
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
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) ->
|
||||
fill_subscription_lists(Items, [], []);
|
||||
_ ->
|
||||
@ -653,7 +703,7 @@ get_subscription_lists(_, User, Server)
|
||||
%% New_F = [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],
|
||||
F, T) ->
|
||||
case Subscription of
|
||||
@ -714,7 +764,7 @@ process_subscription(Direction, User, Server, JID1, Type, Reason)
|
||||
F = fun() ->
|
||||
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
|
||||
@ -911,12 +961,12 @@ remove_user(User, Server)
|
||||
fun(R) ->
|
||||
gen_storage:delete_object(LServer, R)
|
||||
end,
|
||||
gen_storage:select(LServer, rosteritem, [{'=', user_server_jid, {LUser, LServer, '_'}}])),
|
||||
gen_storage:select(LServer, rosteritem, [{'=', user_host_jid, {LUser, LServer, '_'}}])),
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
gen_storage:delete_object(LServer, R)
|
||||
end,
|
||||
gen_storage:select(LServer, rostergroup, [{'=', user_server_jid, {LUser, LServer, '_'}}]))
|
||||
gen_storage:select(LServer, rostergroup, [{'=', user_host_jid, {LUser, LServer, '_'}}]))
|
||||
end,
|
||||
gen_storage:transaction(LServer, rosteritem, F)
|
||||
catch
|
||||
@ -1014,7 +1064,7 @@ process_item_set_t(LUser, LServer, #xmlel{} = El) ->
|
||||
ask = Ask,
|
||||
askmessage = AskMessage,
|
||||
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,
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
@ -1023,7 +1073,7 @@ process_item_set_t(LUser, LServer, #xmlel{} = El) ->
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
gen_storage:write(LServer,
|
||||
#rostergroup{user_server_jid = {LUser, LServer, LJID},
|
||||
#rostergroup{user_host_jid = {LUser, LServer, LJID},
|
||||
grp = Group})
|
||||
end, Groups)
|
||||
end
|
||||
@ -1082,10 +1132,10 @@ get_in_pending_subscriptions(Ls, User, Server)
|
||||
JID = exmpp_jid:make(User, Server),
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
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) ->
|
||||
Ls ++ lists:map(
|
||||
fun(#rosteritem{user_server_jid = {_, _, RJID},
|
||||
fun(#rosteritem{user_host_jid = {_, _, RJID},
|
||||
askmessage = Message}) ->
|
||||
Status = if is_binary(Message) ->
|
||||
binary_to_list(Message);
|
||||
@ -1165,169 +1215,92 @@ get_jid_info(_, User, Server, JID)
|
||||
{none, []}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% @hidden
|
||||
%% Only supports migration from ejabberd 1.1.2 or higher.
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, roster),
|
||||
case mnesia:table_info(roster, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp();
|
||||
[uj, user, jid, name, subscription, ask, groups, xattrs, xs] ->
|
||||
convert_table1(Fields);
|
||||
[usj, us, jid, name, subscription, ask, groups, xattrs, xs] ->
|
||||
convert_table2(Fields);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating roster table", []),
|
||||
mnesia:transform_table(roster, ignore, Fields)
|
||||
end.
|
||||
update_table(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, rosteritem,
|
||||
[{roster, [usj, us, jid, name, subscription, ask, groups, askmessage, xs],
|
||||
fun({roster, USJ, _, _, Name, Subscription, Ask, Groups, AskMessage, _Xs}) ->
|
||||
%% Convert "" to undefined in JIDs and string() to binary().
|
||||
{USJ_U, USJ_S, {USJ_JU, USJ_JS, USJ_JR}} = USJ,
|
||||
USJ_U1 = convert_jid_to_exmpp(USJ_U),
|
||||
USJ_S1 = convert_jid_to_exmpp(USJ_S),
|
||||
USJ_JU1 = convert_jid_to_exmpp(USJ_JU),
|
||||
USJ_JS1 = convert_jid_to_exmpp(USJ_JS),
|
||||
USJ_JR1 = convert_jid_to_exmpp(USJ_JR),
|
||||
USJ1 = {USJ_U1, USJ_S1, {USJ_JU1, USJ_JS1, USJ_JR1}},
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
Group2 = list_to_binary(Group),
|
||||
gen_storage:write(Host,
|
||||
#rostergroup{user_host_jid = USJ1,
|
||||
grp = Group2})
|
||||
end, Groups),
|
||||
Name2 = convert_name_to_exmpp(Name),
|
||||
AskMessage2 = convert_askmessage_to_exmpp(AskMessage),
|
||||
#rosteritem{user_host_jid = USJ1,
|
||||
name = Name2,
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
askmessage = AskMessage2}
|
||||
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_S1 = convert_jid_to_exmpp(USJ_S),
|
||||
USJ_JU1 = convert_jid_to_exmpp(USJ_JU),
|
||||
USJ_JS1 = convert_jid_to_exmpp(USJ_JS),
|
||||
USJ_JR1 = convert_jid_to_exmpp(USJ_JR),
|
||||
US_U1 = convert_jid_to_exmpp(US_U),
|
||||
US_S1 = convert_jid_to_exmpp(US_S),
|
||||
JID_U1 = convert_jid_to_exmpp(JID_U),
|
||||
JID_S1 = convert_jid_to_exmpp(JID_S),
|
||||
JID_R1 = convert_jid_to_exmpp(JID_R),
|
||||
% Convert name.
|
||||
N1 = convert_name_to_exmpp(N),
|
||||
% Convert groups.
|
||||
G1 = convert_groups_to_exmpp(G, []),
|
||||
% Convert xs.
|
||||
XS1 = convert_xs_to_exmpp(XS),
|
||||
% Convert askmessage.
|
||||
AM1 = convert_askmessage_to_exmpp(AM),
|
||||
% Prepare the new record.
|
||||
New_R = R#roster{
|
||||
usj = {USJ_U1, USJ_S1, {USJ_JU1, USJ_JS1, USJ_JR1}},
|
||||
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(V) when is_list(V) -> list_to_binary(V).
|
||||
|
||||
%% @hidden
|
||||
|
||||
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) ->
|
||||
AM;
|
||||
convert_askmessage_to_exmpp(AM) ->
|
||||
list_to_binary(AM).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% @spec (Acc, Host, Request) -> {stop, Result} | Acc
|
||||
%% Acc = term()
|
||||
%% Host = string()
|
||||
@ -1381,14 +1354,14 @@ user_roster(User, Server, Query, Lang) ->
|
||||
Groups =
|
||||
lists:flatmap(
|
||||
fun(Group) ->
|
||||
[?C(binary_to_list(Group)), ?BR]
|
||||
[?C(Group), ?BR]
|
||||
end, R#roster.groups),
|
||||
Pending = ask_to_pending(R#roster.ask),
|
||||
TDJID = build_contact_jid_td(R#roster.jid),
|
||||
?XE("tr",
|
||||
[TDJID,
|
||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||
binary_to_list(R#roster.name)),
|
||||
R#roster.name),
|
||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||
atom_to_list(R#roster.subscription)),
|
||||
?XAC("td", [?XMLATTR('class', <<"valign">>)],
|
||||
@ -1571,83 +1544,3 @@ us_to_list({User, Server}) ->
|
||||
webadmin_user(Acc, _User, _Server, Lang) ->
|
||||
% `Lang' is used by the `T' macro, called from the `ACT' macro.
|
||||
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,
|
||||
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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
@ -48,8 +74,8 @@
|
||||
|
||||
-define(JUD_MATCHES, 30).
|
||||
|
||||
-record(vcard_search, {user_server,
|
||||
user, luser,
|
||||
-record(vcard_search, {user_host,
|
||||
username, lusername,
|
||||
fn, lfn,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
@ -62,7 +88,7 @@
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit
|
||||
}).
|
||||
-record(vcard, {user_server, vcard}).
|
||||
-record(vcard, {user_host, vcard}).
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_vcard).
|
||||
|
||||
@ -73,15 +99,14 @@ start(Host, Opts) ->
|
||||
[{disc_only_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{attributes, record_info(fields, vcard)},
|
||||
{types, [{user_server, {text, text}}]}]),
|
||||
{types, [{user_host, {text, text}}]}]),
|
||||
gen_storage:create_table(Backend, HostB, vcard_search,
|
||||
[{disc_copies, [node()]},
|
||||
{odbc_host, Host},
|
||||
{attributes, record_info(fields, vcard_search)},
|
||||
{types, [{user_server, {text, text}},
|
||||
{user, {text, text}}]}]),
|
||||
update_tables(Host),
|
||||
gen_storage:add_table_index(Host, vcard_search, luser),
|
||||
{types, [{user_host, {text, text}}]}]),
|
||||
update_tables(Host, Backend),
|
||||
gen_storage:add_table_index(Host, vcard_search, lusername),
|
||||
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, 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) ->
|
||||
LUser = exmpp_jid:prep_node_as_list(To),
|
||||
LServer = exmpp_jid:prep_domain_as_list(To),
|
||||
US = {LUser, LServer},
|
||||
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
|
||||
case get_vcard(LUser, LServer) of
|
||||
{vcard, VCard} ->
|
||||
exmpp_iq:result(IQ_Rec, VCard);
|
||||
novcard ->
|
||||
@ -229,6 +237,22 @@ process_sm_iq(From, _To, #iq{type = set, payload = Request} = IQ_Rec) ->
|
||||
exmpp_iq:error(IQ_Rec, 'not-allowed')
|
||||
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) ->
|
||||
FN = exmpp_xml:get_path(VCARD,
|
||||
[{element, 'FN'}, cdata_as_list]),
|
||||
@ -277,12 +301,16 @@ set_vcard(User, LServer, VCARD) ->
|
||||
|
||||
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() ->
|
||||
gen_storage:write(LServer, #vcard{user_server = US, vcard = VCARD}),
|
||||
gen_storage:write(
|
||||
#vcard_search{user_server=US,
|
||||
user = {User, LServer},
|
||||
luser = LUser,
|
||||
gen_storage:write(LServer, #vcard{user_host = US, vcard = VcardToStore}),
|
||||
gen_storage:write(LServer,
|
||||
#vcard_search{user_host=US,
|
||||
username = User, lusername = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@ -501,7 +529,7 @@ search_result(Lang, JID, ServerHost, Data) ->
|
||||
[#xmlcdata{cdata = Val}]}]}).
|
||||
|
||||
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 =
|
||||
[
|
||||
?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,
|
||||
search_all_hosts, true) of
|
||||
true ->
|
||||
{like, luser, make_val(LVal)};
|
||||
{like, lusername, make_val(LVal)};
|
||||
false ->
|
||||
Host = find_my_host(LServer),
|
||||
{like, user_server, {make_val(LVal), Host}}
|
||||
{like, user_host, {make_val(LVal), Host}}
|
||||
end;
|
||||
"fn" -> {like, lfn, make_val(LVal)};
|
||||
"last" -> {like, lfamily, make_val(LVal)};
|
||||
@ -628,7 +656,7 @@ parts_to_string(Parts) ->
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
set_vcard_t(R, _) ->
|
||||
US = R#vcard.user_server,
|
||||
US = R#vcard.user_host,
|
||||
User = US,
|
||||
VCARD = R#vcard.vcard,
|
||||
|
||||
@ -669,8 +697,8 @@ set_vcard_t(R, _) ->
|
||||
LOrgName = exmpp_stringprep:to_lower(OrgName),
|
||||
LOrgUnit = exmpp_stringprep:to_lower(OrgUnit),
|
||||
mnesia:write(
|
||||
#vcard_search{user_server= US,
|
||||
user = User, luser = LUser,
|
||||
#vcard_search{user_host= US,
|
||||
username = User, lusername = LUser,
|
||||
fn = FN, lfn = LFN,
|
||||
family = Family, lfamily = LFamily,
|
||||
given = Given, lgiven = LGiven,
|
||||
@ -707,174 +735,89 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
|
||||
gen_storage:transaction(LServer, vcard, F).
|
||||
|
||||
|
||||
update_tables(Host) ->
|
||||
update_vcard_table(),
|
||||
update_vcard_search_table(),
|
||||
update_vcard_storage(Host).
|
||||
%%%
|
||||
%%% Update tables
|
||||
%%%
|
||||
|
||||
update_vcard_table() ->
|
||||
Fields = record_info(fields, vcard),
|
||||
case mnesia:table_info(vcard, attributes) of
|
||||
Fields ->
|
||||
convert_to_exmpp();
|
||||
[user, vcard] ->
|
||||
?INFO_MSG("Converting vcard table from "
|
||||
"{user, vcard} format", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{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.
|
||||
update_tables(Host, mnesia) ->
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, vcard,
|
||||
[{vcard, [us, vcard],
|
||||
fun({vcard, US, Vcard}) ->
|
||||
#vcard{user_host = US,
|
||||
vcard = convert_vcard_element(Vcard)}
|
||||
end}]),
|
||||
gen_storage_migration:migrate_mnesia(
|
||||
Host, vcard_search,
|
||||
[{vcard_search, [us,
|
||||
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(Record) ->
|
||||
Record
|
||||
end}]);
|
||||
|
||||
update_tables(Host, odbc) ->
|
||||
gen_storage_migration:migrate_odbc(
|
||||
Host, [vcard],
|
||||
[{"vcard", ["username", "vcard"],
|
||||
fun(_, Username, Vcard) ->
|
||||
[#vcard{user_host = {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_host = {LUser, Host},
|
||||
username = User, lusername = 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}]).
|
||||
|
||||
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,
|
||||
family, lfamily,
|
||||
given, lgiven,
|
||||
middle, lmiddle,
|
||||
nickname, lnickname,
|
||||
bday, lbday,
|
||||
ctry, lctry,
|
||||
locality, llocality,
|
||||
email, lemail,
|
||||
orgname, lorgname,
|
||||
orgunit, lorgunit] ->
|
||||
?INFO_MSG("Converting vcard_search table from "
|
||||
"{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", []),
|
||||
Host = ?MYNAME,
|
||||
{atomic, ok} = mnesia:create_table(
|
||||
mod_vcard_tmp_table,
|
||||
[{disc_only_copies, [node()]},
|
||||
{type, bag},
|
||||
{local_content, true},
|
||||
{record_name, vcard_search},
|
||||
{attributes, record_info(fields, vcard_search)}]),
|
||||
F1 = fun() ->
|
||||
mnesia:write_lock_table(mod_vcard_tmp_table),
|
||||
mnesia:foldl(
|
||||
fun({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
|
||||
}, _) ->
|
||||
mnesia:dirty_write(
|
||||
mod_vcard_tmp_table,
|
||||
#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, ok, vcard_search)
|
||||
end,
|
||||
mnesia:transaction(F1),
|
||||
lists:foreach(fun(I) ->
|
||||
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.
|
||||
convert_vcard_element(Xmlelement) ->
|
||||
Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement, [?NS_VCARD], []),
|
||||
exmpp_xml:remove_whitespaces_deeply(Xmlel).
|
||||
|
||||
%%%
|
||||
%%% WebAdmin
|
||||
@ -898,16 +841,14 @@ webadmin_page(_, Host,
|
||||
webadmin_page(Acc, _, _) -> Acc.
|
||||
|
||||
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),
|
||||
VcardString = case mnesia:dirty_read({vcard, US}) of
|
||||
[Vcard] ->
|
||||
Xml = Vcard#vcard.vcard,
|
||||
VcardString = case get_vcard(LUser, LServer) of
|
||||
{vcard, Xml} ->
|
||||
XmlString = lists:flatten(exmpp_xml:document_to_list(Xml)),
|
||||
Reduced = re:replace(XmlString, "<BINVAL>[^<]*", "<BINVAL>...", [global, {return, list}]),
|
||||
try_indent(Reduced);
|
||||
[] -> "no vcard";
|
||||
_ -> "error getting vcard"
|
||||
novcard -> "no vcard"
|
||||
end,
|
||||
[?XE('h1', [?CT("vCard")]),
|
||||
?XE('h2', [?AC("../", us_to_list(US))])
|
||||
@ -937,18 +878,16 @@ try_indent(String) ->
|
||||
end.
|
||||
|
||||
get_user_photo(User, Host) ->
|
||||
US = {User, Host},
|
||||
case mnesia:dirty_read({vcard, US}) of
|
||||
[VCard] ->
|
||||
case exmpp_xml:get_path(VCard#vcard.vcard, [{element, "PHOTO"}, {element, "BINVAL"}, cdata_as_list]) of
|
||||
case get_vcard(User, Host) of
|
||||
{vcard, VCard} ->
|
||||
case exmpp_xml:get_path(VCard, [{element, "PHOTO"}, {element, "BINVAL"}, cdata_as_list]) of
|
||||
[] -> "no avatar";
|
||||
BinVal -> case catch jlib:decode_base64(BinVal) of
|
||||
{'EXIT', _} -> "error";
|
||||
Decoded -> Decoded
|
||||
end
|
||||
end;
|
||||
[] -> "no vcard";
|
||||
_ -> "error getting vcard"
|
||||
novcard -> "no vcard"
|
||||
end.
|
||||
|
||||
user_queue_parse_query(US, Query) ->
|
||||
@ -971,11 +910,11 @@ us_to_list({User, Server}) ->
|
||||
exmpp_jid:to_list(User, Server).
|
||||
|
||||
webadmin_user(Acc, User, Server, Lang) ->
|
||||
US = {exmpp_stringprep:nodeprep(User), exmpp_stringprep:nameprep(Server)},
|
||||
VcardSize = case mnesia:dirty_read({vcard, US}) of
|
||||
[Vcard] -> get_vcard_size(Vcard);
|
||||
[] -> 0;
|
||||
_ -> -1
|
||||
LUser = exmpp_stringprep:nodeprep(User),
|
||||
LServer = exmpp_stringprep:nameprep(Server),
|
||||
VcardSize = case get_vcard(LUser, LServer) of
|
||||
{vcard, Vcard} -> get_vcard_size(Vcard);
|
||||
novcard -> 0
|
||||
end,
|
||||
FVcardSize = case VcardSize > 0 of
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
webadmin_user_parse_query(_, "removevcard", User, Server, _Query) ->
|
||||
@ -1002,86 +941,3 @@ webadmin_user_parse_query(_, "removevcard", User, Server, _Query) ->
|
||||
end;
|
||||
webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) ->
|
||||
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