diff --git a/doc/dev.tex b/doc/dev.tex index 1e46c1af2..7fb47e9cc 100644 --- a/doc/dev.tex +++ b/doc/dev.tex @@ -37,24 +37,20 @@ \newcommand{\modecho}{\module{mod\_echo}} \newcommand{\modirc}{\module{mod\_irc}} \newcommand{\modlast}{\module{mod\_last}} -\newcommand{\modlastodbc}{\module{mod\_last\_odbc}} \newcommand{\modmuc}{\module{mod\_muc}} \newcommand{\modmuclog}{\module{mod\_muc\_log}} \newcommand{\modoffline}{\module{mod\_offline}} -\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}} \newcommand{\modprivacy}{\module{mod\_privacy}} \newcommand{\modprivate}{\module{mod\_private}} \newcommand{\modpubsub}{\module{mod\_pubsub}} \newcommand{\modregister}{\module{mod\_register}} \newcommand{\modroster}{\module{mod\_roster}} -\newcommand{\modrosterodbc}{\module{mod\_roster\_odbc}} \newcommand{\modservicelog}{\module{mod\_service\_log}} \newcommand{\modsharedroster}{\module{mod\_shared\_roster}} \newcommand{\modstats}{\module{mod\_stats}} \newcommand{\modtime}{\module{mod\_time}} \newcommand{\modvcard}{\module{mod\_vcard}} \newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}} -\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}} \newcommand{\modversion}{\module{mod\_version}} %% Title page diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 1662b5400..f95a04763 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -141,8 +141,7 @@ check_password(User, Server, Password, Digest, DigestGen) %% {true, AuthModule} | {false, Reason::string()} %% where %% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external -%% | ejabberd_auth_internal | ejabberd_auth_ldap -%% | ejabberd_auth_odbc | ejabberd_auth_pam +%% | ejabberd_auth_ldap | ejabberd_auth_pam | ejabberd_auth_storage %% @doc Check if the user and password can login in server. %% The user can login if at least an authentication method accepts the user %% and the password. diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index 33a97da31..e03437722 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -371,12 +371,9 @@ get_mod_last_enabled(Server) -> end. get_mod_last_configured(Server) -> - ML = is_configured(Server, mod_last), - MLO = is_configured(Server, mod_last_odbc), - case {ML, MLO} of - {true, _} -> mod_last; - {false, true} -> mod_last_odbc; - {false, false} -> no_mod_last + case is_configured(Server, mod_last) of + true -> mod_last; + false -> no_mod_last end. is_configured(Host, Module) -> diff --git a/src/ejabberd_auth_internal.erl b/src/ejabberd_auth_internal.erl deleted file mode 100644 index ac2208478..000000000 --- a/src/ejabberd_auth_internal.erl +++ /dev/null @@ -1,468 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_auth_internal.erl -%%% Author : Alexey Shchepin -%%% Purpose : Authentification via mnesia (obsolete) -%%% Created : 12 Dec 2004 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_auth_internal). --author('alexey@process-one.net'). - -%% External exports --export([start/1, - set_password/3, - check_password/3, - check_password/5, - try_register/3, - dirty_get_registered_users/0, - get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, - get_password/2, - get_password_s/2, - is_user_exists/2, - remove_user/2, - remove_user/3, - plain_password_required/0 - ]). - --include("ejabberd.hrl"). - --record(passwd, {us, password}). --record(reg_users_counter, {vhost, count}). - -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- - -%% @spec (Host) -> ok -%% Host = string() - -start(Host) -> - mnesia:create_table(passwd, [{disc_copies, [node()]}, - {attributes, record_info(fields, passwd)}]), - mnesia:create_table(reg_users_counter, - [{ram_copies, [node()]}, - {attributes, record_info(fields, reg_users_counter)}]), - update_table(), - 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 = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({passwd, US}) of - [#passwd{password = Password}] -> - Password /= ""; - _ -> - false - end. - -%% @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 mnesia:dirty_read({passwd, US}) of - [#passwd{password = Passwd}] -> - DigRes = if - Digest /= "" -> - Digest == DigestGen(Passwd); - true -> - false - end, - if DigRes -> - true; - true -> - (Passwd == Password) and (Password /= "") - end; - _ -> - false - end. - -%% @spec (User, Server, Password) -> ok | {error, invalid_jid} -%% User = string() -%% Server = string() -%% Password = string() - -set_password(User, Server, Password) -> - LUser = (catch exmpp_stringprep:nodeprep(User)), - LServer = (catch exmpp_stringprep:nameprep(Server)), - case {LUser, LServer} of - {{stringprep, _, invalid_string, _}, _} -> - {error, invalid_jid}; - {_, {stringprep, _, invalid_string, _}} -> - {error, invalid_jid}; - US -> - F = fun() -> - mnesia:write(#passwd{us = US, - password = Password}) - end, - {atomic, ok} = mnesia:transaction(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 = (catch exmpp_stringprep:nodeprep(User)), - LServer = (catch exmpp_stringprep:nameprep(Server)), - case {LUser, LServer} of - {{stringprep, _, invalid_string, _}, _} -> - {error, invalid_jid}; - {_, {stringprep, _, invalid_string, _}} -> - {error, invalid_jid}; - US -> - F = fun() -> - case mnesia:read({passwd, US}) of - [] -> - mnesia:write(#passwd{us = US, - password = Password}), - mnesia:dirty_update_counter( - reg_users_counter, - exmpp_jid:prep_domain(exmpp_jid:parse(Server)), 1), - ok; - [_E] -> - exists - end - end, - mnesia:transaction(F) - end. - -%% @spec () -> [{LUser, LServer}] -%% LUser = string() -%% LServer = string() -%% @doc Get all registered users in Mnesia. - -dirty_get_registered_users() -> - mnesia:dirty_all_keys(passwd). - -%% @spec (Server) -> [{LUser, LServer}] -%% Server = string() -%% LUser = string() -%% LServer = string() - -get_vh_registered_users(Server) -> - LServer = exmpp_stringprep:nameprep(Server), - mnesia:dirty_select( - passwd, - [{#passwd{us = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, LServer}], - ['$1']}]). - -%% @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: -%% - -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) -> - 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) - end; - -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}, {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) -> - 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) - 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) -> - 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) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read(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) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read(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) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - case catch mnesia:dirty_read({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) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - F = fun() -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, - exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1) - end, - mnesia:transaction(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) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - F = fun() -> - case mnesia:read({passwd, US}) of - [#passwd{password = Password}] -> - mnesia:delete({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 mnesia:transaction(F) of - {atomic, ok} -> - ok; - {atomic, Res} -> - Res; - _ -> - bad_request - end - catch - _ -> - bad_request - end. - -%% @spec () -> term() - -update_table() -> - Fields = record_info(fields, passwd), - case mnesia:table_info(passwd, attributes) of - Fields -> - % No conversion is needed when the table comes from an exmpp-less - % Ejabberd because ejabberd_auth* modules use string() and not - % binary(). - ok; - [user, password] -> - ?INFO_MSG("Converting passwd table from " - "{user, password} format", []), - Host = ?MYNAME, - {atomic, ok} = mnesia:create_table( - ejabberd_auth_internal_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, passwd}, - {attributes, record_info(fields, passwd)}]), - mnesia:transform_table(passwd, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(ejabberd_auth_internal_tmp_table), - mnesia:foldl( - fun(#passwd{us = U} = R, _) -> - mnesia:dirty_write( - ejabberd_auth_internal_tmp_table, - R#passwd{us = {U, Host}}) - end, ok, passwd) - end, - mnesia:transaction(F1), - mnesia:clear_table(passwd), - F2 = fun() -> - mnesia:write_lock_table(passwd), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, ejabberd_auth_internal_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(ejabberd_auth_internal_tmp_table); - _ -> - ?INFO_MSG("Recreating passwd table", []), - mnesia:transform_table(passwd, ignore, Fields) - end. - - - diff --git a/src/ejabberd_auth_odbc.erl b/src/ejabberd_auth_odbc.erl deleted file mode 100644 index e169da98d..000000000 --- a/src/ejabberd_auth_odbc.erl +++ /dev/null @@ -1,368 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_auth_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : Authentification via ODBC (obsolete) -%%% Created : 12 Dec 2004 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(ejabberd_auth_odbc). --author('alexey@process-one.net'). - -%% External exports --export([start/1, - stop/1, - set_password/3, - check_password/3, - check_password/5, - try_register/3, - dirty_get_registered_users/0, - get_vh_registered_users/1, - get_vh_registered_users/2, - get_vh_registered_users_number/1, - get_vh_registered_users_number/2, - get_password/2, - get_password_s/2, - is_user_exists/2, - remove_user/2, - remove_user/3, - plain_password_required/0 - ]). - --include("ejabberd.hrl"). - -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- - -%% @spec (Host) -> ok -%% Host = string() - -start(Host) -> - case ejabberd_odbc:running(Host) of - true -> ok; - false -> ejabberd_rdbms:start_odbc(Host) - end. - -stop(Host) -> - ejabberd_rdbms:stop_odbc(Host). - -%% @spec () -> bool() - -plain_password_required() -> - false. - -%% @spec (User, Server, Password) -> bool() -%% User = string() -%% Server = string() -%% Password = string() - -check_password(User, Server, Password) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - try odbc_queries:get_password(LServer, Username) of - {selected, ["password"], [{Password}]} -> - Password /= ""; %% Password is correct, and not empty - {selected, ["password"], [{_Password2}]} -> - false; %% Password is not correct - {selected, ["password"], []} -> - false; %% Account does not exist - {error, _Error} -> - false %% Typical error is that table doesn't exist - catch - _:_ -> - false %% Typical error is database not accessible - end - catch - _ -> - false - end. - -%% @spec (User, Server, Password, Digest, DigestGen) -> bool() -%% User = string() -%% Server = string() -%% Password = string() -%% Digest = string() -%% DigestGen = function() - -check_password(User, Server, Password, Digest, DigestGen) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - try odbc_queries:get_password(LServer, Username) of - %% Account exists, check if password is valid - {selected, ["password"], [{Passwd}]} -> - DigRes = if - Digest /= "" -> - Digest == DigestGen(Passwd); - true -> - false - end, - if DigRes -> - true; - true -> - (Passwd == Password) and (Password /= "") - end; - {selected, ["password"], []} -> - false; %% Account does not exist - {error, _Error} -> - false %% Typical error is that table doesn't exist - catch - _:_ -> - false %% Typical error is database not accessible - end - catch - _ -> - false - end. - -%% @spec (User, Server, Password) -> ok | {error, invalid_jid} -%% User = string() -%% Server = string() -%% Password = string() - -set_password(User, Server, Password) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - Pass = ejabberd_odbc:escape(Password), - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:set_password_t(LServer, Username, Pass) of - {atomic, ok} -> ok; - Other -> {error, Other} - end - catch - _ -> - {error, invalid_jid} - end. - - -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} -%% User = string() -%% Server = string() -%% Password = string() - -try_register(User, Server, Password) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - Pass = ejabberd_odbc:escape(Password), - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:add_user(LServer, Username, Pass) of - {updated, 1} -> - {atomic, ok}; - _ -> - {atomic, exists} - end - catch - _ -> - {error, invalid_jid} - end. - -%% @spec () -> [{LUser, LServer}] -%% LUser = string() -%% LServer = string() - -dirty_get_registered_users() -> - Servers = ejabberd_config:get_vh_by_auth_method(odbc), - lists:flatmap( - fun(Server) -> - get_vh_registered_users(Server) - end, Servers). - -%% @spec (Server) -> [{LUser, LServer}] -%% Server = string() -%% LUser = string() -%% LServer = string() - -get_vh_registered_users(Server) -> - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:list_users(LServer) of - {selected, ["username"], Res} -> - [{U, LServer} || {U} <- Res]; - _ -> - [] - end. - -%% @spec (Server, Opts) -> [{LUser, LServer}] -%% Server = string() -%% Opts = [{Opt, Val}] -%% Opt = atom() -%% Val = term() -%% LUser = string() -%% LServer = string() - -get_vh_registered_users(Server, Opts) -> - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:list_users(LServer, Opts) of - {selected, ["username"], Res} -> - [{U, LServer} || {U} <- Res]; - _ -> - [] - end. - -%% @spec (Server) -> Users_Number -%% Server = string() -%% Users_Number = integer() - -get_vh_registered_users_number(Server) -> - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:users_number(LServer) of - {selected, [_], [{Res}]} -> - list_to_integer(Res); - _ -> - 0 - end. - -%% @spec (Server, Opts) -> Users_Number -%% Server = string() -%% Opts = [{Opt, Val}] -%% Opt = atom() -%% Val = term() -%% Users_Number = integer() - -get_vh_registered_users_number(Server, Opts) -> - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:users_number(LServer, Opts) of - {selected, [_], [{Res}]} -> - list_to_integer(Res); - _Other -> - 0 - end. - -%% @spec (User, Server) -> Password | false -%% User = string() -%% Server = string() -%% Password = string() - -get_password(User, Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:get_password(LServer, Username) of - {selected, ["password"], [{Password}]} -> - Password; - _ -> - false - end - catch - _ -> - false - end. - -%% @spec (User, Server) -> Password | nil() -%% User = string() -%% Server = string() -%% Password = string() - -get_password_s(User, Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - case catch odbc_queries:get_password(LServer, Username) of - {selected, ["password"], [{Password}]} -> - Password; - _ -> - "" - end - catch - _ -> - "" - end. - -%% @spec (User, Server) -> true | false | {error, Error} -%% User = string() -%% Server = string() - -is_user_exists(User, Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - try odbc_queries:get_password(LServer, Username) of - {selected, ["password"], [{_Password}]} -> - true; %% Account exists - {selected, ["password"], []} -> - false; %% Account does not exist - {error, Error} -> - {error, Error} %% Typical error is that table doesn't exist - catch - _:B -> - {error, B} %% Typical error is database not accessible - end - catch - _ -> - false - end. - -%% @spec (User, Server) -> ok | error -%% User = string() -%% Server = string() -%% @doc Remove user. -%% Note: it may return ok even if there was some problem removing the user. - -remove_user(User, Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - LServer = exmpp_stringprep:nameprep(Server), - catch odbc_queries:del_user(LServer, Username), - ok - catch - _ -> - error - end. - -%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed -%% User = string() -%% Server = string() -%% Password = string() -%% @doc Remove user if the provided password is correct. - -remove_user(User, Server, Password) -> - try - LUser = exmpp_stringprep:nodeprep(User), - Username = ejabberd_odbc:escape(LUser), - Pass = ejabberd_odbc:escape(Password), - LServer = exmpp_stringprep:nameprep(Server), - F = fun() -> - Result = odbc_queries:del_user_return_password( - LServer, Username, Pass), - case Result of - {selected, ["password"], [{Password}]} -> - ok; - {selected, ["password"], []} -> - not_exists; - _ -> - not_allowed - end - end, - {atomic, Result} = odbc_queries:sql_transaction(LServer, F), - Result - catch - _ -> - error - end. diff --git a/src/mod_last_odbc.erl b/src/mod_last_odbc.erl deleted file mode 100644 index 975abe430..000000000 --- a/src/mod_last_odbc.erl +++ /dev/null @@ -1,198 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_last_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : jabber:iq:last support (XEP-0012) for ODBC (obsolete) -%%% Created : 24 Oct 2003 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_last_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([start/2, - stop/1, - process_local_iq/3, - process_sm_iq/3, - on_presence_update/4, - store_last_info/4, - get_last_info/2, - remove_user/2]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). --include("mod_privacy.hrl"). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - 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, - ?MODULE, process_sm_iq, IQDisc), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(unset_presence_hook, HostB, - ?MODULE, on_presence_update, 50). - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(unset_presence_hook, HostB, - ?MODULE, on_presence_update, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY). - -%%% -%%% Uptime of ejabberd node -%%% - -process_local_iq(_From, _To, #iq{type = get} = IQ_Rec) -> - Sec = get_node_uptime(), - Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', attrs = - [?XMLATTR(<<"seconds">>, Sec)]}, - exmpp_iq:result(IQ_Rec, Response); -process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) -> - exmpp_iq:error(IQ_Rec, 'not-allowed'). - -%% @spec () -> integer() -%% @doc Get the uptime of the ejabberd node, expressed in seconds. -%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime. -get_node_uptime() -> - case ejabberd_config:get_local_option(node_start) of - {_, _, _} = StartNow -> - now_to_seconds(now()) - now_to_seconds(StartNow); - _undefined -> - trunc(element(1, erlang:statistics(wall_clock))/1000) - end. - -now_to_seconds({MegaSecs, Secs, _MicroSecs}) -> - MegaSecs * 1000000 + Secs. - - -%%% -%%% Serve queries about user last online -%%% -process_sm_iq(From, To, #iq{type = get} = IQ_Rec) -> - User = exmpp_jid:prep_node_as_list(To), - Server = exmpp_jid:prep_domain_as_list(To), - {Subscription, _Groups} = - ejabberd_hooks:run_fold( - roster_get_jid_info, exmpp_jid:prep_domain(To), - {none, []}, [exmpp_jid:prep_node(To), exmpp_jid:prep_domain(To), From]), - if - (Subscription == both) or (Subscription == from) -> - UserListRecord = ejabberd_hooks:run_fold( - privacy_get_user_list, exmpp_jid:prep_domain(To), - #userlist{}, - [exmpp_jid:prep_node(To), exmpp_jid:prep_domain(To)]), - case ejabberd_hooks:run_fold( - privacy_check_packet, exmpp_jid:prep_domain(To), - allow, - [exmpp_jid:prep_node(To), exmpp_jid:prep_domain(To), UserListRecord, - {To, From, - exmpp_presence:available()}, - out]) of - allow -> - get_last(IQ_Rec, User, Server); - deny -> - exmpp_iq:error(IQ_Rec, 'not-allowed') - end; - true -> - exmpp_iq:error(IQ_Rec, 'not-allowed') - end; -process_sm_iq(_From, _To, #iq{type = set} = IQ_Rec) -> - exmpp_iq:error(IQ_Rec, 'not-allowed'). - -%% TODO: This function could use get_last_info/2 -get_last(IQ_Rec, LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - case catch odbc_queries:get_last(LServer, Username) of - {selected, ["seconds","state"], []} -> - exmpp_iq:error(IQ_Rec, 'service-unavailable'); - {selected, ["seconds","state"], [{STimeStamp, Status}]} -> - case catch list_to_integer(STimeStamp) of - TimeStamp when is_integer(TimeStamp) -> - TimeStamp2 = now_to_seconds(now()), - Sec = TimeStamp2 - TimeStamp, - Response = #xmlel{ns = ?NS_LAST_ACTIVITY, name = 'query', - attrs = [?XMLATTR(<<"seconds">>, Sec)], - children = [#xmlcdata{cdata = list_to_binary(Status)}]}, - exmpp_iq:result(IQ_Rec, Response); - _ -> - exmpp_iq:error(IQ_Rec, 'internal-server-error') - end; - _ -> - exmpp_iq:error(IQ_Rec, 'internal-server-error') - end. - -on_presence_update(User, Server, _Resource, Status) -> - TimeStamp = now_to_seconds(now()), - store_last_info(User, Server, TimeStamp, Status). - -store_last_info(User, Server, TimeStamp, Status) - when is_binary(User), is_binary(Server) -> - try - %LUser = exmpp_stringprep:nodeprep(User), - %LServer = exmpp_stringprep:nameprep(Server), - LUser = binary_to_list(User), - LServer = binary_to_list(Server), - - Username = ejabberd_odbc:escape(LUser), - Seconds = ejabberd_odbc:escape(integer_to_list(TimeStamp)), - State = ejabberd_odbc:escape(Status), - odbc_queries:set_last_t(LServer, Username, Seconds, State) - catch - _ -> - ok - end. - -%% @spec (LUser::string(), LServer::string()) -> -%% {ok, Timestamp::integer(), Status::string()} | not_found -get_last_info(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - case catch odbc_queries:get_last(LServer, Username) of - {selected, ["seconds","state"], []} -> - not_found; - {selected, ["seconds","state"], [{STimeStamp, Status}]} -> - case catch list_to_integer(STimeStamp) of - TimeStamp when is_integer(TimeStamp) -> - {ok, TimeStamp, Status}; - _ -> - not_found - end; - _ -> - not_found - end. - -remove_user(User, Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - Username = ejabberd_odbc:escape(LUser), - odbc_queries:del_last(LServer, Username) - catch - _ -> - ok - end. diff --git a/src/mod_offline_odbc.erl b/src/mod_offline_odbc.erl deleted file mode 100644 index 8d42f2369..000000000 --- a/src/mod_offline_odbc.erl +++ /dev/null @@ -1,582 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_offline_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : Store and manage offline messages in relational database (obsolete) -%%% Created : 5 Jan 2003 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_offline_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([count_offline_messages/2]). - --export([start/2, - loop/2, - stop/1, - store_packet/3, - pop_offline_messages/3, - get_sm_features/5, - remove_user/2, - get_queue_length/2, - webadmin_page/3, - webadmin_user/4, - webadmin_user_parse_query/5]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). --include("web/ejabberd_http.hrl"). --include("web/ejabberd_web_admin.hrl"). - --record(offline_msg, {user, server, timestamp, expire, from, to, packet}). - --define(PROCNAME, ejabberd_offline). --define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000). - -% These are the namespace already declared by the stream opening. This is -% used at serialization time. --define(DEFAULT_NS, ?NS_JABBER_CLIENT). --define(PREFIXED_NS, [{?NS_XMPP, ?NS_XMPP_pfx}]). - -%% default value for the maximum number of user messages --define(MAX_USER_MESSAGES, infinity). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - ejabberd_hooks:add(offline_message_hook, HostB, - ?MODULE, store_packet, 50), - ejabberd_hooks:add(resend_offline_messages_hook, HostB, - ?MODULE, pop_offline_messages, 50), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(anonymous_purge_hook, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(disco_sm_features, HostB, - ?MODULE, get_sm_features, 50), - ejabberd_hooks:add(disco_local_features, HostB, - ?MODULE, get_sm_features, 50), - ejabberd_hooks:add(webadmin_page_host, HostB, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, HostB, - ?MODULE, webadmin_user, 50), - ejabberd_hooks:add(webadmin_user_parse_query, HostB, - ?MODULE, webadmin_user_parse_query, 50), - AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages), - register(gen_mod:get_module_proc(Host, ?PROCNAME), - spawn(?MODULE, loop, [Host, AccessMaxOfflineMsgs])). - -loop(Host, AccessMaxOfflineMsgs) -> - receive - #offline_msg{user = User, server = Server} = Msg -> - Msgs = receive_all(User, Server, [Msg]), - Len = length(Msgs), - MaxOfflineMsgs = get_max_user_messages(AccessMaxOfflineMsgs, - User, Host), - - %% Only count existing messages if needed: - Count = if MaxOfflineMsgs =/= infinity -> - Len + count_offline_messages(User, Server); - true -> 0 - end, - if - Count > MaxOfflineMsgs -> - discard_warn_sender(Msgs); - true -> - Query = lists:map( - fun(M) -> - Username = - ejabberd_odbc:escape( - exmpp_jid:prep_node_as_list(M#offline_msg.to)), - From = M#offline_msg.from, - To = M#offline_msg.to, - Packet0 = exmpp_stanza:set_jids( - M#offline_msg.packet, - From, - To), - Packet1 = exmpp_xml:append_children( - Packet0, - [jlib:timestamp_to_xml( - calendar:now_to_universal_time( - M#offline_msg.timestamp), - utc, - exmpp_jid:make("", Host, ""), - "Offline Storage"), - %% TODO: Delete the next three lines once XEP-0091 is Obsolete - jlib:timestamp_to_xml( - calendar:now_to_universal_time( - M#offline_msg.timestamp))]), - XML = - ejabberd_odbc:escape( - exmpp_xml:document_to_list(Packet1)), - odbc_queries:add_spool_sql(Server, Username, XML) - end, Msgs), - case catch odbc_queries:add_spool(Server, Query) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~n", [Reason]); - {error, Reason} -> - ?ERROR_MSG("~p~n", [Reason]); - _ -> - ok - end - end, - loop(Host, AccessMaxOfflineMsgs); - _ -> - loop(Host, AccessMaxOfflineMsgs) - end. - -%% Function copied from ejabberd_sm.erl: -get_max_user_messages(AccessRule, LUser, Host) -> - case acl:match_rule( - Host, AccessRule, exmpp_jid:make(LUser, Host, "")) of - Max when is_integer(Max) -> Max; - infinity -> infinity; - _ -> ?MAX_USER_MESSAGES - end. - -receive_all(Username, Server, Msgs) -> - receive - #offline_msg{user = Username, server = Server} = Msg -> - receive_all(Username, Server, [Msg | Msgs]) - after 0 -> - lists:reverse(Msgs) - end. - - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(offline_message_hook, HostB, - ?MODULE, store_packet, 50), - ejabberd_hooks:delete(resend_offline_messages_hook, HostB, - ?MODULE, pop_offline_messages, 50), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(anonymous_purge_hook, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(disco_sm_features, HostB, ?MODULE, get_sm_features, 50), - ejabberd_hooks:delete(disco_local_features, HostB, ?MODULE, get_sm_features, 50), - ejabberd_hooks:delete(webadmin_page_host, HostB, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, HostB, - ?MODULE, webadmin_user, 50), - ejabberd_hooks:delete(webadmin_user_parse_query, HostB, - ?MODULE, webadmin_user_parse_query, 50), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - exit(whereis(Proc), stop), - ok. - -get_sm_features(Acc, _From, _To, "", _Lang) -> - Feats = case Acc of - {result, I} -> I; - _ -> [] - end, - {result, Feats ++ [?NS_MSGOFFLINE]}; - -get_sm_features(_Acc, _From, _To, ?NS_MSGOFFLINE, _Lang) -> - %% override all lesser features... - {result, []}; - -get_sm_features(Acc, _From, _To, _Node, _Lang) -> - Acc. - - -store_packet(From, To, Packet) -> - Type = exmpp_stanza:get_type(Packet), - if - (Type /= <<"error">>) and (Type /= <<"groupchat">>) and - (Type /= <<"headline">>) -> - case check_event_chatstates(From, To, Packet) of - true -> - LUser = exmpp_jid:prep_node_as_list(To), - LServer = exmpp_jid:prep_domain_as_list(To), - TimeStamp = now(), - Expire = find_x_expire(TimeStamp, Packet#xmlel.children), - Proc1 = gen_mod:get_module_proc(LServer, ?PROCNAME), - Proc = case whereis(Proc1) of - undefined -> - gen_mod:get_module_proc(global, ?PROCNAME); - _ -> - Proc1 - end, - Proc ! - #offline_msg{user = LUser, - server = LServer, - timestamp = TimeStamp, - expire = Expire, - from = From, - to = To, - packet = Packet}, - stop; - _ -> - ok - end; - true -> - ok - end. - -%% Check if the packet has any content about XEP-0022 or XEP-0085 -check_event_chatstates(From, To, Packet) -> - case find_x_event_chatstates(Packet#xmlel.children, {false, false, false}) of - %% There wasn't any x:event or chatstates subelements - {false, false, _} -> - true; - %% There a chatstates subelement and other stuff, but no x:event - {false, CEl, true} when CEl /= false -> - true; - %% There was only a subelement: a chatstates - {false, CEl, false} when CEl /= false -> - %% Don't allow offline storage - false; - %% There was an x:event element, and maybe also other stuff - {El, _, _} when El /= false-> - case exmpp_xml:get_element(El, 'id') of - undefined -> - case exmpp_xml:get_element(El, 'offline') of - undefined -> - true; - _ -> - ID = case exmpp_stanza:get_id(Packet) of - undefined -> - #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id'}; - S -> - #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'id', - children = [#xmlcdata{cdata = - S}]} - end, - X = #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'x', children = - [ID, #xmlel{ns = ?NS_MESSAGE_EVENT, name = 'offline'}]}, - ejabberd_router:route( - To, From, exmpp_xml:set_children(Packet, [X])), - true - end; - _ -> - false - end - end. - -%% Check if the packet has subelements about XEP-0022, XEP-0085 or other -find_x_event_chatstates([], Res) -> - Res; -find_x_event_chatstates([#xmlel{ns = ?NS_MESSAGE_EVENT} = El | Els], {_, B, C}) -> - find_x_event_chatstates(Els, {El, B, C}); -find_x_event_chatstates([#xmlel{ns = ?NS_CHATSTATES} = El | Els], {A, _, C}) -> - find_x_event_chatstates(Els, {A, El, C}); -find_x_event_chatstates([#xmlcdata{} = _ | Els], {A, B, C}) -> - find_x_event_chatstates(Els, {A, B, C}); -find_x_event_chatstates([_ | Els], {A, B, _}) -> - find_x_event_chatstates(Els, {A, B, true}). - -find_x_expire(_, []) -> - never; -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; - Int when Int > 0 -> - {MegaSecs, Secs, MicroSecs} = TimeStamp, - S = MegaSecs * 1000000 + Secs + Int, - MegaSecs1 = S div 1000000, - Secs1 = S rem 1000000, - {MegaSecs1, Secs1, MicroSecs}; - _ -> - never - end; -find_x_expire(TimeStamp, [_ | Els]) -> - find_x_expire(TimeStamp, Els). - - -pop_offline_messages(Ls, User, Server) - when is_binary(User), is_binary(Server) -> - try - LUser = binary_to_list(User), - LServer = binary_to_list(Server), - EUser = ejabberd_odbc:escape(LUser), - case odbc_queries:get_and_del_spool_msg_t(LServer, EUser) of - {atomic, {selected, ["username","xml"], Rs}} -> - Ls ++ lists:flatmap( - fun({_, XML}) -> - try - [El] = exmpp_xml:parse_document(XML, - [names_as_atom, {check_elems, xmpp}, - {check_nss,xmpp} ]), - To = exmpp_jid:parse( - exmpp_stanza:get_recipient(El)), - From = exmpp_jid:parse( - exmpp_stanza:get_sender(El)), - [{route, From, To, El}] - catch - _ -> - [] - end - end, Rs); - _ -> - Ls - end - catch - _ -> - [] - end. - - -remove_user(User, Server) - when is_binary(User), is_binary(Server) -> - try - LUser = binary_to_list(exmpp_stringprep:nodeprep(User)), - LServer = binary_to_list(exmpp_stringprep:nameprep(Server)), - Username = ejabberd_odbc:escape(LUser), - odbc_queries:del_spool_msg(LServer, Username) - catch - _ -> - ok - end. - - -%% Helper functions: - -%% TODO: Warning - This function is a duplicate from mod_offline.erl -%% It is duplicate to stay consistent (many functions are duplicated -%% in this module). It will be refactored later on. -%% Warn senders that their messages have been discarded: -discard_warn_sender(Msgs) -> - lists:foreach( - fun(#offline_msg{from=From, to=To, packet=Packet}) -> - ErrText = "Your contact offline message queue is full. The message has been discarded.", - Error = exmpp_stanza:error(Packet#xmlel.ns, 'resource-constraint', - {"en", ErrText}), - Err = exmpp_stanza:reply_with_error(Packet, Error), - ejabberd_router:route( - To, - From, Err) - end, Msgs). - - -webadmin_page(_, Host, - #request{us = _US, - path = ["user", U, "queue"], - q = Query, - lang = Lang} = _Request) -> - Res = user_queue(U, Host, Query, Lang), - {stop, Res}; - -webadmin_page(Acc, _, _) -> Acc. - -user_queue(User, Server, Query, Lang) -> - {US, MsgsAll, Res} = try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - US0 = {LUser, LServer}, - R = user_queue_parse_query(Username, LServer, Query), - M = case catch ejabberd_odbc:sql_query( - LServer, - ["select username, xml from spool" - " where username='", Username, "'" - " and host='", Host, "'" - " order by seq;"]) of - {selected, ["username", "xml"], Rs} -> - lists:flatmap( - fun({_, XML}) -> - try exmpp_xml:parse_document(XML, - [names_as_atom, {check_elems, xmpp}, - {check_nss,xmpp}]) of - [El] -> - [El] - catch - _ -> - [] - end - end, Rs); - _ -> - [] - end, - {US0, M, R} - catch - _ -> - {{"invalid", "invalid"}, [], nothing} - end, - Msgs = get_messages_subset(User, Server, MsgsAll), - FMsgs = - lists:map( - fun(#xmlel{} = Msg) -> - ID = jlib:encode_base64(binary_to_list(term_to_binary(Msg))), - Packet = Msg, - FPacket = exmpp_xml:node_to_list( - exmpp_xml:indent_document(Packet, <<" ">>), - [?DEFAULT_NS], ?PREFIXED_NS), - ?XE("tr", - [?XAE("td", [?XMLATTR(<<"class">>, <<"valign">>)], [?INPUT("checkbox", "selected", ID)]), - ?XAE("td", [?XMLATTR(<<"class">>, <<"valign">>)], [?XC("pre", FPacket)])] - ) - end, Msgs), - [?XC("h1", io_lib:format(?T("~s's Offline Messages Queue"), - [us_to_list(US)]))] ++ - case Res of - ok -> [?XREST("Submitted")]; - nothing -> [] - end ++ - [?XAE("form", [?XMLATTR(<<"action">>, <<"">>), ?XMLATTR(<<"method">>, <<"post">>)], - [?XE("table", - [?XE("thead", - [?XE("tr", - [?X("td"), - ?XCT("td", "Packet") - ])]), - ?XE("tbody", - if - FMsgs == [] -> - [?XE("tr", - [?XAC("td", [?XMLATTR(<<"colspan">>, <<"4">>)], " ")] - )]; - true -> - FMsgs - end - )]), - ?BR, - ?INPUTT("submit", "delete", "Delete Selected") - ])]. - -user_queue_parse_query(Username, LServer, Query) -> - case lists:keysearch("delete", 1, Query) of - {value, _} -> - Host = ejabberd_odbc:escape(LServer), - Msgs = case catch ejabberd_odbc:sql_query( - LServer, - ["select xml, seq from spool" - " where username='", Username, "' and" - " host='", Host, "'" - " order by seq;"]) of - {selected, ["xml", "seq"], Rs} -> - lists:flatmap( - fun({XML, Seq}) -> - try exmpp_xml:parse_document(XML, - [names_as_atom, {check_elems, xmpp}, - {check_nss,xmpp} ]) of - [El] -> - [{El, Seq}] - catch - _ -> - [] - end - end, Rs); - _ -> - [] - end, - F = fun() -> - lists:foreach( - fun({Msg, Seq}) -> - ID = jlib:encode_base64( - binary_to_list(term_to_binary(Msg))), - case lists:member({"selected", ID}, Query) of - true -> - SSeq = ejabberd_odbc:escape(Seq), - catch ejabberd_odbc:sql_query( - LServer, - ["delete from spool" - " where username='", Username, "' and host='", Host, "'" - " and seq='", SSeq, "';"]); - false -> - ok - end - end, Msgs) - end, - mnesia:transaction(F), - ok; - false -> - nothing - end. - -us_to_list({User, Server}) -> - exmpp_jid:to_list(User, Server). - -get_queue_length(Username, LServer) -> - Host = ejabberd_odbc:escape(LServer), - case catch ejabberd_odbc:sql_query( - LServer, - ["select count(*) from spool" - " where username='", Username, "'" - " and host='", Host, "';"]) of - {selected, [_], [{SCount}]} -> - SCount; - _ -> - 0 - end. - -get_messages_subset(User, Host, MsgsAll) -> - Access = gen_mod:get_module_opt(Host, ?MODULE, access_max_user_messages, - max_user_offline_messages), - MaxOfflineMsgs = case get_max_user_messages(Access, User, Host) of - Number when is_integer(Number) -> Number; - _ -> 100 - end, - Length = length(MsgsAll), - get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll). - -get_messages_subset2(Max, Length, MsgsAll) when Length =< Max*2 -> - MsgsAll; -get_messages_subset2(Max, Length, MsgsAll) -> - FirstN = Max, - {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll), - MsgsLastN = lists:nthtail(Length - FirstN - FirstN, Msgs2), - IntermediateMsg = exmpp_xml:element("..."), - MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN. - -webadmin_user(Acc, User, Server, Lang) -> - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - Username = ejabberd_odbc:escape(LUser), - QueueLen = get_queue_length(Username, LServer), - FQueueLen = [?AC("queue/", QueueLen)], - Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")]. - -webadmin_user_parse_query(_, "removealloffline", User, Server, _Query) -> - case catch odbc_queries:del_spool_msg(Server, User) of - {'EXIT', Reason} -> - ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]), - {stop, error}; - {error, Reason} -> - ?ERROR_MSG("Failed to remove offline messages: ~p", [Reason]), - {stop, error}; - _ -> - ?INFO_MSG("Removed all offline messages for ~s@~s", [User, Server]), - {stop, ok} - end; -webadmin_user_parse_query(Acc, _Action, _User, _Server, _Query) -> - Acc. - -%% ------------------------------------------------ -%% mod_offline: number of messages quota management - -%% Returns as integer the number of offline messages for a given user -count_offline_messages(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - case catch odbc_queries:count_records_where( - LServer, "spool", "where username='" ++ Username ++ - "' and host='" ++ Host ++ "'") of - {selected, [_], [{Res}]} -> - list_to_integer(Res); - _ -> - 0 - end. diff --git a/src/mod_privacy_odbc.erl b/src/mod_privacy_odbc.erl deleted file mode 100644 index 10a04aac7..000000000 --- a/src/mod_privacy_odbc.erl +++ /dev/null @@ -1,850 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_privacy_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : jabber:iq:privacy support for ODBC database (obsolete) -%%% Created : 5 Oct 2006 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_privacy_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([start/2, stop/1, - process_iq/3, - process_iq_set/4, - process_iq_get/5, - get_user_list/3, - check_packet/6, - remove_user/2, - updated_list/3]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). --include("mod_privacy.hrl"). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - ejabberd_hooks:add(privacy_iq_get, HostB, - ?MODULE, process_iq_get, 50), - ejabberd_hooks:add(privacy_iq_set, HostB, - ?MODULE, process_iq_set, 50), - ejabberd_hooks:add(privacy_get_user_list, HostB, - ?MODULE, get_user_list, 50), - ejabberd_hooks:add(privacy_check_packet, HostB, - ?MODULE, check_packet, 50), - ejabberd_hooks:add(privacy_updated_list, HostB, - ?MODULE, updated_list, 50), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY, - ?MODULE, process_iq, IQDisc). - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(privacy_iq_get, HostB, - ?MODULE, process_iq_get, 50), - ejabberd_hooks:delete(privacy_iq_set, HostB, - ?MODULE, process_iq_set, 50), - ejabberd_hooks:delete(privacy_get_user_list, HostB, - ?MODULE, get_user_list, 50), - ejabberd_hooks:delete(privacy_check_packet, HostB, - ?MODULE, check_packet, 50), - ejabberd_hooks:delete(privacy_updated_list, HostB, - ?MODULE, updated_list, 50), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVACY). - -process_iq(_From, _To, IQ_Rec) -> - exmpp_iq:error(IQ_Rec, 'not-allowed'). - - -process_iq_get(_, From, _To, #iq{payload = SubEl}, - #userlist{name = Active}) -> - LUser = exmpp_jid:prep_node_as_list(From), - LServer = exmpp_jid:prep_domain_as_list(From), - case exmpp_xml:get_child_elements(SubEl) of - [] -> - process_lists_get(LUser, LServer, Active); - [#xmlel{name = Name} = Child] -> - case Name of - list -> - ListName = exmpp_xml:get_attribute_as_list(Child, <<"name">>, false), - process_list_get(LUser, LServer, ListName); - _ -> - {error, 'bad-request'} - end; - _ -> - {error, 'bad-request'} - end. - - -process_lists_get(LUser, LServer, Active) -> - Default = case catch sql_get_default_privacy_list(LUser, LServer) of - {selected, ["name"], []} -> - none; - {selected, ["name"], [{DefName}]} -> - DefName; - _ -> - none - end, - case catch sql_get_privacy_list_names(LUser, LServer) of - {selected, ["name"], []} -> - {result, #xmlel{ns = ?NS_PRIVACY, name = 'query'}}; - {selected, ["name"], Names} -> - LItems = lists:map( - fun({N}) -> - exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = list}, <<"name">>, N) - end, Names), - DItems = - case Default of - none -> - LItems; - _ -> - [exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = default}, <<"name">>, Default) | LItems] - end, - ADItems = - case Active of - none -> - DItems; - _ -> - [exmpp_xml:set_attribute(#xmlel{ns = ?NS_PRIVACY, name = active}, <<"name">>, Active) | DItems] - end, - {result, #xmlel{ns = ?NS_PRIVACY, name = 'query', children = ADItems}}; - _ -> - {error, 'internal-server-error'} - end. - -process_list_get(_LUser, _LServer, false) -> - {error, 'bad-request'}; - -process_list_get(LUser, LServer, Name) -> - case catch sql_get_privacy_list_id(LUser, LServer, Name) of - {selected, ["id"], []} -> - {error, 'item-not-found'}; - {selected, ["id"], [{ID}]} -> - case catch sql_get_privacy_list_data_by_id(ID, LServer) of - {selected, ["t", "value", "action", "ord", "match_all", - "match_iq", "match_message", - "match_presence_in", "match_presence_out"], - RItems} -> - Items = lists:map(fun raw_to_item/1, RItems), - LItems = lists:map(fun item_to_xml/1, Items), - ListEl = exmpp_xml:set_attribute(#xmlel{name = list, - ns = ?NS_PRIVACY, - children = LItems}, - <<"name">>, - Name), - {result, #xmlel{ns = ?NS_PRIVACY, name = 'query', children = [ListEl]}}; - _ -> - {error, 'internal-server-error'} - end; - _ -> - {error, 'internal-server-error'} - end. - - -item_to_xml(Item) -> - Attrs1 = [?XMLATTR(<<"action">>, action_to_binary(Item#listitem.action)), - ?XMLATTR(<<"order">>, order_to_binary(Item#listitem.order))], - Attrs2 = case Item#listitem.type of - none -> - Attrs1; - Type -> - [?XMLATTR(<<"type">>, type_to_binary(Item#listitem.type)), - ?XMLATTR(<<"value">>, value_to_binary(Type, Item#listitem.value)) | - Attrs1] - end, - SubEls = case Item#listitem.match_all of - true -> - []; - false -> - SE1 = case Item#listitem.match_iq of - true -> - [#xmlel{ns = ?NS_PRIVACY, name = iq}]; - false -> - [] - end, - SE2 = case Item#listitem.match_message of - true -> - [#xmlel{ns = ?NS_PRIVACY, name = message} | SE1]; - false -> - SE1 - end, - SE3 = case Item#listitem.match_presence_in of - true -> - [#xmlel{ns = ?NS_PRIVACY, name = 'presence-in'} | SE2]; - false -> - SE2 - end, - SE4 = case Item#listitem.match_presence_out of - true -> - [#xmlel{ns = ?NS_PRIVACY, name = 'presence-out'} | SE3]; - false -> - SE3 - end, - SE4 - end, - exmpp_xml:set_attributes(#xmlel{ns = ?NS_PRIVACY, name = item, children = SubEls}, Attrs2). - - -action_to_binary(Action) -> - case Action of - allow -> <<"allow">>; - deny -> <<"deny">> - end. - -order_to_binary(Order) -> - list_to_binary(integer_to_list(Order)). - -type_to_binary(Type) -> - case Type of - jid -> <<"jid">>; - group -> <<"group">>; - 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) -> - case S of - "allow" -> allow; - "deny" -> deny - end. - - - -process_iq_set(_, From, _To, #iq{payload = SubEl}) -> - LUser = exmpp_jid:prep_node_as_list(From), - LServer = exmpp_jid:prep_domain_as_list(From), - case exmpp_xml:get_child_elements(SubEl) of - [#xmlel{name = Name} = Child] -> - ListName = exmpp_xml:get_attribute_as_list(Child, <<"name">>, false), - case Name of - list -> - process_list_set(LUser, LServer, ListName, - exmpp_xml:get_child_elements(Child)); - active -> - process_active_set(LUser, LServer, ListName); - default -> - process_default_set(LUser, LServer, ListName); - _ -> - {error, 'bad-request'} - end; - _ -> - {error, 'bad-request'} - end. - - -process_default_set(LUser, LServer, false) -> - case catch sql_unset_default_privacy_list(LUser, LServer) of - {'EXIT', _Reason} -> - {error, 'internal_server_error'}; - {error, _Reason} -> - {error, 'internal_server_error'}; - _ -> - {result, []} - end; - -process_default_set(LUser, LServer, Name) -> - F = fun() -> - case sql_get_privacy_list_names_t(LUser, LServer) of - {selected, ["name"], []} -> - {error, 'item-not-found'}; - {selected, ["name"], Names} -> - case lists:member({Name}, Names) of - true -> - sql_set_default_privacy_list(LUser, LServer, Name), - {result, []}; - false -> - {error, 'item-not-found'} - end - end - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, _} = Res} -> - Res; - _ -> - {error, 'internal-server-error'} - end. - - -process_active_set(_LUser, _LServer, false) -> - {result, [], #userlist{}}; - -process_active_set(LUser, LServer, Name) -> - case catch sql_get_privacy_list_id(LUser, LServer, Name) of - {selected, ["id"], []} -> - {error, 'item-not-found'}; - {selected, ["id"], [{ID}]} -> - case catch sql_get_privacy_list_data_by_id(ID, LServer) of - {selected, ["t", "value", "action", "ord", "match_all", - "match_iq", "match_message", - "match_presence_in", "match_presence_out"], - RItems} -> - Items = lists:map(fun raw_to_item/1, RItems), - NeedDb = is_list_needdb(Items), - {result, [], #userlist{name = Name, list = Items, needdb = NeedDb}}; - _ -> - {error, 'internal-server-error'} - end; - _ -> - {error, 'internal_server_error'} - end. - - - -process_list_set(_LUser, _LServer, false, _Els) -> - {error, 'bad-request'}; - -process_list_set(LUser, LServer, Name, Els) -> - ?DEBUG("~p@~p: name ~p, els: ~p", [LUser, LServer, Name, Els]), - case parse_items(Els) of - false -> - {error, 'bad-request'}; - remove -> - F = - fun() -> - case sql_get_default_privacy_list_t(LUser, LServer) of - {selected, ["name"], []} -> - ?DEBUG("Going to delete privacy list ~p", [Name]), - sql_remove_privacy_list(LUser, LServer, Name), - {result, []}; - {selected, ["name"], [{Default}]} -> - % TODO: check active - if - Name == Default -> - {error, 'conflict'}; - true -> - ?DEBUG("Going to delete default list named ~p", [Name]), - sql_remove_privacy_list(LUser, LServer, Name), - {result, []} - end - end - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, _} = Res} -> - ListString = lists:flatten(io_lib:format("~p.", [#userlist{name = Name, list = []}])), - ejabberd_router:route( - exmpp_jid:make(LUser, LServer), - exmpp_jid:make(LUser, LServer), - #xmlel{name = 'broadcast', ns = privacy_list, - attrs = [?XMLATTR(<<"list_name">>, Name)], - children = [exmpp_xml:cdata(ListString)]}), - Res; - _ -> - {error, 'internal-server-error'} - end; - List -> - RItems = lists:map(fun item_to_raw/1, List), - ?DEBUG("Got a list of items: ~p", [RItems]), - F = - fun() -> - ?DEBUG("About to grab the id...", []), - ID = - case sql_get_privacy_list_id_t(LUser, LServer, Name) of - {selected, ["id"], []} -> - ?DEBUG("No privacy list called ~p",[Name]), - sql_add_privacy_list(LUser, LServer, Name), - ?DEBUG("Added the privacy list called ~p",[Name]), - {selected, ["id"], [{I}]} = - sql_get_privacy_list_id_t(LUser, LServer, Name), - ?DEBUG("Privacy list called ~p added, now id ~p",[Name, I]), - I; - {selected, ["id"], [{I}]} -> - I - end, - sql_set_privacy_list(ID, RItems), - {result, []} - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, _} = Res} -> - ListString = lists:flatten(io_lib:format("~p.", [#userlist{name = Name, list = List}])), - ejabberd_router:route( - exmpp_jid:make(LUser, LServer), - exmpp_jid:make(LUser, LServer), - #xmlel{name = 'broadcast', ns = privacy_list, - attrs = [?XMLATTR(<<"list_name">>, Name)], - children = [exmpp_xml:cdata(ListString)]}), - Res; - _ -> - {error, 'internal_server_error'} - end - end. - - - -parse_items([]) -> - remove; -parse_items(Els) -> - parse_items(Els, []). - -parse_items([], Res) -> - %% Sort the items by their 'order' attribute - lists:keysort(#listitem.order, Res); -parse_items([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), - 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 - {'EXIT', _} -> false; - Val -> Val - end, - Order = case catch list_to_integer(SOrder) of - {'EXIT', _} -> - false; - IntVal -> - if - IntVal >= 0 -> - IntVal; - true -> - false - end - end, - if - (Action /= false) and (Order /= false) -> - I1 = #listitem{action = Action, order = Order}, - I2 = case {Type, Value} of - {T, V} when is_list(T), is_list(V) -> - case T of - "jid" -> - try - JID = exmpp_jid:parse(V), - I1#listitem{ - type = jid, - value = jlib:short_prepd_jid(JID)} - catch - _ -> - false - end; - "group" -> - I1#listitem{type = group, - value = V}; - "subscription" -> - case V of - "none" -> - I1#listitem{type = subscription, - value = none}; - "both" -> - I1#listitem{type = subscription, - value = both}; - "from" -> - I1#listitem{type = subscription, - value = from}; - "to" -> - I1#listitem{type = subscription, - value = to}; - _ -> - false - end - end; - {T, false} when is_list(T) -> - false; - _ -> - I1 - end, - case I2 of - false -> - false; - _ -> - case parse_matches(I2, exmpp_xml:get_child_elements(El)) of - false -> - false; - I3 -> - parse_items(Els, [I3 | Res]) - end - end; - true -> - false - end; - -parse_items(_, _Res) -> - false. - - -parse_matches(Item, []) -> - Item#listitem{match_all = true}; -parse_matches(Item, Els) -> - parse_matches1(Item, Els). - -parse_matches1(Item, []) -> - Item; -parse_matches1(Item, [#xmlel{name = message} | Els]) -> - parse_matches1(Item#listitem{match_message = true}, Els); -parse_matches1(Item, [#xmlel{name = iq} | Els]) -> - parse_matches1(Item#listitem{match_iq = true}, Els); -parse_matches1(Item, [#xmlel{name = 'presence-in'} | Els]) -> - parse_matches1(Item#listitem{match_presence_in = true}, Els); -parse_matches1(Item, [#xmlel{name = 'presence-out'} | Els]) -> - parse_matches1(Item#listitem{match_presence_out = true}, Els); -parse_matches1(_Item, [#xmlel{} | _Els]) -> - false. - - - - - -is_list_needdb(Items) -> - lists:any( - fun(X) -> - case X#listitem.type of - subscription -> true; - group -> true; - _ -> false - end - end, Items). - -get_user_list(_, User, Server) - when is_binary(User), is_binary(Server) -> - try - LUser = binary_to_list(User), - LServer = binary_to_list(Server), - case catch sql_get_default_privacy_list(LUser, LServer) of - {selected, ["name"], []} -> - #userlist{}; - {selected, ["name"], [{Default}]} -> - case catch sql_get_privacy_list_data(LUser, LServer, Default) of - {selected, ["t", "value", "action", "ord", "match_all", - "match_iq", "match_message", - "match_presence_in", "match_presence_out"], - RItems} -> - Items = lists:map(fun raw_to_item/1, RItems), - NeedDb = is_list_needdb(Items), - #userlist{name = Default, list = Items, needdb = NeedDb}; - _ -> - #userlist{} - end; - _ -> - #userlist{} - end - catch - _ -> - #userlist{} - end. - - -check_packet(_, User, Server, - #userlist{list = List, needdb = NeedDb}, - {From, To, #xmlel{name = PName}}, - Dir) when - PName =:= message ; - PName =:= iq ; - PName =:= presence -> - case List of - [] -> - allow; - _ -> - case {PName, Dir} of - {message, in} -> - LJID = jlib:short_prepd_jid(From), - {Subscription, Groups} = - case NeedDb of - true -> ejabberd_hooks:run_fold(roster_get_jid_info, Server, {none, []}, [User, Server, LJID]); - false -> {[], []} - end, - check_packet_aux(List, message, - LJID, Subscription, Groups); - {iq, in} -> - LJID = jlib:short_prepd_jid(From), - {Subscription, Groups} = - case NeedDb of - true -> ejabberd_hooks:run_fold(roster_get_jid_info, Server, {none, []}, [User, Server, LJID]); - false -> {[], []} - end, - check_packet_aux(List, iq, - LJID, Subscription, Groups); - {presence, in} -> - LJID = jlib:short_prepd_jid(From), - {Subscription, Groups} = - case NeedDb of - true -> ejabberd_hooks:run_fold(roster_get_jid_info, Server, {none, []}, [User, Server, LJID]); - false -> {[], []} - end, - check_packet_aux(List, presence_in, - LJID, Subscription, Groups); - {presence, out} -> - LJID = jlib:short_prepd_jid(To), - {Subscription, Groups} = - case NeedDb of - true -> ejabberd_hooks:run_fold(roster_get_jid_info, exmpp_stringprep:nameprep(Server), {none, []}, [User, Server, LJID]); - false -> {[], []} - end, - check_packet_aux(List, presence_out, - LJID, Subscription, Groups); - _ -> - allow - end - end. - -check_packet_aux([], _PType, _JID, _Subscription, _Groups) -> - allow; -check_packet_aux([Item | List], PType, JID, Subscription, Groups) -> - #listitem{type = Type, value = Value, action = Action} = Item, - case is_ptype_match(Item, PType) of - true -> - case Type of - none -> - Action; - _ -> - case is_type_match(Type, Value, - JID, Subscription, Groups) of - true -> - Action; - false -> - check_packet_aux(List, PType, - JID, Subscription, Groups) - end - end; - false -> - check_packet_aux(List, PType, JID, Subscription, Groups) - end. - - -is_ptype_match(Item, PType) -> - case Item#listitem.match_all of - true -> - true; - false -> - case PType of - message -> - Item#listitem.match_message; - iq -> - Item#listitem.match_iq; - presence_in -> - Item#listitem.match_presence_in; - presence_out -> - Item#listitem.match_presence_out - end - end. - - -%% TODO: Investigate this: sometimes Value has binaries, other times has strings -is_type_match(jid, Value, JIDtuple, _Subscription, _Groups) -> - {User, Server, Resource} = Value, - JID = exmpp_jid:make(JIDtuple), - ((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) -> - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - sql_del_privacy_lists(binary_to_list(LUser), binary_to_list(LServer)). - - -updated_list(_, - #userlist{name = OldName} = Old, - #userlist{name = NewName} = New) -> - if - OldName == NewName -> - New; - true -> - Old - end. - - -raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ, - SMatchMessage, SMatchPresenceIn, SMatchPresenceOut}) -> - {Type, Value} = - case SType of - "n" -> - {none, none}; - "j" -> - JID = exmpp_jid:parse(SValue), - {jid, jlib:short_prepd_jid(JID)}; - "g" -> - {group, SValue}; - "s" -> - case SValue of - "none" -> - {subscription, none}; - "both" -> - {subscription, both}; - "from" -> - {subscription, from}; - "to" -> - {subscription, to} - end - end, - Action = - case SAction of - "a" -> allow; - "d" -> deny - end, - Order = list_to_integer(SOrder), - MatchAll = ejabberd_odbc:to_bool(SMatchAll), - MatchIQ = ejabberd_odbc:to_bool(SMatchIQ), - MatchMessage = ejabberd_odbc:to_bool(SMatchMessage), - MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn), - MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut), - #listitem{type = Type, - value = Value, - action = Action, - order = Order, - match_all = MatchAll, - match_iq = MatchIQ, - match_message = MatchMessage, - match_presence_in = MatchPresenceIn, - match_presence_out = MatchPresenceOut - }. - -item_to_raw(#listitem{type = Type, - value = Value, - action = Action, - order = Order, - match_all = MatchAll, - match_iq = MatchIQ, - match_message = MatchMessage, - match_presence_in = MatchPresenceIn, - match_presence_out = MatchPresenceOut - }) -> - {SType, SValue} = - case Type of - none -> - {"n", ""}; - jid -> - {N0, D0, R0} = Value, - {"j", exmpp_jid:to_list(N0, D0, R0)}; - group -> - {"g", Value}; - subscription -> - case Value of - none -> - {"s", "none"}; - both -> - {"s", "both"}; - from -> - {"s", "from"}; - to -> - {"s", "to"} - end - end, - SAction = - case Action of - allow -> "a"; - deny -> "d" - end, - SOrder = integer_to_list(Order), - SMatchAll = if MatchAll -> "1"; true -> "0" end, - SMatchIQ = if MatchIQ -> "1"; true -> "0" end, - SMatchMessage = if MatchMessage -> "1"; true -> "0" end, - SMatchPresenceIn = if MatchPresenceIn -> "1"; true -> "0" end, - SMatchPresenceOut = if MatchPresenceOut -> "1"; true -> "0" end, - [SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ, - SMatchMessage, SMatchPresenceIn, SMatchPresenceOut]. - -sql_get_default_privacy_list(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - odbc_queries:get_default_privacy_list(LServer, Username). - -sql_get_default_privacy_list_t(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - odbc_queries:get_default_privacy_list_t(LServer, Username). - -sql_get_privacy_list_names(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - odbc_queries:get_privacy_list_names(LServer, Username). - -sql_get_privacy_list_names_t(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - odbc_queries:get_privacy_list_names_t(LServer, Username). - -sql_get_privacy_list_id(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:get_privacy_list_id(Host, Username, SName). - -sql_get_privacy_list_id_t(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:get_privacy_list_id_t(Host, Username, SName). - -sql_get_privacy_list_data(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:get_privacy_list_data(Host, Username, SName). - -sql_get_privacy_list_data_by_id(ID, LServer) -> - odbc_queries:get_privacy_list_data_by_id(LServer, ID). - -sql_set_default_privacy_list(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:set_default_privacy_list(Host, Username, SName). - -sql_unset_default_privacy_list(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - odbc_queries:unset_default_privacy_list(LServer, Username). - -sql_remove_privacy_list(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:remove_privacy_list(Host, Username, SName). - -sql_add_privacy_list(LUser, LServer, Name) -> - Username = ejabberd_odbc:escape(LUser), - Host = ejabberd_odbc:escape(LServer), - SName = ejabberd_odbc:escape(Name), - odbc_queries:add_privacy_list(Host, Username, SName). - -sql_set_privacy_list(ID, RItems) -> - odbc_queries:set_privacy_list(ID, RItems). - -sql_del_privacy_lists(LUser, LServer) -> - Username = ejabberd_odbc:escape(LUser), - Server = ejabberd_odbc:escape(LServer), - odbc_queries:del_privacy_lists(LServer, Server, Username). diff --git a/src/mod_private_odbc.erl b/src/mod_private_odbc.erl deleted file mode 100644 index 15e505bbf..000000000 --- a/src/mod_private_odbc.erl +++ /dev/null @@ -1,181 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_private_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : Private storage support in ODBC (obsolete) -%%% Created : 5 Oct 2006 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_private_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([start/2, - stop/1, - process_sm_iq/3, - remove_user/2]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE, - ?MODULE, process_sm_iq, IQDisc). - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE). - - -process_sm_iq(From, To, #iq{type = Type} = IQ_Rec) -> - case check_packet(From, To, IQ_Rec) of - ok -> - case Type of - set -> - process_iq_set(From, To, IQ_Rec); - get -> - process_iq_get(From, To, IQ_Rec) - end; - {error, Error} -> - exmpp_iq:error(IQ_Rec, Error) - end. - -process_iq_get(From, _To, #iq{payload = SubEl} = IQ_Rec) -> - LUser = exmpp_jid:prep_node_as_list(From), - LServer = exmpp_jid:prep_domain_as_list(From), - case catch get_data(LUser, - LServer, - exmpp_xml:get_child_elements(SubEl)) of - {'EXIT', _Reason} -> - {error, 'internal-server-error'}; - Res -> - exmpp_iq:result(IQ_Rec, #xmlel{ns = ?NS_PRIVATE, - name = 'query', - children = Res}) - end. - - -process_iq_set(From, _To, #iq{payload = SubEl} = IQ_Rec) -> - LUser = exmpp_jid:prep_node_as_list(From), - LServer = exmpp_jid:prep_domain_as_list(From), - F = fun() -> - lists:foreach( - fun(El) -> - set_data(LUser, LServer, El) - end, exmpp_xml:get_child_elements(SubEl)) - end, - odbc_queries:sql_transaction(LServer, F), - exmpp_iq:result(IQ_Rec). - - -check_packet(From, To, IQ_Rec) -> - check_packet(From, To, IQ_Rec, [ fun check_domain/3, - fun check_user/3, - fun check_ns/3]). -check_packet(_From, _To, _IQ_Rec, []) -> - ok; -check_packet(From, To, IQ_Rec, [F | R]) -> - case F(From, To, IQ_Rec) of - {error, _} = Error -> Error; - ok -> check_packet(From, To, IQ_Rec, R) - end. - -check_domain(From, _To, _IQ_Rec) -> - LServer = exmpp_jid:prep_domain_as_list(From), - case ?IS_MY_HOST(LServer) of - true -> ok; - false -> {error, 'not-allowed'} - end. - -% the iq can't be directed to another jid -check_user(From, To, _IQ_Rec) -> - case exmpp_jid:bare_compare(From, To) of - true -> ok; - false -> {error, 'forbidden'} - end. - -%there must be at least one child, and every child should have -%a namespace specified (reject if the namespace is jabber:iq:private, -%the same than the parent element). -check_ns(_From, _To, #iq{payload = SubEl}) -> - case exmpp_xml:get_child_elements(SubEl) of - [] -> - {error, 'not-acceptable'}; - Children -> - case lists:any(fun(Child) -> - exmpp_xml:get_ns_as_atom(Child) =:= ?NS_PRIVATE - end, Children) of - true -> {error, 'not-acceptable'}; - false -> ok - end - end. - - - -set_data(LUser, LServer, El) -> - XMLNS = exmpp_xml:get_ns_as_list(El), - Username = ejabberd_odbc:escape(LUser), - LXMLNS = ejabberd_odbc:escape(XMLNS), - SData = ejabberd_odbc:escape(exmpp_xml:document_to_list(El)), - odbc_queries:set_private_data(LServer, Username, LXMLNS, SData). - -get_data(LUser, LServer, Els) -> - get_data(LUser, LServer, Els, []). - -get_data(_LUser, _LServer, [], Res) -> - lists:reverse(Res); -get_data(LUser, LServer, [El | Els], Res) -> - XMLNS = exmpp_xml:get_ns_as_list(El), - Username = ejabberd_odbc:escape(LUser), - LXMLNS = ejabberd_odbc:escape(XMLNS), - case catch odbc_queries:get_private_data(LServer, Username, LXMLNS) of - {selected, ["data"], [{SData}]} -> - [Data] = exmpp_xml:parse_document(SData, - [names_as_atom, {check_elems, xmpp}, - {check_nss,xmpp} ]), - get_data(LUser, LServer, Els, [Data | Res]); - %% MREMOND: I wonder when the query could return a vcard ? - {selected, ["vcard"], []} -> - get_data(LUser, LServer, Els, - [El | Res]); - _ -> - get_data(LUser, LServer, Els, [El | Res]) -end. - - -remove_user(User, Server) when is_binary(User), is_binary(Server) -> - try - LUser = binary_to_list(exmpp_stringprep:nodeprep(User)), - LServer = binary_to_list(exmpp_stringprep:nameprep(Server)), - Username = ejabberd_odbc:escape(LUser), - odbc_queries:del_user_private_storage(LServer, Username) - catch - _ -> - ok - end. diff --git a/src/mod_roster_odbc.erl b/src/mod_roster_odbc.erl deleted file mode 100644 index 5181d106e..000000000 --- a/src/mod_roster_odbc.erl +++ /dev/null @@ -1,1256 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_roster_odbc.erl -%%% Author : Alexey Shchepin -%%% Purpose : Roster management for ODBC (obsolete) -%%% Created : 15 Dec 2004 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - -%%% @doc Roster management (Mnesia storage). -%%% -%%% Includes support for XEP-0237: Roster Versioning. -%%% The roster versioning follows an all-or-nothing strategy: -%%% - If the version supplied by the client is the latest, return an empty response. -%%% - If not, return the entire new roster (with updated version string). -%%% Roster version is a hash digest of the entire roster. -%%% No additional data is stored in DB. - --module(mod_roster_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([start/2, stop/1, - process_iq/3, - process_local_iq/3, - get_user_roster/2, - get_subscription_lists/3, - get_in_pending_subscriptions/3, - in_subscription/6, - out_subscription/4, - set_items/3, - remove_user/2, - get_jid_info/4, - webadmin_page/3, - webadmin_user/4, - get_versioning_feature/2, - roster_versioning_enabled/1]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). --include("mod_roster.hrl"). --include("web/ejabberd_http.hrl"). --include("web/ejabberd_web_admin.hrl"). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - ejabberd_hooks:add(roster_get, HostB, - ?MODULE, get_user_roster, 50), - ejabberd_hooks:add(roster_in_subscription, HostB, - ?MODULE, in_subscription, 50), - ejabberd_hooks:add(roster_out_subscription, HostB, - ?MODULE, out_subscription, 50), - ejabberd_hooks:add(roster_get_subscription_lists, HostB, - ?MODULE, get_subscription_lists, 50), - ejabberd_hooks:add(roster_get_jid_info, HostB, - ?MODULE, get_jid_info, 50), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(anonymous_purge_hook, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(resend_subscription_requests_hook, HostB, - ?MODULE, get_in_pending_subscriptions, 50), - ejabberd_hooks:add(roster_get_versioning_feature, HostB, - ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:add(webadmin_page_host, HostB, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, HostB, - ?MODULE, webadmin_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_ROSTER, - ?MODULE, process_iq, IQDisc). - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(roster_get, HostB, - ?MODULE, get_user_roster, 50), - ejabberd_hooks:delete(roster_in_subscription, HostB, - ?MODULE, in_subscription, 50), - ejabberd_hooks:delete(roster_out_subscription, HostB, - ?MODULE, out_subscription, 50), - ejabberd_hooks:delete(roster_get_subscription_lists, HostB, - ?MODULE, get_subscription_lists, 50), - ejabberd_hooks:delete(roster_get_jid_info, HostB, - ?MODULE, get_jid_info, 50), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(anonymous_purge_hook, HostB, - ?MODULE, remove_user, 50), - ejabberd_hooks:delete(resend_subscription_requests_hook, HostB, - ?MODULE, get_in_pending_subscriptions, 50), - ejabberd_hooks:delete(roster_get_versioning_feature, HostB, - ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:delete(webadmin_page_host, HostB, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, HostB, - ?MODULE, webadmin_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_ROSTER). - - -process_iq(From, To, IQ_Rec) -> - LServer = exmpp_jid:prep_domain_as_list(From), - case ?IS_MY_HOST(LServer) of - true -> - process_local_iq(From, To, IQ_Rec); - _ -> - exmpp_iq:error(IQ_Rec, 'item-not-found') - end. - -process_local_iq(From, To, #iq{type = get} = IQ_Rec) -> - process_iq_get(From, To, IQ_Rec); -process_local_iq(From, To, #iq{type = set} = IQ_Rec) -> - process_iq_set(From, To, IQ_Rec). - - - -roster_hash(Items) -> - sha:sha(term_to_binary( - lists:sort( - [R#roster{groups = lists:sort(Grs)} || - R = #roster{groups = Grs} <- Items]))). - -%% @spec (Host::binary()) -> true | false -roster_versioning_enabled(Host) -> - gen_mod:get_module_opt(binary_to_list(Host), ?MODULE, versioning, false). - -%% @spec (Host::binary()) -> true | false -roster_version_on_db(Host) -> - gen_mod:get_module_opt(binary_to_list(Host), ?MODULE, store_current_id, false). - -%% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled. -get_versioning_feature(Acc, Host) -> - case roster_versioning_enabled(Host) of - true -> - Feature = exmpp_xml:element(?NS_ROSTER_VER_s, 'ver', [], - [exmpp_xml:element(?NS_ROSTER_VER_s, 'optional')]), - [Feature | Acc]; - false -> [] - end. - -roster_version(LServer ,LUser) -> - US = {LUser, LServer}, - case roster_version_on_db(LServer) of - true -> - case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of - {selected, ["version"], [{Version}]} -> Version; - {selected, ["version"], []} -> not_found - end; - false -> - roster_hash(ejabberd_hooks:run_fold(roster_get, LServer, [], [US])) - end. - -%% Load roster from DB only if neccesary. -%% It is neccesary if -%% - roster versioning is disabled in server OR -%% - roster versioning is not used by the client OR -%% - roster versioning is used by server and client, BUT the server isn't storing versions on db OR -%% - the roster version from client don't match current version. -process_iq_get(From, To, IQ_Rec) -> - US = {LUser, LServer} = {exmpp_jid:prep_node(From), exmpp_jid:prep_domain(From)}, - try - {ItemsToSend, VersionToSend} = - case {exmpp_xml:get_attribute_as_list(exmpp_iq:get_request(IQ_Rec), <<"ver">>, not_found), - roster_versioning_enabled(LServer), - roster_version_on_db(LServer)} of - {not_found, _ , _} -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, exmpp_jid:prep_domain(To), [], [US])), false}; - {_, false, _} -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, exmpp_jid:prep_domain(To), [], [US])), false}; - - {RequestedVersion, true, true} -> - %% Retrieve version from DB. Only load entire roster - %% when neccesary. - case odbc_queries:get_roster_version(ejabberd_odbc:escape(LServer), ejabberd_odbc:escape(LUser)) of - {selected, ["version"], [{RequestedVersion}]} -> - {false, false}; - {selected, ["version"], [{NewVersion}]} -> - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, exmpp_jid:prep_domain(To), [], [US])), NewVersion}; - {selected, ["version"], []} -> - RosterVersion = sha:sha(term_to_binary(now())), - {atomic, {updated,1}} = odbc_queries:sql_transaction(binary_to_list(LServer), fun() -> - odbc_queries:set_roster_version(ejabberd_odbc:escape(LUser), RosterVersion) - end), - {lists:map(fun item_to_xml/1, - ejabberd_hooks:run_fold(roster_get, exmpp_jid:prep_domain(To), [], [US])), RosterVersion} - end; - - {RequestedVersion, true, false} -> - RosterItems = ejabberd_hooks:run_fold(roster_get, exmpp_jid:prep_domain(To), [] , [US]), - case roster_hash(RosterItems) of - RequestedVersion -> - {false, false}; - New -> - {lists:map(fun item_to_xml/1, RosterItems), New} - end - - end, - case {ItemsToSend, VersionToSend} of - {false, false} -> - exmpp_iq:result(IQ_Rec); - {Items, false} -> - exmpp_iq:result(IQ_Rec, exmpp_xml:element(?NS_ROSTER, 'query', [] , Items)); - {Items, Version} -> - exmpp_iq:result(IQ_Rec, exmpp_xml:element(?NS_ROSTER, 'query', [?XMLATTR(<<"ver">>, Version)], Items)) - end - catch - _:_ -> - exmpp_iq:error(IQ_Rec, 'internal-server-error') - end. - -get_user_roster(Acc, {LUser, LServer}) -> - Items = get_roster(LUser, LServer), - lists:filter(fun(#roster{subscription = none, ask = in}) -> - false; - (_) -> - true - end, Items) ++ Acc. - -get_roster(LUser, LServer) when is_binary(LUser), is_binary(LServer)-> - Username = ejabberd_odbc:escape(LUser), - DomainString = binary_to_list(LServer), - case catch odbc_queries:get_roster(DomainString, Username) of - {selected, ["username", "jid", "nick", "subscription", "ask", - "askmessage", "server", "subscribe", "type"], - Items} when is_list(Items) -> - JIDGroups = case catch odbc_queries:get_roster_jid_groups(DomainString, Username) of - {selected, ["jid","grp"], JGrps} - when is_list(JGrps) -> - [{list_to_binary(S), list_to_binary(G)} || {S, G} <- JGrps]; - _ -> - [] - end, - RItems = lists:flatmap( - fun(I) -> - case raw_to_record(LServer, I) of - %% Bad JID in database: - error -> - []; - R -> - {U2, S2, R2} = R#roster.jid, - SJID = exmpp_jid:to_binary(U2, S2, R2), - Groups = lists:flatmap( - fun({S, G}) when S == SJID -> - [G]; - (_) -> - [] - end, JIDGroups), - [R#roster{groups = Groups}] - end - end, Items), - RItems; - _ -> - [] - end. - - -item_to_xml(Item) -> - {U, S, R} = Item#roster.jid, - Attrs1 = exmpp_xml:set_attribute_in_list([], - <<"jid">>, exmpp_jid:to_binary(U, S, R)), - Attrs2 = case Item#roster.name of - <<>> -> - Attrs1; - Name -> - exmpp_xml:set_attribute_in_list(Attrs1, <<"name">>, Name) - end, - Attrs3 = exmpp_xml:set_attribute_in_list(Attrs2, - <<"subscription">>, Item#roster.subscription), - Attrs = case ask_to_pending(Item#roster.ask) of - out -> - exmpp_xml:set_attribute_in_list(Attrs3, - <<"ask">>, <<"subscribe">>); - both -> - exmpp_xml:set_attribute_in_list(Attrs3, - <<"ask">>, <<"subscribe">>); - _ -> - Attrs3 - end, - SubEls = lists:map(fun(G) -> - exmpp_xml:set_cdata( - #xmlel{ns = ?NS_ROSTER, name = 'group'}, G) - end, Item#roster.groups), - #xmlel{ns = ?NS_ROSTER, name = 'item', attrs = Attrs, children = SubEls}. - - -process_iq_set(From, To, #iq{payload = Request} = IQ_Rec) -> - case Request of - #xmlel{children = Els} -> - lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els); - _ -> - ok - end, - exmpp_iq:result(IQ_Rec). - -process_item_set(From, To, #xmlel{} = El) -> - try - JID1 = exmpp_jid:parse(exmpp_xml:get_attribute_as_binary(El, <<"jid">>, <<>>)), - User = exmpp_jid:prep_node(From), - Server = exmpp_jid:prep_domain(From), - LServer = binary_to_list(Server), - {U0, S0, R0} = LJID = jlib:short_prepd_jid(JID1), - Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(U0, S0, R0)), - F = fun() -> - {selected, - ["username", "jid", "nick", "subscription", - "ask", "askmessage", "server", "subscribe", "type"], - Res} = odbc_queries:get_roster_by_jid(LServer, Username, SJID), - Item = case Res of - [] -> - #roster{usj = {User, Server, LJID}, - us = {User, Server}, - jid = LJID}; - [I] -> - R = raw_to_record(exmpp_jid:prep_domain(From), I), - case R of - %% Bad JID in database: - error -> - #roster{usj = {User, Server, LJID}, - us = {User, Server}, - jid = LJID}; - _ -> - R#roster{ - usj = {User, Server, LJID}, - us = {User, Server}, - jid = LJID, - name = <<>>} - end - end, - Item1 = process_item_attrs(Item, El#xmlel.attrs), - Item2 = process_item_els(Item1, El#xmlel.children), - case Item2#roster.subscription of - remove -> - odbc_queries:del_roster(LServer, Username, SJID); - _ -> - ItemVals = record_to_string(Item2), - ItemGroups = groups_to_string(Item2), - odbc_queries:update_roster(LServer, Username, SJID, ItemVals, ItemGroups) - end, - %% If the item exist in shared roster, take the - %% subscription information from there: - Item3 = ejabberd_hooks:run_fold(roster_process_item, - exmpp_jid:prep_domain(From), Item2, [exmpp_jid:prep_domain(From)]), - case roster_version_on_db(Server) of - true -> odbc_queries:set_roster_version(Username, sha:sha(term_to_binary(now()))); - false -> ok - end, - - {Item, Item3} - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {OldItem, Item}} -> - push_item(exmpp_jid:node(From), exmpp_jid:prep_domain(From), To, Item), - case Item#roster.subscription of - remove -> - IsTo = case OldItem#roster.subscription of - both -> true; - to -> true; - _ -> false - end, - IsFrom = case OldItem#roster.subscription of - both -> true; - from -> true; - _ -> false - end, - {U, S, R} = OldItem#roster.jid, - if IsTo -> - ejabberd_router:route( - exmpp_jid:bare(From), - exmpp_jid:make(U, S, R), - exmpp_presence:unsubscribe()); - true -> ok - end, - if IsFrom -> - ejabberd_router:route( - exmpp_jid:bare(From), - exmpp_jid:make(U, S, R), - exmpp_presence:unsubscribed()); - true -> ok - end, - ok; - _ -> - ok - end; - E -> - ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), - ok - end - catch - _ -> - ok - end; -process_item_set(_From, _To, _) -> - ok. - -process_item_attrs(Item, [#xmlattr{name = Attr, value = Val} | Attrs]) -> - case Attr of - <<"name">> -> - process_item_attrs(Item#roster{name = Val}, Attrs); - <<"subscription">> -> - case Val of - <<"remove">> -> - process_item_attrs(Item#roster{subscription = remove}, - Attrs); - _ -> - process_item_attrs(Item, Attrs) - end; - <<"ask">> -> - process_item_attrs(Item, Attrs); - _ -> - process_item_attrs(Item, Attrs) - end; -process_item_attrs(Item, []) -> - Item. - - -process_item_els(Item, [#xmlel{name = Name} = El | Els]) -> - case Name of - 'group' -> - Groups = [exmpp_xml:get_cdata(El) | Item#roster.groups], - process_item_els(Item#roster{groups = Groups}, Els); - _ -> - process_item_els(Item, Els) - end; -process_item_els(Item, [_ | Els]) -> - process_item_els(Item, Els); -process_item_els(Item, []) -> - Item. - - -push_item(User, Server, From, Item) when is_binary(User), is_binary(Server) -> - {U, S, R} = Item#roster.jid, - ejabberd_sm:route(exmpp_jid:make(), - exmpp_jid:make(User, Server), - #xmlel{name = 'broadcast', ns = roster_item, attrs = - [?XMLATTR(<<"u">>, U), - ?XMLATTR(<<"s">>, S), - ?XMLATTR(<<"r">>, R), - ?XMLATTR(<<"subs">>, Item#roster.subscription)]}), - case roster_versioning_enabled(Server) of - true -> - push_item_version(Server, User, From, Item, roster_version(Server, User)); - false -> - lists:foreach(fun(Resource) -> - push_item(User, Server, Resource, From, Item) - end, ejabberd_sm:get_user_resources(User, Server)) - end. - - -% TODO: don't push to those who not load roster -push_item(User, Server, Resource, From, Item) -> - Request = #xmlel{ns = ?NS_ROSTER, name = 'query', - children = [item_to_xml(Item)]}, - ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request, - "push" ++ randoms:get_string()), - ejabberd_router:route( - From, - exmpp_jid:make(User, Server, Resource), - ResIQ). - -%% @doc Roster push, calculate and include the version attribute. -%% TODO: don't push to those who didn't load roster -push_item_version(Server, User, From, Item, RosterVersion) -> - lists:foreach(fun(Resource) -> - push_item_version(User, Server, Resource, From, Item, RosterVersion) - end, ejabberd_sm:get_user_resources(User, Server)). - -push_item_version(User, Server, Resource, From, Item, RosterVersion) -> - Request = #xmlel{ns = ?NS_ROSTER, name = 'query', attrs = [?XMLATTR(<<"ver">>, RosterVersion)], - children = [mod_roster:item_to_xml(Item)]}, - ResIQ = exmpp_iq:set(?NS_JABBER_CLIENT, Request, - "push" ++ randoms:get_string()), - ejabberd_router:route( - From, - exmpp_jid:make(User, Server, Resource), - ResIQ). - -get_subscription_lists(_, User, Server) - when is_binary(User), is_binary(Server) -> - try - LServer = binary_to_list(Server), - Username = ejabberd_odbc:escape(User), - case catch odbc_queries:get_roster(LServer, Username) of - {selected, ["username", "jid", "nick", "subscription", "ask", - "askmessage", "server", "subscribe", "type"], - Items} when is_list(Items) -> - fill_subscription_lists(Server, Items, [], []); - _ -> - {[], []} - end - catch - _ -> - {[], []} - end. - -fill_subscription_lists(LServer, [RawI | Is], F, T) -> - I = raw_to_record(LServer, RawI), - case I of - %% Bad JID in database: - error -> - fill_subscription_lists(LServer, Is, F, T); - _ -> - J = I#roster.jid, - case I#roster.subscription of - both -> - fill_subscription_lists(LServer, Is, [J | F], [J | T]); - from -> - fill_subscription_lists(LServer, Is, [J | F], T); - to -> - fill_subscription_lists(LServer, Is, F, [J | T]); - _ -> - fill_subscription_lists(LServer, Is, F, T) - end - end; -fill_subscription_lists(_LServer, [], F, T) -> - {F, T}. - -ask_to_pending(subscribe) -> out; -ask_to_pending(unsubscribe) -> none; -ask_to_pending(Ask) -> Ask. - - - -in_subscription(_, User, Server, JID, Type, Reason) -> - process_subscription(in, User, Server, JID, Type, Reason). - -out_subscription(User, Server, JID, Type) -> - process_subscription(out, User, Server, JID, Type, <<>>). - -process_subscription(Direction, User, Server, JID1, Type, Reason) - when is_binary(User), is_binary(Server) -> - try - LServer = binary_to_list(Server), - {N0,D0,R0} = LJID = jlib:short_prepd_jid(JID1), - Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(N0,D0,R0)), - F = fun() -> - Item = - case odbc_queries:get_roster_by_jid(LServer, Username, SJID) of - {selected, - ["username", "jid", "nick", "subscription", "ask", - "askmessage", "server", "subscribe", "type"], - [I]} -> - %% raw_to_record can return error, but - %% jlib_to_string would fail before this point - R = raw_to_record(list_to_binary(LServer), I), - Groups = - case odbc_queries:get_roster_groups(LServer, Username, SJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [list_to_binary(JGrp) || {JGrp} <- JGrps]; - _ -> - [] - end, - R#roster{groups = Groups}; - {selected, - ["username", "jid", "nick", "subscription", "ask", - "askmessage", "server", "subscribe", "type"], - []} -> - #roster{usj = {User, Server, LJID}, - us = {User, Server}, - jid = LJID} - end, - NewState = case Direction of - out -> - out_state_change(Item#roster.subscription, - Item#roster.ask, - Type); - in -> - in_state_change(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AutoReply = case Direction of - out -> - none; - in -> - in_auto_reply(Item#roster.subscription, - Item#roster.ask, - Type) - end, - AskMessage = case NewState of - {_, both} -> Reason; - {_, in} -> Reason; - _ -> <<>> - end, - case NewState of - none -> - {none, AutoReply}; - {none, none} when Item#roster.subscription == none, - Item#roster.ask == in -> - odbc_queries:del_roster(LServer, Username, SJID), - {none, AutoReply}; - {Subscription, Pending} -> - AskBinary = case AskMessage of - undefined -> <<>>; - B -> B - end, - NewItem = Item#roster{subscription = Subscription, - ask = Pending, - askmessage = AskBinary}, - ItemVals = record_to_string(NewItem), - odbc_queries:roster_subscribe(LServer, Username, SJID, ItemVals), - case roster_version_on_db(Server) of - true -> odbc_queries:set_roster_version(Username, sha:sha(term_to_binary(now()))); - false -> ok - end, - {{push, NewItem}, AutoReply} - end - end, - case odbc_queries:sql_transaction(LServer, F) of - {atomic, {Push, AutoReply}} -> - case AutoReply of - none -> - ok; - _ -> - ejabberd_router:route( - exmpp_jid:make(User, Server), JID1, - exmpp_presence:AutoReply()) - end, - case Push of - {push, Item} -> - if - Item#roster.subscription == none, - Item#roster.ask == in -> - ok; - true -> - push_item(User, Server, - exmpp_jid:make(User, Server), Item) - end, - true; - none -> - false - end; - _ -> - false - end - catch - _ -> - false - end. - - -%% in_state_change(Subscription, Pending, Type) -> NewState -%% NewState = none | {NewSubscription, NewPending} --ifdef(ROSTER_GATEWAY_WORKAROUND). --define(NNSD, {to, none}). --define(NISD, {to, in}). --else. --define(NNSD, none). --define(NISD, none). --endif. - -in_state_change(none, none, subscribe) -> {none, in}; -in_state_change(none, none, subscribed) -> ?NNSD; -in_state_change(none, none, unsubscribe) -> none; -in_state_change(none, none, unsubscribed) -> none; -in_state_change(none, out, subscribe) -> {none, both}; -in_state_change(none, out, subscribed) -> {to, none}; -in_state_change(none, out, unsubscribe) -> none; -in_state_change(none, out, unsubscribed) -> {none, none}; -in_state_change(none, in, subscribe) -> none; -in_state_change(none, in, subscribed) -> ?NISD; -in_state_change(none, in, unsubscribe) -> {none, none}; -in_state_change(none, in, unsubscribed) -> none; -in_state_change(none, both, subscribe) -> none; -in_state_change(none, both, subscribed) -> {to, in}; -in_state_change(none, both, unsubscribe) -> {none, out}; -in_state_change(none, both, unsubscribed) -> {none, in}; -in_state_change(to, none, subscribe) -> {to, in}; -in_state_change(to, none, subscribed) -> none; -in_state_change(to, none, unsubscribe) -> none; -in_state_change(to, none, unsubscribed) -> {none, none}; -in_state_change(to, in, subscribe) -> none; -in_state_change(to, in, subscribed) -> none; -in_state_change(to, in, unsubscribe) -> {to, none}; -in_state_change(to, in, unsubscribed) -> {none, in}; -in_state_change(from, none, subscribe) -> none; -in_state_change(from, none, subscribed) -> {both, none}; -in_state_change(from, none, unsubscribe) -> {none, none}; -in_state_change(from, none, unsubscribed) -> none; -in_state_change(from, out, subscribe) -> none; -in_state_change(from, out, subscribed) -> {both, none}; -in_state_change(from, out, unsubscribe) -> {none, out}; -in_state_change(from, out, unsubscribed) -> {from, none}; -in_state_change(both, none, subscribe) -> none; -in_state_change(both, none, subscribed) -> none; -in_state_change(both, none, unsubscribe) -> {to, none}; -in_state_change(both, none, unsubscribed) -> {from, none}. - -out_state_change(none, none, subscribe) -> {none, out}; -out_state_change(none, none, subscribed) -> none; -out_state_change(none, none, unsubscribe) -> none; -out_state_change(none, none, unsubscribed) -> none; -out_state_change(none, out, subscribe) -> {none, out}; %% We need to resend query (RFC3921, section 9.2) -out_state_change(none, out, subscribed) -> none; -out_state_change(none, out, unsubscribe) -> {none, none}; -out_state_change(none, out, unsubscribed) -> none; -out_state_change(none, in, subscribe) -> {none, both}; -out_state_change(none, in, subscribed) -> {from, none}; -out_state_change(none, in, unsubscribe) -> none; -out_state_change(none, in, unsubscribed) -> {none, none}; -out_state_change(none, both, subscribe) -> none; -out_state_change(none, both, subscribed) -> {from, out}; -out_state_change(none, both, unsubscribe) -> {none, in}; -out_state_change(none, both, unsubscribed) -> {none, out}; -out_state_change(to, none, subscribe) -> none; -out_state_change(to, none, subscribed) -> {both, none}; -out_state_change(to, none, unsubscribe) -> {none, none}; -out_state_change(to, none, unsubscribed) -> none; -out_state_change(to, in, subscribe) -> none; -out_state_change(to, in, subscribed) -> {both, none}; -out_state_change(to, in, unsubscribe) -> {none, in}; -out_state_change(to, in, unsubscribed) -> {to, none}; -out_state_change(from, none, subscribe) -> {from, out}; -out_state_change(from, none, subscribed) -> none; -out_state_change(from, none, unsubscribe) -> none; -out_state_change(from, none, unsubscribed) -> {none, none}; -out_state_change(from, out, subscribe) -> none; -out_state_change(from, out, subscribed) -> none; -out_state_change(from, out, unsubscribe) -> {from, none}; -out_state_change(from, out, unsubscribed) -> {none, out}; -out_state_change(both, none, subscribe) -> none; -out_state_change(both, none, subscribed) -> none; -out_state_change(both, none, unsubscribe) -> {from, none}; -out_state_change(both, none, unsubscribed) -> {to, none}. - -in_auto_reply(from, none, subscribe) -> subscribed; -in_auto_reply(from, out, subscribe) -> subscribed; -in_auto_reply(both, none, subscribe) -> subscribed; -in_auto_reply(none, in, unsubscribe) -> unsubscribed; -in_auto_reply(none, both, unsubscribe) -> unsubscribed; -in_auto_reply(to, in, unsubscribe) -> unsubscribed; -in_auto_reply(from, none, unsubscribe) -> unsubscribed; -in_auto_reply(from, out, unsubscribe) -> unsubscribed; -in_auto_reply(both, none, unsubscribe) -> unsubscribed; -in_auto_reply(_, _, _) -> none. - - -remove_user(User, Server) when is_binary(User), is_binary(Server) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - LServerStr = binary_to_list(LServer), - Username = ejabberd_odbc:escape(LUser), - send_unsubscription_to_rosteritems(LUser, LServer), - odbc_queries:del_user_roster_t(LServerStr, Username), - ok - catch - _ -> - ok - end. - -%% For each contact with Subscription: -%% Both or From, send a "unsubscribed" presence stanza; -%% Both or To, send a "unsubscribe" presence stanza. -send_unsubscription_to_rosteritems(LUser, LServer) -> - RosterItems = get_user_roster([], {LUser, LServer}), - From = exmpp_jid:make(LUser, LServer), - lists:foreach(fun(RosterItem) -> - send_unsubscribing_presence(From, RosterItem) - end, - RosterItems). - -%% @spec (From::jid(), Item::roster()) -> ok -send_unsubscribing_presence(From, Item) -> - IsTo = case Item#roster.subscription of - both -> true; - to -> true; - _ -> false - end, - IsFrom = case Item#roster.subscription of - both -> true; - from -> true; - _ -> false - end, - {INode, IDom, IRes} = Item#roster.jid, - SendToJID = exmpp_jid:make(INode, IDom, IRes), - if IsTo -> - ejabberd_router:route( - exmpp_jid:bare(From), - SendToJID, - exmpp_presence:unsubscribe()); - true -> ok - end, - if IsFrom -> - ejabberd_router:route( - exmpp_jid:bare(From), - SendToJID, - exmpp_presence:unsubscribed()); - true -> ok - end, - ok. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -set_items(User, Server, #xmlel{children = Els}) when is_binary(User), is_binary(Server) -> - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - catch odbc_queries:sql_transaction( - LServer, - lists:flatmap(fun(El) -> - process_item_set_t(LUser, LServer, El) - end, Els)). - -process_item_set_t(LUser, LServer, #xmlel{} = El) -> - try - JID1 = exmpp_jid:parse(exmpp_xml:get_attribute_as_binary(El, <<"jid">>, <<>>)), - {U0, S0, R0} = LJID = jlib:short_prepd_jid(JID1), - Username = ejabberd_odbc:escape(LUser), - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(U0, S0, R0)), - Item = #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, - jid = LJID}, - Item1 = process_item_attrs_ws(Item, El#xmlel.attrs), - Item2 = process_item_els(Item1, El#xmlel.children), - case Item2#roster.subscription of - remove -> - odbc_queries:del_roster_sql(LServer, Username, SJID); - _ -> - ItemVals = record_to_string(Item1), - ItemGroups = groups_to_string(Item2), - odbc_queries:update_roster_sql(LServer, Username, SJID, ItemVals, ItemGroups) - end - catch - _ -> - [] - end; -process_item_set_t(_LUser, _LServer, _) -> - []. - -process_item_attrs_ws(Item, [#xmlattr{name = Attr, value = Val} | Attrs]) -> - case Attr of - <<"name">> -> - process_item_attrs_ws(Item#roster{name = Val}, Attrs); - <<"subscription">> -> - case Val of - <<"remove">> -> - process_item_attrs_ws(Item#roster{subscription = remove}, - Attrs); - <<"none">> -> - process_item_attrs_ws(Item#roster{subscription = none}, - Attrs); - <<"both">> -> - process_item_attrs_ws(Item#roster{subscription = both}, - Attrs); - <<"from">> -> - process_item_attrs_ws(Item#roster{subscription = from}, - Attrs); - <<"to">> -> - process_item_attrs_ws(Item#roster{subscription = to}, - Attrs); - _ -> - process_item_attrs_ws(Item, Attrs) - end; - 'ask' -> - process_item_attrs_ws(Item, Attrs); - _ -> - process_item_attrs_ws(Item, Attrs) - end; -process_item_attrs_ws(Item, []) -> - Item. - -get_in_pending_subscriptions(Ls, User, Server) - when is_binary(User), is_binary(Server) -> - JID = exmpp_jid:make(User, Server), - LUser = exmpp_jid:prep_node(JID), - LServer = exmpp_jid:prep_domain_as_list(JID), - Username = ejabberd_odbc:escape(LUser), - case catch odbc_queries:get_roster(LServer, Username) of - {selected, ["username", "jid", "nick", "subscription", "ask", - "askmessage", "server", "subscribe", "type"], - Items} when is_list(Items) -> - Ls ++ lists:map( - fun(R) -> - Message = R#roster.askmessage, - {U0, S0, R0} = R#roster.jid, - Pres1 = exmpp_presence:subscribe(), - Pres2 = exmpp_stanza:set_jids(Pres1, - exmpp_jid:to_binary(U0, S0, R0), - exmpp_jid:to_binary(JID)), - exmpp_presence:set_status(Pres2, Message) - end, - lists:flatmap( - fun(I) -> - case raw_to_record(exmpp_jid:prep_domain(JID), I) of - %% Bad JID in database: - error -> - []; - R -> - case R#roster.ask of - in -> [R]; - both -> [R]; - _ -> [] - end - end - end, - Items)); - _ -> - Ls - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% JID is jid() record, because it's used latter on for both short_prepd_jid -%% and short_prepd_bare_jid -get_jid_info(_, User, Server, JID) when is_binary(User), is_binary(Server) -> - try - LServer = binary_to_list(Server), - LJID = {N, D, R} = jlib:short_prepd_jid(JID), - Username = ejabberd_odbc:escape(User), - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(N, D, R)), - case catch odbc_queries:get_subscription(LServer, Username, SJID) of - {selected, ["subscription"], [{SSubscription}]} -> - Subscription = case SSubscription of - "B" -> both; - "T" -> to; - "F" -> from; - _ -> none - end, - Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [list_to_binary(JGrp) || {JGrp} <- JGrps]; - _ -> - [] - end, - {Subscription, Groups}; - _ -> - LRJID = jlib:short_prepd_bare_jid(JID), - if - LRJID == LJID -> - {none, []}; - true -> - {LR_N, LR_D, LR_R} = LRJID, - SRJID = ejabberd_odbc:escape(exmpp_jid:to_binary(LR_N, LR_D, LR_R)), - case catch odbc_queries:get_subscription(LServer, Username, SRJID) of - {selected, ["subscription"], [{SSubscription}]} -> - Subscription = case SSubscription of - "B" -> both; - "T" -> to; - "F" -> from; - _ -> none - end, - Groups = case catch odbc_queries:get_rostergroup_by_jid(LServer, Username, SRJID) of - {selected, ["grp"], JGrps} when is_list(JGrps) -> - [list_to_binary(JGrp) || {JGrp} <- JGrps]; - _ -> - [] - end, - {Subscription, Groups}; - _ -> - {none, []} - end - end - end - catch - _ -> - {none, []} - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}) when is_binary(LServer) -> - try - JID = exmpp_jid:parse(SJID), - LJID = jlib:short_prepd_jid(JID), - Subscription = case SSubscription of - "B" -> both; - "T" -> to; - "F" -> from; - _ -> none - end, - Ask = case SAsk of - "S" -> subscribe; - "U" -> unsubscribe; - "B" -> both; - "O" -> out; - "I" -> in; - _ -> none - end, - UserB = list_to_binary(User), - #roster{usj = {UserB, LServer, LJID}, - us = {UserB, LServer}, - jid = LJID, - name = list_to_binary(Nick), - subscription = Subscription, - ask = Ask, - askmessage = list_to_binary(SAskMessage)} - catch - _ -> - error - end. - -record_to_string(#roster{us = {User, _Server}, - jid = JID, - name = Name, - subscription = Subscription, - ask = Ask, - askmessage = AskMessage}) -> - Username = ejabberd_odbc:escape(User), - {U, S, R} = JID, - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(U, S, R)), - Nick = ejabberd_odbc:escape(Name), - SSubscription = case Subscription of - both -> "B"; - to -> "T"; - from -> "F"; - none -> "N" - end, - SAsk = case Ask of - subscribe -> "S"; - unsubscribe -> "U"; - both -> "B"; - out -> "O"; - in -> "I"; - none -> "N" - end, - SAskMessage = ejabberd_odbc:escape(AskMessage), - [Username, SJID, Nick, SSubscription, SAsk, SAskMessage, "N", "", "item"]. - -groups_to_string(#roster{us = {User, _Server}, - jid = JID, - groups = Groups}) -> - Username = ejabberd_odbc:escape(User), - {U, S, R} = JID, - SJID = ejabberd_odbc:escape(exmpp_jid:to_binary(U, S, R)), - - %% Empty groups do not need to be converted to string to be inserted in - %% the database - lists:foldl( - fun([], Acc) -> Acc; - (Group, Acc) -> - G = ejabberd_odbc:escape(Group), - [[Username, SJID, G]|Acc] end, [], Groups). - -webadmin_page(_, Host, - #request{us = _US, - path = ["user", U, "roster"], - q = Query, - lang = Lang} = _Request) -> - Res = user_roster(U, Host, Query, Lang), - {stop, Res}; - -webadmin_page(Acc, _, _) -> Acc. - -user_roster(User, Server, Query, Lang) -> - try - LUser = exmpp_stringprep:nodeprep(User), - LServer = exmpp_stringprep:nameprep(Server), - US = {LUser, LServer}, - LUserB = list_to_binary(LUser), - LServerB = list_to_binary(LServer), - Items1 = get_roster(LUserB, LServerB), - Res = user_roster_parse_query(User, Server, Items1, Query), - Items = get_roster(LUserB, LServerB), - SItems = lists:sort(Items), - FItems = - case SItems of - [] -> - [?CT("None")]; - _ -> - [?XE("table", - [?XE("thead", - [?XE("tr", - [?XCT("td", "Jabber ID"), - ?XCT("td", "Nickname"), - ?XCT("td", "Subscription"), - ?XCT("td", "Pending"), - ?XCT("td", "Groups") - ])]), - ?XE("tbody", - lists:map( - fun(R) -> - Groups = - lists:flatmap( - fun(Group) -> - [?C(binary_to_list(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)), - ?XAC("td", [?XMLATTR(<<"class">>, <<"valign">>)], - atom_to_list(R#roster.subscription)), - ?XAC("td", [?XMLATTR(<<"class">>, <<"valign">>)], - atom_to_list(Pending)), - ?XAE("td", [?XMLATTR(<<"class">>, <<"valign">>)], Groups), - if - Pending == in -> - ?XAE("td", [?XMLATTR(<<"class">>, <<"valign">>)], - [?INPUTT("submit", - "validate" ++ - ejabberd_web_admin:term_to_id(R#roster.jid), - "Validate")]); - true -> - ?X("td") - end, - ?XAE("td", [?XMLATTR(<<"class">>, <<"valign">>)], - [?INPUTT("submit", - "remove" ++ - ejabberd_web_admin:term_to_id(R#roster.jid), - "Remove")])]) - end, SItems))])] - end, - [?XC("h1", ?T("Roster of ") ++ us_to_list(US))] ++ - case Res of - ok -> [?XREST("Submitted")]; - error -> [?XREST("Bad format")]; - nothing -> [] - end ++ - [?XAE("form", [?XMLATTR(<<"action">>, <<"">>), ?XMLATTR(<<"method">>, <<"post">>)], - FItems ++ - [?P, - ?INPUT("text", "newjid", ""), ?C(" "), - ?INPUTT("submit", "addjid", "Add Jabber ID") - ])] - catch - _ -> - [?XC("h1", ?T("Roster of ") ++ us_to_list({User, Server}))] ++ - [?CT("Bad format"), ?P] ++ - [?XAE("form", [?XMLATTR(<<"action">>, <<"">>), ?XMLATTR(<<"method">>, <<"post">>)], - [?P, - ?INPUT("text", "newjid", ""), ?C(" "), - ?INPUTT("submit", "addjid", "Add Jabber ID") - ])] - end. - -build_contact_jid_td({U, S, R}) -> - %% Convert {U, S, R} into {jid, U, S, R, U, S, R}: - ContactJID = exmpp_jid:make(U, S, R), - JIDURI = case {exmpp_jid:prep_node(ContactJID), exmpp_jid:prep_domain(ContactJID)} of - {undefined, _} -> ""; - {CUser, CServer} -> - CUser_S = binary_to_list(CUser), - CServer_S = binary_to_list(CServer), - case ?IS_MY_HOST(CServer_S) of - false -> ""; - true -> "/admin/server/" ++ CServer_S ++ "/user/" ++ CUser_S ++ "/" - end - end, - case JIDURI of - [] -> - ?XAC('td', [?XMLATTR(<<"class">>, <<"valign">>)], exmpp_jid:to_list(ContactJID)); - URI when is_list(URI) -> - ?XAE('td', [?XMLATTR(<<"class">>, <<"valign">>)], [?AC(JIDURI, exmpp_jid:to_list(ContactJID))]) - end. - -user_roster_parse_query(User, Server, Items, Query) -> - case lists:keysearch("addjid", 1, Query) of - {value, _} -> - case lists:keysearch("newjid", 1, Query) of - {value, {_, undefined}} -> - error; - {value, {_, SJID}} -> - try - JID = exmpp_jid:parse(SJID), - user_roster_subscribe_jid(User, Server, JID), - ok - catch - _ -> - error - end; - false -> - error - end; - false -> - case catch user_roster_item_parse_query( - User, Server, Items, Query) of - submitted -> - ok; - {'EXIT', _Reason} -> - error; - _ -> - nothing - end - end. - - -user_roster_subscribe_jid(User, Server, JID) -> - out_subscription(User, Server, JID, subscribe), - UJID = exmpp_jid:make(User, Server), - ejabberd_router:route( - UJID, JID, exmpp_presence:subscribe()). - -user_roster_item_parse_query(User, Server, Items, Query) -> - lists:foreach( - fun(R) -> - JID = R#roster.jid, - case lists:keysearch( - "validate" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of - {value, _} -> - {U, S, Resource} = JID, - JID1 = exmpp_jid:make(U, S, Resource), - out_subscription( - User, Server, JID1, subscribed), - UJID = exmpp_jid:make(User, Server), - ejabberd_router:route( - UJID, JID1, exmpp_presence:subscribed()), - throw(submitted); - false -> - case lists:keysearch( - "remove" ++ ejabberd_web_admin:term_to_id(JID), 1, Query) of - {value, _} -> - UJID = exmpp_jid:make(User, Server), - Attrs1 = exmpp_xml:set_attribute_in_list([], - <<"jid">>, exmpp_jid:to_list(JID)), - Attrs2 = exmpp_xml:set_attribute_in_list(Attrs1, - <<"subscription">>, "remove"), - Item = #xmlel{ns = ?NS_ROSTER, name = 'item', - attrs = Attrs2}, - Request = #xmlel{ - ns = ?NS_ROSTER, - name = 'query', - children = [Item]}, - process_iq( - UJID, UJID, - exmpp_iq:set(?NS_JABBER_CLIENT, Request)), - throw(submitted); - false -> - ok - end - - end - end, Items), - nothing. - -us_to_list({User, Server}) -> - exmpp_jid:bare_to_list(User, Server). - -webadmin_user(Acc, _User, _Server, Lang) -> - Acc ++ [?XE("h3", [?ACT("roster/", "Roster")])]. diff --git a/src/mod_vcard_odbc.erl b/src/mod_vcard_odbc.erl deleted file mode 100644 index ee0a756bd..000000000 --- a/src/mod_vcard_odbc.erl +++ /dev/null @@ -1,610 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_vcard.erl -%%% Author : Alexey Shchepin -%%% Purpose : vCard support via ODBC (obsolete) -%%% Created : 2 Jan 2003 by Alexey Shchepin -%%% -%%% -%%% 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 -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_vcard_odbc). --author('alexey@process-one.net'). - --behaviour(gen_mod). - --export([start/2, init/3, stop/1, - get_sm_features/5, - process_local_iq/3, - process_sm_iq/3, - %reindex_vcards/0, - remove_user/2]). - --include_lib("exmpp/include/exmpp.hrl"). - --include("ejabberd.hrl"). - - --define(JUD_MATCHES, 30). --define(PROCNAME, ejabberd_mod_vcard). - -start(Host, Opts) -> - HostB = list_to_binary(Host), - ejabberd_hooks:add(remove_user, HostB, - ?MODULE, remove_user, 50), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_VCARD, - ?MODULE, process_local_iq, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_VCARD, - ?MODULE, process_sm_iq, IQDisc), - ejabberd_hooks:add(disco_sm_features, HostB, ?MODULE, get_sm_features, 50), - MyHost = gen_mod:expand_host_name(Host, Opts, "vjud"), - Search = gen_mod:get_opt(search, Opts, true), - register(gen_mod:get_module_proc(Host, ?PROCNAME), - spawn(?MODULE, init, [MyHost, Host, Search])). - - -init(Host, ServerHost, Search) -> - case Search of - false -> - loop(Host, ServerHost); - _ -> - ejabberd_router:register_route(Host), - loop(Host, ServerHost) - end. - -loop(Host, ServerHost) -> - receive - {route, From, To, Packet} -> - case catch do_route(ServerHost, From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end, - loop(Host, ServerHost); - stop -> - ejabberd_router:unregister_route(Host), - ok; - _ -> - loop(Host, ServerHost) - end. - -stop(Host) -> - HostB = list_to_binary(Host), - ejabberd_hooks:delete(remove_user, HostB, - ?MODULE, remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, HostB, ?NS_VCARD), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostB, ?NS_VCARD), - ejabberd_hooks:delete(disco_sm_features, HostB, ?MODULE, get_sm_features, 50), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - Proc ! stop, - {wait, Proc}. - -get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> - Acc; - -get_sm_features(Acc, _From, _To, Node, _Lang) -> - case Node of - [] -> - case Acc of - {result, Features} -> - {result, [?NS_VCARD_s | Features]}; - empty -> - {result, [?NS_VCARD_s]} - end; - _ -> - Acc - end. - -process_local_iq(_From, _To, #iq{type = get, lang = Lang} = IQ_Rec) -> - Result = #xmlel{ns = ?NS_VCARD, name = 'vCard', children = [ - exmpp_xml:set_cdata(#xmlel{ns = ?NS_VCARD, name = 'FN'}, - "ejabberd"), - exmpp_xml:set_cdata(#xmlel{ns = ?NS_VCARD, name = 'URL'}, - ?EJABBERD_URI), - exmpp_xml:set_cdata(#xmlel{ns = ?NS_VCARD, name = 'DESC'}, - translate:translate(Lang, "Erlang Jabber Server") ++ - "\nCopyright (c) 2002-2010 ProcessOne"), - exmpp_xml:set_cdata(#xmlel{ns = ?NS_VCARD, name = 'BDAY'}, - "2002-11-16") - ]}, - exmpp_iq:result(IQ_Rec, Result); -process_local_iq(_From, _To, #iq{type = set} = IQ_Rec) -> - exmpp_iq:error(IQ_Rec, 'not-allowed'). - - -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), - Username = ejabberd_odbc:escape(LUser), - case catch odbc_queries:get_vcard(LServer, Username) of - {selected, ["vcard"], [{SVCARD}]} -> - try exmpp_xml:parse_document(SVCARD, - [names_as_atom, {check_elems, xmpp}, - {check_nss,xmpp} ]) of - [VCARD] -> - exmpp_iq:result(IQ_Rec, VCARD) - catch - _Type:_Error -> - ?ERROR_MSG("Error parsing vCard: ~s", [SVCARD]), - exmpp_iq:error(IQ_Rec, 'service-unavailable') - end; - {selected, ["vcard"], []} -> - exmpp_iq:result(IQ_Rec); - _ -> - exmpp_iq:error(IQ_Rec, 'internal-server-error') - end; -process_sm_iq(From, _To, #iq{type = set, payload = Request} = IQ_Rec) -> - User = exmpp_jid:node_as_list(From), - LServer = exmpp_jid:prep_domain_as_list(From), - case ?IS_MY_HOST(LServer) of - true -> - set_vcard(User, LServer, Request), - exmpp_iq:result(IQ_Rec); - false -> - exmpp_iq:error(IQ_Rec, 'not-allowed') - end. - -set_vcard(User, LServer, VCARD) -> - FN = exmpp_xml:get_path(VCARD, - [{element, 'FN'}, cdata_as_list]), - Family = exmpp_xml:get_path(VCARD, - [{element, 'N'}, {element, 'FAMILY'}, cdata_as_list]), - Given = exmpp_xml:get_path(VCARD, - [{element, 'N'}, {element, 'GIVEN'}, cdata_as_list]), - Middle = exmpp_xml:get_path(VCARD, - [{element, 'N'}, {element, 'MIDDLE'}, cdata_as_list]), - Nickname = exmpp_xml:get_path(VCARD, - [{element, 'NICKNAME'}, cdata_as_list]), - BDay = exmpp_xml:get_path(VCARD, - [{element, 'BDAY'}, cdata_as_list]), - CTRY = exmpp_xml:get_path(VCARD, - [{element, 'ADR'}, {element, 'CTRY'}, cdata_as_list]), - Locality = exmpp_xml:get_path(VCARD, - [{element, 'ADR'}, {element, 'LOCALITY'}, cdata_as_list]), - EMail1 = exmpp_xml:get_path(VCARD, - [{element, 'EMAIL'}, {element, 'USERID'}, cdata_as_list]), - EMail2 = exmpp_xml:get_path(VCARD, - [{element, 'EMAIL'}, cdata_as_list]), - OrgName = exmpp_xml:get_path(VCARD, - [{element, 'ORG'}, {element, 'ORGNAME'}, cdata_as_list]), - OrgUnit = exmpp_xml:get_path(VCARD, - [{element, 'ORG'}, {element, 'ORGUNIT'}, cdata_as_list]), - EMail = case EMail1 of - "" -> - EMail2; - _ -> - EMail1 - end, - - try - LUser = exmpp_stringprep:nodeprep(User), - LFN = exmpp_stringprep:to_lower(FN), - LFamily = exmpp_stringprep:to_lower(Family), - LGiven = exmpp_stringprep:to_lower(Given), - LMiddle = exmpp_stringprep:to_lower(Middle), - LNickname = exmpp_stringprep:to_lower(Nickname), - LBDay = exmpp_stringprep:to_lower(BDay), - LCTRY = exmpp_stringprep:to_lower(CTRY), - LLocality = exmpp_stringprep:to_lower(Locality), - LEMail = exmpp_stringprep:to_lower(EMail), - LOrgName = exmpp_stringprep:to_lower(OrgName), - LOrgUnit = exmpp_stringprep:to_lower(OrgUnit), - - Username = ejabberd_odbc:escape(User), - LUsername = ejabberd_odbc:escape(LUser), - SVCARD = ejabberd_odbc:escape(exmpp_xml:document_to_list(VCARD)), - - SFN = ejabberd_odbc:escape(FN), - SLFN = ejabberd_odbc:escape(LFN), - SFamily = ejabberd_odbc:escape(Family), - SLFamily = ejabberd_odbc:escape(LFamily), - SGiven = ejabberd_odbc:escape(Given), - SLGiven = ejabberd_odbc:escape(LGiven), - SMiddle = ejabberd_odbc:escape(Middle), - SLMiddle = ejabberd_odbc:escape(LMiddle), - SNickname = ejabberd_odbc:escape(Nickname), - SLNickname = ejabberd_odbc:escape(LNickname), - SBDay = ejabberd_odbc:escape(BDay), - SLBDay = ejabberd_odbc:escape(LBDay), - SCTRY = ejabberd_odbc:escape(CTRY), - SLCTRY = ejabberd_odbc:escape(LCTRY), - SLocality = ejabberd_odbc:escape(Locality), - SLLocality = ejabberd_odbc:escape(LLocality), - SEMail = ejabberd_odbc:escape(EMail), - SLEMail = ejabberd_odbc:escape(LEMail), - SOrgName = ejabberd_odbc:escape(OrgName), - SLOrgName = ejabberd_odbc:escape(LOrgName), - SOrgUnit = ejabberd_odbc:escape(OrgUnit), - SLOrgUnit = ejabberd_odbc:escape(LOrgUnit), - - odbc_queries:set_vcard(LServer, LUsername, SBDay, SCTRY, SEMail, - SFN, SFamily, SGiven, SLBDay, SLCTRY, - SLEMail, SLFN, SLFamily, SLGiven, - SLLocality, SLMiddle, SLNickname, - SLOrgName, SLOrgUnit, SLocality, - SMiddle, SNickname, SOrgName, - SOrgUnit, SVCARD, Username), - - LServerB = list_to_binary(LServer), - ejabberd_hooks:run(vcard_set, LServerB, [list_to_binary(LUser), LServerB, VCARD]) - - catch - _ -> - {error, badarg} - end. - --define(TLFIELD(Type, Label, Var), - #xmlel{ns = ?NS_VCARD, name = 'field', attrs = [ - ?XMLATTR(<<"type">>, Type), - ?XMLATTR(<<"label">>, translate:translate(Lang, Label)), - ?XMLATTR(<<"var">>, Var)]}). - - --define(FORM(JID), - [#xmlel{ns = ?NS_SEARCH, name = 'instructions', children = - [#xmlcdata{cdata = list_to_binary(translate:translate(Lang, "You need an x:data capable client to search"))}]}, - #xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = - [?XMLATTR(<<"type">>, <<"form">>)], children = - [#xmlel{ns = ?NS_DATA_FORMS, name = 'title', children = - [#xmlcdata{cdata = list_to_binary(translate:translate(Lang, "Search users in ") ++ exmpp_jid:to_list(JID))}]}, - #xmlel{ns = ?NS_SEARCH, name = 'instructions', children = - [#xmlcdata{cdata = list_to_binary(translate:translate(Lang, - "Fill in the form to search " - "for any matching Jabber User " - "(Add * to the end of field to " - "match substring)"))}]}, - ?TLFIELD(<<"text-single">>, "User", <<"user">>), - ?TLFIELD(<<"text-single">>, "Full Name", <<"fn">>), - ?TLFIELD(<<"text-single">>, "Name", <<"first">>), - ?TLFIELD(<<"text-single">>, "Middle Name", <<"middle">>), - ?TLFIELD(<<"text-single">>, "Family Name", <<"last">>), - ?TLFIELD(<<"text-single">>, "Nickname", <<"nick">>), - ?TLFIELD(<<"text-single">>, "Birthday", <<"bday">>), - ?TLFIELD(<<"text-single">>, "Country", <<"ctry">>), - ?TLFIELD(<<"text-single">>, "City", <<"locality">>), - ?TLFIELD(<<"text-single">>, "Email", <<"email">>), - ?TLFIELD(<<"text-single">>, "Organization Name", <<"orgname">>), - ?TLFIELD(<<"text-single">>, "Organization Unit", <<"orgunit">>) - ]}]). - -do_route(global, From, To, Packet) -> - Host = exmpp_jid:prep_domain_as_list(To), - ServerHost = ejabberd_global_router:server_host(Host, self()), - do_route(ServerHost, From, To, Packet); -do_route(ServerHost, From, To, Packet) -> - User = exmpp_jid:node(To), - Resource = exmpp_jid:resource(To), - if - (User /= undefined) or (Resource /= undefined) -> - Err = exmpp_stanza:reply_with_error(Packet, 'service-unavailable'), - ejabberd_router:route(To, From, Err); - true -> - try - Request = exmpp_iq:get_request(Packet), - Type = exmpp_iq:get_type(Packet), - Lang = exmpp_stanza:get_lang(Packet), - case {Type, Request#xmlel.ns} of - {set, ?NS_SEARCH} -> - XDataEl = find_xdata_el(Request), - case XDataEl of - false -> - Err = exmpp_iq:error(Packet, 'bad-request'), - ejabberd_router:route(To, From, Err); - _ -> - XData = jlib:parse_xdata_submit(XDataEl), - case XData of - invalid -> - Err = exmpp_iq:error(Packet, 'bad-request'), - ejabberd_router:route(To, From, Err); - _ -> - Result = #xmlel{ns = ?NS_SEARCH, - name = 'query', - children = [ - #xmlel{ns = ?NS_DATA_FORMS, - name = 'x', - attrs = [ - ?XMLATTR(<<"type">>, - <<"result">>)], - children = search_result(Lang, - To, ServerHost, XData)}]}, - ResIQ = exmpp_iq:result(Packet, Result), - ejabberd_router:route( - To, From, ResIQ) - end - end; - {get, ?NS_SEARCH} -> - Result = #xmlel{ns = ?NS_SEARCH, name = 'query', - children = ?FORM(To)}, - ResIQ = exmpp_iq:result(Packet, Result), - ejabberd_router:route(To, - From, - ResIQ); - {set, ?NS_DISCO_INFO} -> - Err = exmpp_iq:error(Packet, 'not-allowed'), - ejabberd_router:route(To, From, Err); - {get, ?NS_DISCO_INFO} -> - ServerHostB = list_to_binary(ServerHost), - Info = ejabberd_hooks:run_fold( - disco_info, ServerHostB, [], - [ServerHost, ?MODULE, <<>>, ""]), - Result = #xmlel{ns = ?NS_DISCO_INFO, name = 'query', - children = Info ++ [ - #xmlel{ns = ?NS_DISCO_INFO, name = 'identity', - attrs = [ - ?XMLATTR(<<"category">>, <<"directory">>), - ?XMLATTR(<<"type">>, <<"user">>), - ?XMLATTR(<<"name">>, translate:translate(Lang, - "vCard User Search"))]}, - #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', - attrs = [ - ?XMLATTR(<<"var">>, ?NS_SEARCH_s)]}, - #xmlel{ns = ?NS_DISCO_INFO, name = 'feature', - attrs = [ - ?XMLATTR(<<"var">>, ?NS_VCARD_s)]} - ]}, - ResIQ = exmpp_iq:result(Packet, Result), - ejabberd_router:route(To, - From, - ResIQ); - {set, ?NS_DISCO_ITEMS} -> - Err = exmpp_iq:error(Packet, 'not-allowed'), - ejabberd_router:route(To, From, Err); - {get, ?NS_DISCO_ITEMS} -> - Result = #xmlel{ns = ?NS_DISCO_ITEMS, name = 'query'}, - ResIQ = exmpp_iq:result(Packet, Result), - ejabberd_router:route(To, - From, - ResIQ); - {get, ?NS_VCARD} -> - Result = #xmlel{ns = ?NS_VCARD, name = 'vCard', - children = iq_get_vcard(Lang)}, - ResIQ = exmpp_iq:result(Packet, Result), - ejabberd_router:route(To, - From, - ResIQ); - _ -> - Err = exmpp_iq:error(Packet, 'service-unavailable'), - ejabberd_router:route(To, From, Err) - end - catch - _ -> - Err1 = exmpp_iq:error(Packet, 'service-unavailable'), - ejabberd_router:route(To, From, Err1) - end - end. - -iq_get_vcard(Lang) -> - [ - #xmlel{ns = ?NS_SEARCH, name = 'FN', children = [ - #xmlcdata{cdata = <<"ejabberd/mod_vcard">>}]}, - #xmlel{ns = ?NS_SEARCH, name = 'URL', children = [ - #xmlcdata{cdata = list_to_binary(?EJABBERD_URI)}]}, - #xmlel{ns = ?NS_SEARCH, name ='DESC', children = [ - #xmlcdata{cdata = list_to_binary( - translate:translate(Lang, "ejabberd vCard module") ++ - "\nCopyright (c) 2003-2010 ProcessOne")}]} - ]. - -find_xdata_el(#xmlel{children = SubEls}) -> - find_xdata_el1(SubEls). - -find_xdata_el1([]) -> - false; -find_xdata_el1([#xmlel{ns = ?NS_DATA_FORMS} = El | _Els]) -> - El; -find_xdata_el1([_ | Els]) -> - find_xdata_el1(Els). - -search_result(Lang, JID, ServerHost, Data) -> - [#xmlel{ns = ?NS_DATA_FORMS, name = 'title', children = - [#xmlcdata{cdata = list_to_binary( - translate:translate(Lang, "Search Results for ") ++ - exmpp_jid:to_list(JID))}]}, - #xmlel{ns = ?NS_DATA_FORMS, name = 'reported', children = - [?TLFIELD(<<"text-single">>, "Jabber ID", <<"jid">>), - ?TLFIELD(<<"text-single">>, "Full Name", <<"fn">>), - ?TLFIELD(<<"text-single">>, "Name", <<"first">>), - ?TLFIELD(<<"text-single">>, "Middle Name", <<"middle">>), - ?TLFIELD(<<"text-single">>, "Family Name", <<"last">>), - ?TLFIELD(<<"text-single">>, "Nickname", <<"nick">>), - ?TLFIELD(<<"text-single">>, "Birthday", <<"bday">>), - ?TLFIELD(<<"text-single">>, "Country", <<"ctry">>), - ?TLFIELD(<<"text-single">>, "City", <<"locality">>), - ?TLFIELD(<<"text-single">>, "Email", <<"email">>), - ?TLFIELD(<<"text-single">>, "Organization Name", <<"orgname">>), - ?TLFIELD(<<"text-single">>, "Organization Unit", <<"orgunit">>) - ]}] ++ lists:map(fun(R) -> record_to_item(ServerHost, R) end, - search(ServerHost, Data)). - --define(FIELD(Var, Val), - #xmlel{ns = ?NS_DATA_FORMS, name = 'field', attrs = - [?XMLATTR(<<"var">>, Var)], children = - [#xmlel{ns = ?NS_DATA_FORMS, name = 'value', children = - [#xmlcdata{cdata = Val}]}]}). - - -record_to_item(LServer, {Username, FN, Family, Given, Middle, - Nickname, BDay, CTRY, Locality, - EMail, OrgName, OrgUnit}) -> - #xmlel{ns = ?NS_DATA_FORMS, name = 'item', children = - [ - ?FIELD(<<"jid">>, list_to_binary(Username ++ "@" ++ LServer)), - ?FIELD(<<"fn">>, list_to_binary(FN)), - ?FIELD(<<"last">>, list_to_binary(Family)), - ?FIELD(<<"first">>, list_to_binary(Given)), - ?FIELD(<<"middle">>, list_to_binary(Middle)), - ?FIELD(<<"nick">>, list_to_binary(Nickname)), - ?FIELD(<<"bday">>, list_to_binary(BDay)), - ?FIELD(<<"ctry">>, list_to_binary(CTRY)), - ?FIELD(<<"locality">>, list_to_binary(Locality)), - ?FIELD(<<"email">>, list_to_binary(EMail)), - ?FIELD(<<"orgname">>, list_to_binary(OrgName)), - ?FIELD(<<"orgunit">>, list_to_binary(OrgUnit)) - ] - }. - - -search(LServer, Data) -> - MatchSpec = make_matchspec(LServer, Data), - Limit = case gen_mod:get_module_opt(LServer, ?MODULE, - matches, ?JUD_MATCHES) of - infinity -> - ""; - Val when is_integer(Val) and (Val > 0) -> - [" LIMIT ", integer_to_list(Val)]; - Val -> - ?ERROR_MSG("Illegal option value ~p. " - "Default value ~p substituted.", - [{matches, Val}, ?JUD_MATCHES]), - [" LIMIT ", integer_to_list(?JUD_MATCHES)] - end, - case catch odbc_queries:search_vcard(LServer, MatchSpec, Limit) of - {selected, ["username", "fn", "family", "given", "middle", - "nickname", "bday", "ctry", "locality", - "email", "orgname", "orgunit"], - Rs} when is_list(Rs) -> - Rs; - Error -> - ?ERROR_MSG("~p", [Error]), - [] - end. - - -make_matchspec(LServer, Data) -> - Host = ejabberd_odbc:escape(LServer), - filter_fields(Data, ["host = '", Host, "'"], LServer). - -filter_fields([], Match, _LServer) -> - [" where ", Match]; -filter_fields([{SVar, [Val]} | Ds], Match, LServer) - when is_list(Val) and (Val /= "") -> - LVal = exmpp_stringprep:to_lower(Val), - NewMatch = case SVar of - "user" -> make_val(Match, "lusername", LVal); - "fn" -> make_val(Match, "lfn", LVal); - "last" -> make_val(Match, "lfamily", LVal); - "first" -> make_val(Match, "lgiven", LVal); - "middle" -> make_val(Match, "lmiddle", LVal); - "nick" -> make_val(Match, "lnickname", LVal); - "bday" -> make_val(Match, "lbday", LVal); - "ctry" -> make_val(Match, "lctry", LVal); - "locality" -> make_val(Match, "llocality", LVal); - "email" -> make_val(Match, "lemail", LVal); - "orgname" -> make_val(Match, "lorgname", LVal); - "orgunit" -> make_val(Match, "lorgunit", LVal); - _ -> Match - end, - filter_fields(Ds, NewMatch, LServer); -filter_fields([_ | Ds], Match, LServer) -> - filter_fields(Ds, Match, LServer). - -make_val(Match, Field, Val) -> - Condition = - case lists:suffix("*", Val) of - true -> - Val1 = lists:sublist(Val, length(Val) - 1), - SVal = ejabberd_odbc:escape_like(Val1) ++ "%", - [Field, " LIKE '", SVal, "'"]; - _ -> - SVal = ejabberd_odbc:escape(Val), - [Field, " = '", SVal, "'"] - end, - [Match, " and ", Condition]. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%set_vcard_t(R, _) -> -% US = R#vcard.us, -% User = US, -% VCARD = R#vcard.vcard, -% -% FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]), -% Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]), -% Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]), -% Middle = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"}, cdata]), -% Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"}, cdata]), -% BDay = xml:get_path_s(VCARD, [{elem, "BDAY"}, cdata]), -% CTRY = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "CTRY"}, cdata]), -% Locality = xml:get_path_s(VCARD, [{elem, "ADR"}, {elem, "LOCALITY"},cdata]), -% EMail = xml:get_path_s(VCARD, [{elem, "EMAIL"}, cdata]), -% OrgName = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGNAME"}, cdata]), -% OrgUnit = xml:get_path_s(VCARD, [{elem, "ORG"}, {elem, "ORGUNIT"}, cdata]), -% -% {LUser, _LServer} = US, -% LFN = stringprep:tolower(FN), -% LFamily = stringprep:tolower(Family), -% LGiven = stringprep:tolower(Given), -% LMiddle = stringprep:tolower(Middle), -% LNickname = stringprep:tolower(Nickname), -% LBDay = stringprep:tolower(BDay), -% LCTRY = stringprep:tolower(CTRY), -% LLocality = stringprep:tolower(Locality), -% LEMail = stringprep:tolower(EMail), -% LOrgName = stringprep:tolower(OrgName), -% LOrgUnit = stringprep:tolower(OrgUnit), -% -% if -% (LUser == error) or -% (LFN == error) or -% (LFamily == error) or -% (LGiven == error) or -% (LMiddle == error) or -% (LNickname == error) or -% (LBDay == error) or -% (LCTRY == error) or -% (LLocality == error) or -% (LEMail == error) or -% (LOrgName == error) or -% (LOrgUnit == error) -> -% {error, badarg}; -% true -> -% mnesia:write( -% #vcard_search{us = US, -% user = User, luser = LUser, -% fn = FN, lfn = LFN, -% family = Family, lfamily = LFamily, -% given = Given, lgiven = LGiven, -% middle = Middle, lmiddle = LMiddle, -% nickname = Nickname, lnickname = LNickname, -% bday = BDay, lbday = LBDay, -% ctry = CTRY, lctry = LCTRY, -% locality = Locality, llocality = LLocality, -% email = EMail, lemail = LEMail, -% orgname = OrgName, lorgname = LOrgName, -% orgunit = OrgUnit, lorgunit = LOrgUnit -% }) -% end. -% -% -%reindex_vcards() -> -% F = fun() -> -% mnesia:foldl(fun set_vcard_t/2, [], vcard) -% end, -% mnesia:transaction(F). - - -remove_user(User, Server) when is_binary(User), is_binary(Server) -> - LUser = binary_to_list(exmpp_stringprep:nodeprep(User)), - LServer = binary_to_list(exmpp_stringprep:nameprep(Server)), - Username = ejabberd_odbc:escape(LUser), - odbc_queries:del_vcard(LServer, Username).