mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +01:00
added p1 modules
This commit is contained in:
parent
7be707f7bc
commit
59ae9bea76
337
src/http_p1.erl
Normal file
337
src/http_p1.erl
Normal file
@ -0,0 +1,337 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : http_p1.erl
|
||||
%%% Author : Emilio Bustos <ebustos@process-one.net>
|
||||
%%% Purpose : Provide a common API for inets / lhttpc / ibrowse
|
||||
%%% Created : 29 Jul 2010 by Emilio Bustos <ebustos@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% 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(http_p1).
|
||||
-author('ebustos@process-one.net').
|
||||
|
||||
-export([
|
||||
start/0,
|
||||
stop/0,
|
||||
get/1,
|
||||
get/2,
|
||||
post/2,
|
||||
post/3,
|
||||
request/3,
|
||||
request/4,
|
||||
request/5
|
||||
]).
|
||||
|
||||
% -define(USE_INETS, 1).
|
||||
% -define(USE_LHTTPC, 1).
|
||||
% -define(USE_IBROWSE, 1).
|
||||
% inets used as default if none specified
|
||||
|
||||
-ifdef(USE_IBROWSE).
|
||||
-define(start(), start_ibrowse()).
|
||||
-define(request(M, U, H, B, O), request_ibrowse(M, U, H, B, O)).
|
||||
-define(stop(), stop_ibrowse()).
|
||||
-else.
|
||||
-ifdef(USE_LHTTPC).
|
||||
-define(start(), start_lhttpc()).
|
||||
-define(request(M, U, H, B, O), request_lhttpc(M, U, H, B, O)).
|
||||
-define(stop(), stop_lhttpc()).
|
||||
-else.
|
||||
-define(start(), start_inets()).
|
||||
-define(request(M, U, H, B, O), request_inets(M, U, H, B, O)).
|
||||
-define(stop(), stop_inets()).
|
||||
-endif.
|
||||
-endif.
|
||||
|
||||
-type header() :: {string() | atom(), string()}.
|
||||
-type headers() :: [header()].
|
||||
|
||||
-type option() ::
|
||||
{connect_timeout, timeout()} |
|
||||
{timeout, timeout()} |
|
||||
|
||||
{send_retry, non_neg_integer()} |
|
||||
{partial_upload, non_neg_integer() | infinity} |
|
||||
{partial_download, pid(), non_neg_integer() | infinity}.
|
||||
|
||||
-type options() :: [option()].
|
||||
|
||||
-type result() :: {ok, {{pos_integer(), string()}, headers(), string()}} |
|
||||
{error, atom()}.
|
||||
|
||||
%% @spec () -> ok | {error, Reason}
|
||||
%% Reason = term()
|
||||
%% @doc
|
||||
%% Start the application.
|
||||
%% This is a helper function that will start the corresponding backend.
|
||||
%% It allows the library to be started using the `-s' flag.
|
||||
%% For instance:
|
||||
%% `$ erl -s http_p1'
|
||||
%%
|
||||
%% @end
|
||||
-spec start() -> ok | {error, any()}.
|
||||
start() ->
|
||||
?start().
|
||||
|
||||
start_inets()->
|
||||
inets:start(),
|
||||
ssl:start().
|
||||
|
||||
start_lhttpc()->
|
||||
application:start(crypto),
|
||||
application:start(ssl),
|
||||
lhttpc:start().
|
||||
|
||||
start_ibrowse()->
|
||||
ibrowse:start(),
|
||||
ssl:start().
|
||||
|
||||
%% @spec () -> ok | {error, Reason}
|
||||
%% Reason = term()
|
||||
%% @doc
|
||||
%% Stops the application.
|
||||
%% This is a helper function that will stop the corresponding backend.
|
||||
%%
|
||||
%% @end
|
||||
-spec stop() -> ok | {error, any()}.
|
||||
stop() ->
|
||||
?stop().
|
||||
|
||||
stop_inets()->
|
||||
inets:stop(),
|
||||
ssl:stop().
|
||||
|
||||
stop_lhttpc()->
|
||||
lhttpc:stop(),
|
||||
application:stop(ssl).
|
||||
|
||||
stop_ibrowse()->
|
||||
ibrowse:stop().
|
||||
|
||||
%% @spec (URL) -> Result
|
||||
%% URL = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a GET request.
|
||||
%% Would be the same as calling `request(get, URL, [])',
|
||||
%% that is {@link request/3} with an empty header list.
|
||||
%% @end
|
||||
%% @see request/3
|
||||
-spec get(string()) -> result().
|
||||
get(URL) ->
|
||||
request(get, URL, []).
|
||||
|
||||
%% @spec (URL, Hdrs) -> Result
|
||||
%% URL = string()
|
||||
%% Hdrs = [{Header, Value}]
|
||||
%% Header = string()
|
||||
%% Value = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a GET request.
|
||||
%% Would be the same as calling `request(get, URL, Hdrs)'.
|
||||
%% @end
|
||||
%% @see request/3
|
||||
-spec get(string(), headers()) -> result().
|
||||
get(URL, Hdrs) ->
|
||||
request(get, URL, Hdrs).
|
||||
|
||||
%% @spec (URL, RequestBody) -> Result
|
||||
%% URL = string()
|
||||
%% RequestBody = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a POST request with form data.
|
||||
%% Would be the same as calling
|
||||
%% `request(post, URL, [{"content-type", "x-www-form-urlencoded"}], Body)'.
|
||||
%% @end
|
||||
%% @see request/4
|
||||
-spec post(string(), string()) -> result().
|
||||
post(URL, Body) ->
|
||||
request(post, URL, [{"content-type", "x-www-form-urlencoded"}], Body).
|
||||
|
||||
%% @spec (URL, Hdrs, RequestBody) -> Result
|
||||
%% URL = string()
|
||||
%% Hdrs = [{Header, Value}]
|
||||
%% Header = string()
|
||||
%% Value = string()
|
||||
%% RequestBody = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a POST request.
|
||||
%% Would be the same as calling
|
||||
%% `request(post, URL, Hdrs, Body)'.
|
||||
%% @end
|
||||
%% @see request/4
|
||||
-spec post(string(), headers(), string()) -> result().
|
||||
post(URL, Hdrs, Body) ->
|
||||
NewHdrs = case [X || {X,_}<-Hdrs, string:to_lower(X) == "content-type"] of
|
||||
[] ->
|
||||
[{"content-type", "x-www-form-urlencoded"} | Hdrs];
|
||||
_ ->
|
||||
Hdrs
|
||||
end,
|
||||
request(post, URL, NewHdrs, Body).
|
||||
|
||||
%% @spec (Method, URL, Hdrs) -> Result
|
||||
%% Method = atom()
|
||||
%% URL = string()
|
||||
%% Hdrs = [{Header, Value}]
|
||||
%% Header = string()
|
||||
%% Value = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a request without a body.
|
||||
%% Would be the same as calling `request(Method, URL, Hdrs, [], [])',
|
||||
%% that is {@link request/5} with an empty body.
|
||||
%% @end
|
||||
%% @see request/5
|
||||
-spec request(atom(), string(), headers()) -> result().
|
||||
request(Method, URL, Hdrs) ->
|
||||
request(Method, URL, Hdrs, [], []).
|
||||
|
||||
%% @spec (Method, URL, Hdrs, RequestBody) -> Result
|
||||
%% Method = atom()
|
||||
%% URL = string()
|
||||
%% Hdrs = [{Header, Value}]
|
||||
%% Header = string()
|
||||
%% Value = string()
|
||||
%% RequestBody = string()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a request with a body.
|
||||
%% Would be the same as calling
|
||||
%% `request(Method, URL, Hdrs, Body, [])', that is {@link request/5}
|
||||
%% with no options.
|
||||
%% @end
|
||||
%% @see request/5
|
||||
-spec request(atom(), string(), headers(), string()) -> result().
|
||||
request(Method, URL, Hdrs, Body) ->
|
||||
request(Method, URL, Hdrs, Body, []).
|
||||
|
||||
%% @spec (Method, URL, Hdrs, RequestBody, Options) -> Result
|
||||
%% Method = atom()
|
||||
%% URL = string()
|
||||
%% Hdrs = [{Header, Value}]
|
||||
%% Header = string()
|
||||
%% Value = string()
|
||||
%% RequestBody = string()
|
||||
%% Options = [Option]
|
||||
%% Option = {timeout, Milliseconds | infinity} |
|
||||
%% {connect_timeout, Milliseconds | infinity} |
|
||||
%% {socket_options, [term()]} |
|
||||
|
||||
%% Milliseconds = integer()
|
||||
%% Result = {ok, StatusCode, Hdrs, ResponseBody}
|
||||
%% | {error, Reason}
|
||||
%% StatusCode = integer()
|
||||
%% ResponseBody = string()
|
||||
%% Reason = connection_closed | connect_timeout | timeout
|
||||
%% @doc Sends a request with a body.
|
||||
%% Would be the same as calling
|
||||
%% `request(Method, URL, Hdrs, Body, [])', that is {@link request/5}
|
||||
%% with no options.
|
||||
%% @end
|
||||
%% @see request/5
|
||||
-spec request(atom(), string(), headers(), string(), options()) -> result().
|
||||
request(Method, URL, Hdrs, Body, Opts) ->
|
||||
% ?DEBUG("Making request with headers: ~p~n~n", [Hdrs]),
|
||||
% Headers = lists:map(fun({H, V}) ->
|
||||
% H2 = if
|
||||
% is_atom(H) ->
|
||||
% string:to_lower(atom_to_list(H));
|
||||
% is_list(H) ->
|
||||
% string:to_lower(H);
|
||||
% true ->
|
||||
% H
|
||||
% end,
|
||||
% {H2, V}
|
||||
% end, Hdrs),
|
||||
?request(Method, URL, Hdrs, Body, Opts).
|
||||
|
||||
request_inets(Method, URL, Hdrs, Body, Opts) ->
|
||||
Request = case Method of
|
||||
get ->
|
||||
{URL, Hdrs};
|
||||
head ->
|
||||
{URL, Hdrs};
|
||||
_ -> % post, etc.
|
||||
{URL, Hdrs, proplists:get_value("content-type", Hdrs, []), Body}
|
||||
end,
|
||||
Options = case proplists:get_value(timeout, Opts, infinity) of
|
||||
infinity ->
|
||||
proplists:delete(timeout, Opts);
|
||||
_ ->
|
||||
Opts
|
||||
end,
|
||||
case http:request(Method, Request, Options, []) of
|
||||
{ok, {{_, Status, _}, Headers, Response}} ->
|
||||
{ok, Status, Headers, Response};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
request_lhttpc(Method, URL, Hdrs, Body, Opts) ->
|
||||
TimeOut = proplists:get_value(timeout, Opts, infinity),
|
||||
SockOpt = proplists:get_value(socket_options, Opts, []),
|
||||
Options = [{connect_options, SockOpt} | proplists:delete(timeout, Opts)],
|
||||
case lhttpc:request(URL, Method, Hdrs, Body, TimeOut, Options) of
|
||||
{ok, {{Status, _Reason}, Headers, Response}} ->
|
||||
{ok, Status, Headers, binary_to_list(Response)};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
request_ibrowse(Method, URL, Hdrs, Body, Opts) ->
|
||||
TimeOut = proplists:get_value(timeout, Opts, infinity),
|
||||
Options = [{inactivity_timeout, TimeOut} | proplists:delete(timeout, Opts)],
|
||||
case ibrowse:send_req(URL, Hdrs, Method, Body, Options) of
|
||||
{ok, Status, Headers, Response} ->
|
||||
{ok, list_to_integer(Status), Headers, Response};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
% ibrowse {response_format, response_format()} |
|
||||
% Options - [option()]
|
||||
% Option - {sync, boolean()} | {stream, StreamTo} | {body_format, body_format()} | {full_result,
|
||||
% boolean()} | {headers_as_is, boolean()}
|
||||
%body_format() = string() | binary()
|
||||
% The body_format option is only valid for the synchronous request and the default is string.
|
||||
% When making an asynchronous request the body will always be received as a binary.
|
||||
% lhttpc: always binary
|
1166
src/mod_admin_p1.erl
Normal file
1166
src/mod_admin_p1.erl
Normal file
File diff suppressed because it is too large
Load Diff
@ -85,7 +85,7 @@ loop(_State) ->
|
||||
%% TODO: Support comment lines starting by %
|
||||
update_bl_c2s() ->
|
||||
?INFO_MSG("Updating C2S Blacklist", []),
|
||||
case http:request(?BLC2S) of
|
||||
case http_p1:request(?BLC2S) of
|
||||
{ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
|
||||
IPs = string:tokens(Body,"\n"),
|
||||
ets:delete_all_objects(bl_c2s),
|
||||
|
875
src/mod_xmlrpc.erl
Normal file
875
src/mod_xmlrpc.erl
Normal file
@ -0,0 +1,875 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_xmlrpc.erl
|
||||
%%% Author : Badlop / Mickael Remond / Christophe Romain
|
||||
%%% Purpose : XML-RPC server
|
||||
%%% Created :
|
||||
%%% Id :
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
%%%/***************************************************************************
|
||||
%%% * *
|
||||
%%% * 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. *
|
||||
%%% * *
|
||||
%%% ***************************************************************************/
|
||||
%%%
|
||||
%%%
|
||||
%%% MOD_XMLRPC - an XML-RPC server module for ejabberd
|
||||
%%%
|
||||
%%% v0.5 - 17 March 2008
|
||||
%%%
|
||||
%%% http://ejabberd.jabber.ru/mod_xmlrpc
|
||||
%%%
|
||||
%%% (C) 2005, Badlop
|
||||
%%% 2006, Process-one
|
||||
%%% 2007, Process-one
|
||||
%%% 2008, Process-one
|
||||
%%%
|
||||
%%% Changelog:
|
||||
%%%
|
||||
%%% 0.7 - 02 April 2009 - cromain
|
||||
%%% - add user nick change
|
||||
%%%
|
||||
%%% 0.6 - 02 June 2008 - cromain
|
||||
%%% - add user existance checking
|
||||
%%% - improve parameter checking
|
||||
%%% - allow orderless parameter
|
||||
%%%
|
||||
%%% 0.5 - 17 March 2008 - cromain
|
||||
%%% - add user changing and higher level methods
|
||||
%%%
|
||||
%%% 0.4 - 18 February 2008 - cromain
|
||||
%%% - add roster handling
|
||||
%%% - add message sending
|
||||
%%% - code and api clean-up
|
||||
%%%
|
||||
%%% 0.3 - 18 October 2007 - cromain
|
||||
%%% - presence improvement
|
||||
%%% - add new functionality
|
||||
%%%
|
||||
%%% 0.2 - 4 March 2006 - mremond
|
||||
%%% - Code clean-up
|
||||
%%% - Made it compatible with current ejabberd SVN version
|
||||
%%%
|
||||
%%% 0.1.2 - 28 December 2005
|
||||
%%% - Now compatible with ejabberd 1.0.0
|
||||
%%% - The XMLRPC server is started only once, not once for every virtual host
|
||||
%%% - Added comments for handlers. Every available handler must be explained
|
||||
%%%
|
||||
|
||||
-module(mod_xmlrpc).
|
||||
-author('Process-one').
|
||||
-vsn('0.6').
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2,
|
||||
handler/2,
|
||||
link_contacts/5, unlink_contacts/3, %% used by Nimbuzz
|
||||
loop/1,
|
||||
stop/1]).
|
||||
|
||||
-export([add_rosteritem/6]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
-ifdef(EJABBERD1).
|
||||
-record(session, {sid, usr, us, priority}). %% ejabberd 1.1.x
|
||||
-else.
|
||||
-record(session, {sid, usr, us, priority, info}). %% ejabberd 2.0.x
|
||||
-endif.
|
||||
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_xmlrpc).
|
||||
-define(PORT, 4560).
|
||||
-define(TIMEOUT, 5000).
|
||||
|
||||
%% -----------------------------
|
||||
%% Module interface
|
||||
%% -----------------------------
|
||||
|
||||
start(_Host, Opts) ->
|
||||
case whereis(?PROCNAME) of
|
||||
undefined ->
|
||||
%% get options
|
||||
Port = gen_mod:get_opt(port, Opts, ?PORT),
|
||||
MaxSessions = 10,
|
||||
Timeout = gen_mod:get_opt(timeout, Opts, ?TIMEOUT),
|
||||
Handler = {mod_xmlrpc, handler},
|
||||
State = tryit,
|
||||
|
||||
%% TODO: this option gives
|
||||
%% error_info: {function_clause,[{gen_tcp,mod,[{ip,{127,0,0,1}}]},
|
||||
%%case gen_mod:get_opt(listen_all, Opts, false) of
|
||||
%% true -> Ip = all;
|
||||
%% false -> Ip = {127, 0, 0, 1}
|
||||
%%end,
|
||||
Ip = all,
|
||||
|
||||
%% start the XML-RPC server
|
||||
{ok, Pid} = xmlrpc:start_link(Ip, Port, MaxSessions, Timeout, Handler, State),
|
||||
|
||||
%% start the loop process
|
||||
register(?PROCNAME, spawn(?MODULE, loop, [Pid])),
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
loop(Pid) ->
|
||||
receive
|
||||
stop ->
|
||||
xmlrpc:stop(Pid)
|
||||
end.
|
||||
|
||||
stop(_Host) ->
|
||||
case whereis(?PROCNAME) of
|
||||
undefined ->
|
||||
ok;
|
||||
_Pid ->
|
||||
?PROCNAME ! stop,
|
||||
unregister(?PROCNAME)
|
||||
end.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Handlers
|
||||
%% -----------------------------
|
||||
|
||||
handler(tryit, Call) ->
|
||||
try handler(notry, Call) of
|
||||
Result -> Result
|
||||
catch
|
||||
A:B ->
|
||||
?ERROR_MSG("Problem '~p' in~nCall: ~p~nError: ~p", [A, Call, B]),
|
||||
{false, {response, [-100]}}
|
||||
end;
|
||||
|
||||
% Call: Arguments: Returns:
|
||||
|
||||
%% .............................
|
||||
%% Debug
|
||||
|
||||
%% echothis String String
|
||||
handler(_State, {call, echothis, [A]}) ->
|
||||
{false, {response, [A]}};
|
||||
|
||||
%% multhis struct[{a, Integer}, {b, Integer}] Integer
|
||||
handler(_State, {call, multhis, [{struct, Struct}]}) ->
|
||||
[{a, A}, {b, B}] = lists:sort(Struct),
|
||||
{false, {response, [A*B]}};
|
||||
|
||||
%% .............................
|
||||
%% User administration
|
||||
|
||||
%% create_account struct[{user, String}, {server, Server}, {password, String}] Integer
|
||||
handler(_State, {call, create_account, [{struct, Struct}]}) ->
|
||||
[{password, P}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:try_register(U, S, P) of
|
||||
{atomic, ok} ->
|
||||
{false, {response, [0]}};
|
||||
{atomic, exists} ->
|
||||
{false, {response, [409]}};
|
||||
_ ->
|
||||
{false, {response, [1]}}
|
||||
end;
|
||||
|
||||
%% delete_account struct[{user, String}, {server, Server}] Integer
|
||||
handler(_State, {call, delete_account, [{struct, Struct}]}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> ejabberd_auth:remove_user(U, S) end,
|
||||
user_action(U, S, Fun, ok);
|
||||
|
||||
%% change_password struct[{user, String}, {server, String}, {newpass, String}] Integer
|
||||
handler(_State, {call, change_password, [{struct, Struct}]}) ->
|
||||
[{newpass, P}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> ejabberd_auth:set_password(U, S, P) end,
|
||||
user_action(U, S, Fun, ok);
|
||||
|
||||
%% set_nickname struct[{user, String}, {server, String}, {nick, String}] Integer
|
||||
handler(_State, {call, set_nickname, [{struct, Struct}]}) ->
|
||||
[{nick, N}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> case mod_vcard:process_sm_iq(
|
||||
{jid, U, S, "", U, S, ""},
|
||||
{jid, U, S, "", U, S, ""},
|
||||
{iq, "", set, "", "en",
|
||||
{xmlelement, "vCard",
|
||||
[{"xmlns", "vcard-temp"}], [
|
||||
{xmlelement, "NICKNAME", [], [{xmlcdata, N}]}
|
||||
]
|
||||
}}) of
|
||||
{iq, [], result, [], _, []} -> ok;
|
||||
_ -> error
|
||||
end
|
||||
end,
|
||||
user_action(U, S, Fun, ok);
|
||||
|
||||
%% set_rosternick struct[{user, String}, {server, String}, {nick, String}] Integer
|
||||
handler(_State, {call, set_rosternick, [{struct, Struct}]}) ->
|
||||
[{nick, N}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> change_rosternick(U, S, N) end,
|
||||
user_action(U, S, Fun, ok);
|
||||
|
||||
%% add_rosteritem struct[{user, String}, {server, String},
|
||||
%% {jid, String}, {group, String}, {nick, String}, {subs, String}] Integer
|
||||
handler(_State, {call, add_rosteritem, [{struct, Struct}]}) ->
|
||||
[{group, G},{jid, JID},{nick, N},{server, S},{subs, Subs},{user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> add_rosteritem(U, S, JID, N, G, Subs) end,
|
||||
user_action(U, S, Fun, {atomic, ok});
|
||||
|
||||
%% link_contacts struct[{jid1, String}, {nick1, String}, {jid2, String}, {nick2, String}] Integer
|
||||
handler(_State, {call, link_contacts, [{struct, Struct}]}) ->
|
||||
[{jid1, JID1}, {jid2, JID2}, {nick1, Nick1}, {nick2, Nick2}] = lists:sort(Struct),
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
|
||||
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of
|
||||
{true, true} ->
|
||||
case link_contacts(JID1, Nick1, JID2, Nick2) of
|
||||
{atomic, ok} ->
|
||||
{false, {response, [0]}};
|
||||
_ ->
|
||||
{false, {response, [1]}}
|
||||
end;
|
||||
_ ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% delete_rosteritem struct[{user, String}, {server, String}, {jid, String}] Integer
|
||||
handler(_State, {call, delete_rosteritem, [{struct, Struct}]}) ->
|
||||
[{jid, JID}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
Fun = fun() -> del_rosteritem(U, S, JID) end,
|
||||
user_action(U, S, Fun, {atomic, ok});
|
||||
|
||||
%% unlink_contacts struct[{jid1, String}, {jid2, String}] Integer
|
||||
handler(_State, {call, unlink_contacts, [{struct, Struct}]}) ->
|
||||
[{jid1, JID1}, {jid2, JID2}] = lists:sort(Struct),
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
|
||||
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case {ejabberd_auth:is_user_exists(U1, S1), ejabberd_auth:is_user_exists(U2, S2)} of
|
||||
{true, true} ->
|
||||
case unlink_contacts(JID1, JID2) of
|
||||
{atomic, ok} ->
|
||||
{false, {response, [0]}};
|
||||
_ ->
|
||||
{false, {response, [1]}}
|
||||
end;
|
||||
_ ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% get_roster struct[{user, String}, {server, String}]
|
||||
%% array[struct[{jid, String}, {group, String}, {nick, String},
|
||||
%% {subscription, String}, {pending, String}]]
|
||||
handler(_State, {call, get_roster, [{struct, Struct}]}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
Roster = format_roster(get_roster(U, S)),
|
||||
{false, {response, [{array, Roster}]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% get_roster_with_presence struct[{user, String}, {server, String}]
|
||||
%% array[struct[{jid, String}, {resource, String}, {group, String}, {nick, String},
|
||||
%% {subscription, String}, {pending, String},
|
||||
%% {show, String}, {status, String}]]
|
||||
handler(_State, {call, get_roster_with_presence, [{struct, Struct}]}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
Roster = format_roster_with_presence(get_roster(U, S)),
|
||||
{false, {response, [{array, Roster}]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% get_presence struct[{user, String}, {server, String}]
|
||||
%% array[struct[{jid, String}, {show, String}, {status, String}]]
|
||||
handler(_State, {call, get_presence, [{struct, Struct}]}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
{Resource, Show, Status} = get_presence(U, S),
|
||||
FullJID = case Resource of
|
||||
[] ->
|
||||
lists:flatten([U,"@",S]);
|
||||
_ ->
|
||||
lists:flatten([U,"@",S,"/",Resource])
|
||||
end,
|
||||
R = {struct, [{jid, FullJID}, {show, Show}, {status, Status} ]},
|
||||
{false, {response, [R]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% get_resources struct[{user, String}, {server, String}]
|
||||
%% array[String]
|
||||
handler(_State, {call, get_resources, [{struct, Struct}]}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
Resources = get_resources(U, S),
|
||||
{false, {response, [{array, Resources}]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% send_chat struct[{from, String}, {to, String}, {body, String}]
|
||||
%% Integer
|
||||
handler(_State, {call, send_chat, [{struct, Struct}]}) ->
|
||||
[{body, Msg}, {from, FromJID}, {to, ToJID}] = lists:sort(Struct),
|
||||
From = jlib:string_to_jid(FromJID),
|
||||
To = jlib:string_to_jid(ToJID),
|
||||
Stanza = {xmlelement, "message", [{"type", "chat"}],
|
||||
[{xmlelement, "body", [], [{xmlcdata, Msg}]}]},
|
||||
ejabberd_router:route(From, To, Stanza),
|
||||
{false, {response, [0]}};
|
||||
|
||||
%% send_message struct[{from, String}, {to, String}, {subject, String}, {body, String}]
|
||||
%% Integer
|
||||
handler(_State, {call, send_message, [{struct, Struct}]}) ->
|
||||
[{body, Msg}, {from, FromJID}, {subject, Sub}, {to, ToJID}] = lists:sort(Struct),
|
||||
From = jlib:string_to_jid(FromJID),
|
||||
To = jlib:string_to_jid(ToJID),
|
||||
Stanza = {xmlelement, "message", [{"type", "normal"}],
|
||||
[{xmlelement, "subject", [], [{xmlcdata, Sub}]},
|
||||
{xmlelement, "body", [], [{xmlcdata, Msg}]}]},
|
||||
ejabberd_router:route(From, To, Stanza),
|
||||
{false, {response, [0]}};
|
||||
|
||||
%% send_stanza struct[{from, String}, {to, String}, {stanza, String}]
|
||||
%% Integer
|
||||
handler(_State, {call, send_stanza, [{struct, Struct}]}) ->
|
||||
[{from, FromJID}, {stanza, StanzaStr}, {to, ToJID}] = lists:sort(Struct),
|
||||
case xml_stream:parse_element(StanzaStr) of
|
||||
{error, _} ->
|
||||
{false, {response, [1]}};
|
||||
Stanza ->
|
||||
{xmlelement, _, Attrs, _} = Stanza,
|
||||
From = jlib:string_to_jid(proplists:get_value("from", Attrs, FromJID)),
|
||||
To = jlib:string_to_jid(proplists:get_value("to", Attrs, ToJID)),
|
||||
ejabberd_router:route(From, To, Stanza),
|
||||
{false, {response, [0]}}
|
||||
end;
|
||||
|
||||
%% rename_account struct[{user, String}, {server, String}, {newuser, String}, {newserver, String}]
|
||||
%% Integer
|
||||
handler(_State, {call, rename_account, [{struct, Struct}]}) ->
|
||||
[{newserver, NS}, {newuser, NU}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
case ejabberd_auth:get_password(U, S) of
|
||||
false ->
|
||||
{false, {response, [1]}};
|
||||
Password ->
|
||||
case ejabberd_auth:try_register(NU, NS, Password) of
|
||||
{atomic, ok} ->
|
||||
OldJID = jlib:jid_to_string({U, S, ""}),
|
||||
NewJID = jlib:jid_to_string({NU, NS, ""}),
|
||||
Roster = get_roster(U, S),
|
||||
lists:foreach(fun(#roster{jid={RU, RS, RE}, name=Nick, groups=Groups}) ->
|
||||
NewGroup = extract_group(Groups),
|
||||
{NewNick, Group} = case lists:filter(fun(#roster{jid={PU, PS, _}}) ->
|
||||
(PU == U) and (PS == S)
|
||||
end, get_roster(RU, RS)) of
|
||||
[#roster{name=OldNick, groups=OldGroups}|_] -> {OldNick, extract_group(OldGroups)};
|
||||
[] -> {NU, []}
|
||||
end,
|
||||
JIDStr = jlib:jid_to_string({RU, RS, RE}),
|
||||
link_contacts(NewJID, NewNick, NewGroup, JIDStr, Nick, Group),
|
||||
unlink_contacts(OldJID, JIDStr)
|
||||
end, Roster),
|
||||
ejabberd_auth:remove_user(U, S),
|
||||
{false, {response, [0]}};
|
||||
{atomic, exists} ->
|
||||
{false, {response, [409]}};
|
||||
_ ->
|
||||
{false, {response, [1]}}
|
||||
end
|
||||
end;
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% add_contacts struct[{user, String}, {server, String},
|
||||
%% array[struct[{jid, String}, {group, String}, {nick, String}]]]
|
||||
%% Integer
|
||||
handler(_State, {call, add_contacts, [{struct, Struct}]}) ->
|
||||
[{array, Contacts}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
JID1 = jlib:jid_to_string({U, S, ""}),
|
||||
Response = lists:foldl(fun({struct, Struct2}, Acc) ->
|
||||
[{group, Group}, {jid, JID2}, {nick, Nick}] = lists:sort(Struct2),
|
||||
{PU, PS, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case ejabberd_auth:is_user_exists(PU, PS) of
|
||||
true ->
|
||||
case link_contacts(JID1, "", "", JID2, Nick, Group) of
|
||||
{atomic, ok} -> Acc;
|
||||
_ -> 1
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end
|
||||
end, 0, element(2, Contacts)),
|
||||
{false, {response, [Response]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% remove_contacts struct[{user, String}, {server, String}, array[String]]
|
||||
%% Integer
|
||||
handler(_State, {call, remove_contacts, [{struct, Struct}]}) ->
|
||||
[{array, Contacts}, {server, S}, {user, U}] = lists:sort(Struct),
|
||||
case ejabberd_auth:is_user_exists(U, S) of
|
||||
true ->
|
||||
JID1 = jlib:jid_to_string({U, S, ""}),
|
||||
Response = lists:foldl(fun(JID2, Acc) ->
|
||||
{PU, PS, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case ejabberd_auth:is_user_exists(PU, PS) of
|
||||
true ->
|
||||
case unlink_contacts(JID1, JID2) of
|
||||
{atomic, ok} -> Acc;
|
||||
_ -> 1
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end
|
||||
end, 0, element(2, Contacts)),
|
||||
{false, {response, [Response]}};
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end;
|
||||
|
||||
%% check_users_registration array[struct[{user, String}, {server, String}]]
|
||||
%% array[struct[{user, String}, {server, String}, {status, Integer}]]
|
||||
handler(_State, {call, check_users_registration, [{array, Users}]}) ->
|
||||
Response = lists:map(fun({struct, Struct}) ->
|
||||
[{server, S}, {user, U}] = lists:sort(Struct),
|
||||
Registered = case ejabberd_auth:is_user_exists(U, S) of
|
||||
true -> 1;
|
||||
false -> 0
|
||||
end,
|
||||
{struct, [{user, U}, {server, S}, {status, Registered}]}
|
||||
end, Users),
|
||||
{false, {response, [{array, Response}]}};
|
||||
|
||||
|
||||
%% If no other guard matches
|
||||
handler(_State, Payload) ->
|
||||
FaultString = lists:flatten(io_lib:format("Unknown call: ~p", [Payload])),
|
||||
{false, {response, {fault, -1, FaultString}}}.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Internal roster handling
|
||||
%% -----------------------------
|
||||
|
||||
get_roster(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
ejabberd_hooks:run_fold(roster_get, LServer, [], [{LUser, LServer}]).
|
||||
|
||||
change_rosternick(User, Server, Nick) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LJID = {LUser, LServer, []},
|
||||
JID = jlib:jid_to_string(LJID),
|
||||
Push = fun(Subscription) ->
|
||||
jlib:iq_to_xml(#iq{type = set, xmlns = ?NS_ROSTER, id = "push",
|
||||
sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}],
|
||||
[{xmlelement, "item", [{"jid", JID}, {"name", Nick}, {"subscription", atom_to_list(Subscription)}],
|
||||
[]}]}]})
|
||||
end,
|
||||
Result = case roster_backend(Server) of
|
||||
mnesia ->
|
||||
%% XXX This way of doing can not work with s2s
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
lists:foreach(fun(Roster) ->
|
||||
{U, S} = Roster#roster.us,
|
||||
mnesia:write(Roster#roster{name = Nick}),
|
||||
lists:foreach(fun(R) ->
|
||||
UJID = jlib:make_jid(U, S, R),
|
||||
ejabberd_router:route(UJID, UJID, Push(Roster#roster.subscription))
|
||||
end, get_resources(U, S))
|
||||
end, mnesia:match_object(#roster{jid = LJID, _ = '_'}))
|
||||
end);
|
||||
odbc ->
|
||||
%%% XXX This way of doing does not work with several domains
|
||||
ejabberd_odbc:sql_transaction(Server,
|
||||
fun() ->
|
||||
SNick = ejabberd_odbc:escape(Nick),
|
||||
SJID = ejabberd_odbc:escape(JID),
|
||||
ejabberd_odbc:sql_query_t(
|
||||
["update rosterusers"
|
||||
" set nick='", SNick, "'"
|
||||
" where jid='", SJID, "';"]),
|
||||
case ejabberd_odbc:sql_query_t(
|
||||
["select username from rosterusers"
|
||||
" where jid='", SJID, "'"
|
||||
" and subscription = 'B';"]) of
|
||||
{selected, ["username"], Users} ->
|
||||
lists:foreach(fun({RU}) ->
|
||||
lists:foreach(fun(R) ->
|
||||
UJID = jlib:make_jid(RU, Server, R),
|
||||
ejabberd_router:route(UJID, UJID, Push(both))
|
||||
end, get_resources(RU, Server))
|
||||
end, Users);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end);
|
||||
none ->
|
||||
{error, no_roster}
|
||||
end,
|
||||
case Result of
|
||||
{atomic, ok} -> ok;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
add_rosteritem(User, Server, JID, Nick, Group, Subscription) ->
|
||||
add_rosteritem(User, Server, JID, Nick, Group, Subscription, true).
|
||||
add_rosteritem(User, Server, JID, Nick, Group, Subscription, Push) ->
|
||||
{RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
|
||||
LJID = {RU,RS,[]},
|
||||
Groups = case Group of
|
||||
[] -> [];
|
||||
_ -> [Group]
|
||||
end,
|
||||
Roster = #roster{
|
||||
usj = {User,Server,LJID},
|
||||
us = {User,Server},
|
||||
jid = LJID,
|
||||
name = Nick,
|
||||
ask = none,
|
||||
subscription = list_to_atom(Subscription),
|
||||
groups = Groups},
|
||||
Result =
|
||||
case roster_backend(Server) of
|
||||
mnesia ->
|
||||
mnesia:transaction(fun() ->
|
||||
case mnesia:read({roster,{User,Server,LJID}}) of
|
||||
[#roster{subscription=both}] ->
|
||||
already_added;
|
||||
_ ->
|
||||
mnesia:write(Roster)
|
||||
end
|
||||
end);
|
||||
odbc ->
|
||||
%% MREMOND: TODO: check if already_added
|
||||
case ejabberd_odbc:sql_transaction(Server,
|
||||
fun() ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
|
||||
case ejabberd_odbc:sql_query_t(
|
||||
["select username from rosterusers "
|
||||
" where username='", Username, "' "
|
||||
" and jid='", SJID,
|
||||
"' and subscription = 'B';"]) of
|
||||
{selected, ["username"],[]} ->
|
||||
ItemVals = record_to_string(Roster),
|
||||
ItemGroups = groups_to_string(Roster),
|
||||
odbc_queries:update_roster(Server, Username,
|
||||
SJID, ItemVals,
|
||||
ItemGroups);
|
||||
_ ->
|
||||
already_added
|
||||
end
|
||||
end) of
|
||||
{atomic, already_added} -> {atomic, already_added};
|
||||
{atomic, _} -> {atomic, ok};
|
||||
Error -> Error
|
||||
end;
|
||||
none ->
|
||||
{error, no_roster}
|
||||
end,
|
||||
case {Result, Push} of
|
||||
{{atomic, already_added}, _} -> ok; %% No need for roster push
|
||||
{{atomic, ok}, true} -> roster_push(User, Server, JID, Nick, Subscription);
|
||||
{{error, no_roster}, true} -> roster_push(User, Server, JID, Nick, Subscription);
|
||||
{{atomic, ok}, false} -> ok;
|
||||
_ -> error
|
||||
end,
|
||||
Result.
|
||||
|
||||
del_rosteritem(User, Server, JID) ->
|
||||
del_rosteritem(User, Server, JID, true).
|
||||
del_rosteritem(User, Server, JID, Push) ->
|
||||
{RU, RS, _} = jlib:jid_tolower(jlib:string_to_jid(JID)),
|
||||
LJID = {RU,RS,[]},
|
||||
Result = case roster_backend(Server) of
|
||||
mnesia ->
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:delete({roster, {User,Server,LJID}})
|
||||
end);
|
||||
odbc ->
|
||||
case ejabberd_odbc:sql_transaction(Server, fun() ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
|
||||
odbc_queries:del_roster(Server, Username, SJID)
|
||||
end) of
|
||||
{atomic, _} -> {atomic, ok};
|
||||
Error -> Error
|
||||
end;
|
||||
none ->
|
||||
{error, no_roster}
|
||||
end,
|
||||
case {Result, Push} of
|
||||
{{atomic, ok}, true} -> roster_push(User, Server, JID, "", "remove");
|
||||
{{error, no_roster}, true} -> roster_push(User, Server, JID, "", "remove");
|
||||
{{atomic, ok}, false} -> ok;
|
||||
_ -> error
|
||||
end,
|
||||
Result.
|
||||
|
||||
link_contacts(JID1, Nick1, JID2, Nick2) ->
|
||||
link_contacts(JID1, Nick1, JID2, Nick2, true).
|
||||
link_contacts(JID1, Nick1, JID2, Nick2, Push) ->
|
||||
link_contacts(JID1, Nick1, [], JID2, Nick2, [], Push).
|
||||
|
||||
link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2) ->
|
||||
link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, true).
|
||||
link_contacts(JID1, Nick1, Group1, JID2, Nick2, Group2, Push) ->
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
|
||||
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case add_rosteritem(U1, S1, JID2, Nick2, Group1, "both", Push) of
|
||||
{atomic, ok} -> add_rosteritem(U2, S2, JID1, Nick1, Group2, "both", Push);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
unlink_contacts(JID1, JID2) ->
|
||||
unlink_contacts(JID1, JID2, true).
|
||||
unlink_contacts(JID1, JID2, Push) ->
|
||||
{U1, S1, _} = jlib:jid_tolower(jlib:string_to_jid(JID1)),
|
||||
{U2, S2, _} = jlib:jid_tolower(jlib:string_to_jid(JID2)),
|
||||
case del_rosteritem(U1, S1, JID2, Push) of
|
||||
{atomic, ok} -> del_rosteritem(U2, S2, JID1, Push);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
roster_push(User, Server, JID, Nick, Subscription) ->
|
||||
LJID = jlib:make_jid(User, Server, ""),
|
||||
TJID = jlib:string_to_jid(JID),
|
||||
{TU, TS, _} = jlib:jid_tolower(TJID),
|
||||
Presence = {xmlelement, "presence", [{"type",
|
||||
case Subscription of
|
||||
"remove" -> "unsubscribed";
|
||||
"none" -> "unsubscribe";
|
||||
"both" -> "subscribed";
|
||||
_ -> "subscribe"
|
||||
end}], []},
|
||||
Item = case Nick of
|
||||
"" -> [{"jid", JID}, {"subscription", Subscription}];
|
||||
_ -> [{"jid", JID}, {"name", Nick}, {"subscription", Subscription}]
|
||||
end,
|
||||
Result = jlib:iq_to_xml(#iq{type = set, xmlns = ?NS_ROSTER, id = "push",
|
||||
sub_el = [{xmlelement, "query", [{"xmlns", ?NS_ROSTER}],
|
||||
[{xmlelement, "item", Item, []}]}]}),
|
||||
ejabberd_router:route(TJID, LJID, Presence),
|
||||
ejabberd_router:route(LJID, LJID, Result),
|
||||
lists:foreach(fun(Resource) ->
|
||||
UJID = jlib:make_jid(User, Server, Resource),
|
||||
ejabberd_router:route(TJID, UJID, Presence),
|
||||
ejabberd_router:route(UJID, UJID, Result),
|
||||
case Subscription of
|
||||
"remove" -> none;
|
||||
_ ->
|
||||
lists:foreach(fun(TR) ->
|
||||
ejabberd_router:route(jlib:make_jid(TU, TS, TR), UJID,
|
||||
{xmlelement, "presence", [], []})
|
||||
end, get_resources(TU, TS))
|
||||
end
|
||||
end, [R || R <- get_resources(User, Server), Subscription =/= "remove"]).
|
||||
|
||||
roster_backend(Server) ->
|
||||
Modules = gen_mod:loaded_modules(Server),
|
||||
Mnesia = lists:member(mod_roster, Modules),
|
||||
Odbc = lists:member(mod_roster_odbc, Modules),
|
||||
if Mnesia -> mnesia;
|
||||
true ->
|
||||
if Odbc -> odbc;
|
||||
true -> none
|
||||
end
|
||||
end.
|
||||
|
||||
record_to_string(#roster{us = {User, _Server},
|
||||
jid = JID,
|
||||
name = Name,
|
||||
subscription = Subscription,
|
||||
ask = Ask,
|
||||
askmessage = AskMessage}) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
|
||||
Nick = ejabberd_odbc:escape(Name),
|
||||
SSubscription = case Subscription of
|
||||
both -> "B";
|
||||
to -> "T";
|
||||
from -> "F";
|
||||
none -> "N"
|
||||
end,
|
||||
SAsk = case Ask of
|
||||
subscribe -> "S";
|
||||
unsubscribe -> "U";
|
||||
both -> "B";
|
||||
out -> "O";
|
||||
in -> "I";
|
||||
none -> "N"
|
||||
end,
|
||||
SAskMessage = ejabberd_odbc:escape(AskMessage),
|
||||
["'", Username, "',"
|
||||
"'", SJID, "',"
|
||||
"'", Nick, "',"
|
||||
"'", SSubscription, "',"
|
||||
"'", SAsk, "',"
|
||||
"'", SAskMessage, "',"
|
||||
"'N', '', 'item'"].
|
||||
|
||||
groups_to_string(#roster{us = {User, _Server},
|
||||
jid = JID,
|
||||
groups = Groups}) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
SJID = ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(JID))),
|
||||
%% Empty groups do not need to be converted to string to be inserted in
|
||||
%% the database
|
||||
lists:foldl(fun([], Acc) -> Acc;
|
||||
(Group, Acc) ->
|
||||
String = ["'", Username, "',"
|
||||
"'", SJID, "',"
|
||||
"'", ejabberd_odbc:escape(Group), "'"],
|
||||
[String|Acc]
|
||||
end, [], Groups).
|
||||
|
||||
%% Format roster items as a list of:
|
||||
%% [{struct, [{jid, "test@localhost"},{group, "Friends"},{nick, "Nicktest"}]}]
|
||||
format_roster([]) ->
|
||||
[];
|
||||
format_roster(Items) ->
|
||||
format_roster(Items, []).
|
||||
format_roster([], Structs) ->
|
||||
Structs;
|
||||
format_roster([#roster{jid=JID, name=Nick, groups=Group,
|
||||
subscription=Subs, ask=Ask}|Items], Structs) ->
|
||||
{User,Server,_Resource} = JID,
|
||||
Struct = {struct, [{jid,lists:flatten([User,"@",Server])},
|
||||
{group, extract_group(Group)},
|
||||
{nick, Nick},
|
||||
{subscription, atom_to_list(Subs)},
|
||||
{pending, atom_to_list(Ask)}
|
||||
]},
|
||||
format_roster(Items, [Struct|Structs]).
|
||||
|
||||
%% Format roster items as a list of:
|
||||
%% [{struct, [{jid, "test@localhost"}, {resource, "Messenger"}, {group, "Friends"},
|
||||
%% {nick, "Nicktest"},{show, "available"}, {status, "Currently at office"}]}]
|
||||
%% Note: If user is connected several times, only keep the resource with the
|
||||
%% highest non-negative priority
|
||||
format_roster_with_presence([]) ->
|
||||
[];
|
||||
format_roster_with_presence(Items) ->
|
||||
format_roster_with_presence(Items, []).
|
||||
format_roster_with_presence([], Structs) ->
|
||||
Structs;
|
||||
format_roster_with_presence([#roster{jid=JID, name=Nick, groups=Group,
|
||||
subscription=Subs, ask=Ask}|Items], Structs) ->
|
||||
{User,Server,_R} = JID,
|
||||
Presence = case Subs of
|
||||
both -> get_presence(User, Server);
|
||||
from -> get_presence(User, Server);
|
||||
_Other -> {"", "unavailable", ""}
|
||||
end,
|
||||
{Resource, Show, Status} =
|
||||
case Presence of
|
||||
{_R, "invisible", _S} -> {"", "unavailable", ""};
|
||||
_Status -> Presence
|
||||
end,
|
||||
Struct = {struct, [{jid,lists:flatten([User,"@",Server])},
|
||||
{resource, Resource},
|
||||
{group, extract_group(Group)},
|
||||
{nick, Nick},
|
||||
{subscription, atom_to_list(Subs)},
|
||||
{pending, atom_to_list(Ask)},
|
||||
{show, Show},
|
||||
{status, Status}
|
||||
]},
|
||||
format_roster_with_presence(Items, [Struct|Structs]).
|
||||
|
||||
extract_group([]) -> [];
|
||||
extract_group([Group|_Groups]) -> Group.
|
||||
|
||||
%% -----------------------------
|
||||
%% Internal session handling
|
||||
%% -----------------------------
|
||||
|
||||
%% This is inspired from ejabberd_sm.erl
|
||||
get_presence(User, Server) ->
|
||||
case get_sessions(User, Server) of
|
||||
[] ->
|
||||
{"", "unavailable", ""};
|
||||
Ss ->
|
||||
Session = hd(Ss),
|
||||
if Session#session.priority >= 0 ->
|
||||
Pid = element(2, Session#session.sid),
|
||||
%{_User, _Resource, Show, Status} = rpc:call(node(Pid), ejabberd_c2s, get_presence, [Pid]),
|
||||
{_User, Resource, Show, Status} = ejabberd_c2s:get_presence(Pid),
|
||||
{Resource, Show, Status};
|
||||
true ->
|
||||
{"", "unavailable", ""}
|
||||
end
|
||||
end.
|
||||
|
||||
get_resources(User, Server) ->
|
||||
lists:map(fun(S) -> element(3, S#session.usr)
|
||||
end, get_sessions(User, Server)).
|
||||
|
||||
get_sessions(User, Server) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case catch mnesia:dirty_index_read(session, {LUser, LServer}, #session.us) of
|
||||
{'EXIT', _Reason} -> [];
|
||||
[] -> [];
|
||||
Result -> lists:reverse(lists:keysort(#session.priority, clean_session_list(Result)))
|
||||
end.
|
||||
|
||||
clean_session_list(Ss) ->
|
||||
clean_session_list(lists:keysort(#session.usr, Ss), []).
|
||||
|
||||
clean_session_list([], Res) ->
|
||||
Res;
|
||||
clean_session_list([S], Res) ->
|
||||
[S | Res];
|
||||
clean_session_list([S1, S2 | Rest], Res) ->
|
||||
if
|
||||
S1#session.usr == S2#session.usr ->
|
||||
if
|
||||
S1#session.sid > S2#session.sid ->
|
||||
clean_session_list([S1 | Rest], Res);
|
||||
true ->
|
||||
clean_session_list([S2 | Rest], Res)
|
||||
end;
|
||||
true ->
|
||||
clean_session_list([S2 | Rest], [S1 | Res])
|
||||
end.
|
||||
|
||||
|
||||
%% -----------------------------
|
||||
%% Internal function pattern
|
||||
%% -----------------------------
|
||||
|
||||
user_action(User, Server, Fun, OK) ->
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
true ->
|
||||
case catch Fun() of
|
||||
OK ->
|
||||
{false, {response, [0]}};
|
||||
_ ->
|
||||
{false, {response, [1]}}
|
||||
end;
|
||||
false ->
|
||||
{false, {response, [404]}}
|
||||
end.
|
Loading…
Reference in New Issue
Block a user