mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
*** empty log message ***
SVN Revision: 13
This commit is contained in:
parent
e6c062fe40
commit
6c96829311
@ -25,6 +25,7 @@ init() ->
|
|||||||
ejabberd_auth:start(),
|
ejabberd_auth:start(),
|
||||||
ejabberd_router:start(),
|
ejabberd_router:start(),
|
||||||
ejabberd_sm:start(),
|
ejabberd_sm:start(),
|
||||||
|
ejabberd_s2s:start(),
|
||||||
ejabberd_local:start(),
|
ejabberd_local:start(),
|
||||||
ejabberd_listener:start(),
|
ejabberd_listener:start(),
|
||||||
loop(Port).
|
loop(Port).
|
||||||
|
@ -15,3 +15,6 @@
|
|||||||
-define(DEBUG(F,A),[]).
|
-define(DEBUG(F,A),[]).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
|
||||||
|
-define(MYNAME,"127.0.0.1").
|
||||||
|
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
handle_info/3,
|
handle_info/3,
|
||||||
terminate/3]).
|
terminate/3]).
|
||||||
|
|
||||||
-record(state, {socket, sender, receiver, streamid,
|
|
||||||
user = "", server = "localhost", resource = ""}).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
|
-record(state, {socket, sender, receiver, streamid,
|
||||||
|
user = "", server = ?MYNAME, resource = ""}).
|
||||||
|
|
||||||
-define(DBGFSM, true).
|
-define(DBGFSM, true).
|
||||||
|
|
||||||
-ifdef(DBGFSM).
|
-ifdef(DBGFSM).
|
||||||
@ -84,7 +84,7 @@ state_name(Event, StateData) ->
|
|||||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||||
% TODO
|
% TODO
|
||||||
Header = io_lib:format(?STREAM_HEADER,
|
Header = io_lib:format(?STREAM_HEADER,
|
||||||
[StateData#state.streamid, "localhost"]),
|
[StateData#state.streamid, ?MYNAME]),
|
||||||
send_text(StateData#state.sender, Header),
|
send_text(StateData#state.sender, Header),
|
||||||
case lists:keysearch("xmlns:stream", 1, Attrs) of
|
case lists:keysearch("xmlns:stream", 1, Attrs) of
|
||||||
{value, {"xmlns:stream", "http://etherx.jabber.org/streams"}} ->
|
{value, {"xmlns:stream", "http://etherx.jabber.org/streams"}} ->
|
||||||
|
@ -21,7 +21,7 @@ start() ->
|
|||||||
spawn(ejabberd_local, init, []).
|
spawn(ejabberd_local, init, []).
|
||||||
|
|
||||||
init() ->
|
init() ->
|
||||||
ejabberd_router:register_local_route("localhost"),
|
ejabberd_router:register_local_route(?MYNAME),
|
||||||
loop().
|
loop().
|
||||||
|
|
||||||
loop() ->
|
loop() ->
|
||||||
|
@ -101,15 +101,16 @@ do_route(From, To, Packet) ->
|
|||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, error} ->
|
{atomic, error} ->
|
||||||
% TODO: start s2s instead of below
|
% TODO: start s2s instead of below
|
||||||
{xmlelement, Name, Attrs, SubTags} = Packet,
|
ejabberd_s2s ! {route, From, To, Packet};
|
||||||
case xml:get_attr_s("type", Attrs) of
|
%{xmlelement, Name, Attrs, SubTags} = Packet,
|
||||||
"error" ->
|
%case xml:get_attr_s("type", Attrs) of
|
||||||
ok;
|
% "error" ->
|
||||||
_ ->
|
% ok;
|
||||||
Err = jlib:make_error_reply(Packet,
|
% _ ->
|
||||||
"502", "Service Unavailable"),
|
% Err = jlib:make_error_reply(Packet,
|
||||||
ejabberd_router ! {route, To, From, Err}
|
% "502", "Service Unavailable"),
|
||||||
end;
|
% ejabberd_router ! {route, To, From, Err}
|
||||||
|
%end;
|
||||||
{atomic, {ok, Node, Pid}} ->
|
{atomic, {ok, Node, Pid}} ->
|
||||||
case node() of
|
case node() of
|
||||||
Node ->
|
Node ->
|
||||||
|
252
src/ejabberd_s2s.erl
Normal file
252
src/ejabberd_s2s.erl
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : ejabberd_s2s.erl
|
||||||
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Purpose :
|
||||||
|
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
%%% Id : $Id$
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(ejabberd_s2s).
|
||||||
|
-author('alexey@sevcom.net').
|
||||||
|
-vsn('$Revision$ ').
|
||||||
|
|
||||||
|
-export([start/0, init/0, open_session/2, close_session/2,
|
||||||
|
have_connection/1,
|
||||||
|
get_key/1]).
|
||||||
|
|
||||||
|
-include_lib("mnemosyne/include/mnemosyne.hrl").
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
|
-record(s2s, {server, node, key}).
|
||||||
|
-record(mys2s, {server, pid}).
|
||||||
|
|
||||||
|
|
||||||
|
start() ->
|
||||||
|
spawn(ejabberd_s2s, init, []).
|
||||||
|
|
||||||
|
init() ->
|
||||||
|
register(ejabberd_s2s, self()),
|
||||||
|
mnesia:create_table(s2s,[{ram_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, s2s)}]),
|
||||||
|
mnesia:add_table_index(session, node),
|
||||||
|
mnesia:create_table(mys2s,
|
||||||
|
[{ram_copies, [node()]},
|
||||||
|
{local_content, true},
|
||||||
|
{attributes, record_info(fields, mys2s)}]),
|
||||||
|
mnesia:subscribe(system),
|
||||||
|
loop().
|
||||||
|
|
||||||
|
loop() ->
|
||||||
|
receive
|
||||||
|
%{open_connection, User, Resource, From} ->
|
||||||
|
% replace_and_register_my_connection(User, Resource, From),
|
||||||
|
% replace_alien_connection(User, Resource),
|
||||||
|
% loop();
|
||||||
|
{closed_conection, Server} ->
|
||||||
|
remove_connection(Server),
|
||||||
|
loop();
|
||||||
|
%{replace, User, Resource} ->
|
||||||
|
% replace_my_connection(User, Resource),
|
||||||
|
% loop();
|
||||||
|
{mnesia_system_event, {mnesia_down, Node}} ->
|
||||||
|
clean_table_from_bad_node(Node),
|
||||||
|
loop();
|
||||||
|
{route, From, To, Packet} ->
|
||||||
|
do_route(From, To, Packet),
|
||||||
|
loop();
|
||||||
|
_ ->
|
||||||
|
loop()
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
open_session(User, Resource) ->
|
||||||
|
ejabberd_s2s ! {open_session, User, Resource, self()}.
|
||||||
|
|
||||||
|
close_session(User, Resource) ->
|
||||||
|
ejabberd_s2s ! {close_session, User, Resource}.
|
||||||
|
|
||||||
|
%replace_alien_connection(User, Resource) ->
|
||||||
|
% F = fun() ->
|
||||||
|
% [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource),
|
||||||
|
% X.user = User,
|
||||||
|
% X.resource = Resource]
|
||||||
|
% end),
|
||||||
|
% Es = mnesia:read({session, ID}),
|
||||||
|
% mnesia:write(#session{id = ID, node = node()}),
|
||||||
|
% Es
|
||||||
|
% end,
|
||||||
|
% case mnesia:transaction(F) of
|
||||||
|
% {atomic, Rs} ->
|
||||||
|
% lists:foreach(
|
||||||
|
% fun(R) ->
|
||||||
|
% if R#session.node /= node() ->
|
||||||
|
% {ejabberd_s2s, R#session.node} !
|
||||||
|
% {replace, User, Resource};
|
||||||
|
% true ->
|
||||||
|
% ok
|
||||||
|
% end
|
||||||
|
% end, Rs);
|
||||||
|
% _ ->
|
||||||
|
% false
|
||||||
|
% end.
|
||||||
|
%
|
||||||
|
%
|
||||||
|
%replace_my_connection(User, Resource) ->
|
||||||
|
% F = fun() ->
|
||||||
|
% [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource),
|
||||||
|
% X.user = User,
|
||||||
|
% X.resource = Resource]
|
||||||
|
% end),
|
||||||
|
%
|
||||||
|
% Es = mnesia:read({mysession, ID}),
|
||||||
|
% mnesia:delete({mysession, ID}),
|
||||||
|
% Es
|
||||||
|
% end,
|
||||||
|
% case mnesia:transaction(F) of
|
||||||
|
% {atomic, Rs} ->
|
||||||
|
% lists:foreach(
|
||||||
|
% fun(R) ->
|
||||||
|
% (R#mysession.info)#mysession_info.pid ! replaced
|
||||||
|
% end, Rs);
|
||||||
|
% _ ->
|
||||||
|
% false
|
||||||
|
% end.
|
||||||
|
|
||||||
|
remove_connection(Server) ->
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:delete({mys2s, Server}),
|
||||||
|
mnesia:delete({s2s, Server})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
%replace_and_register_my_connection(User, Resource, Pid) ->
|
||||||
|
% F = fun() ->
|
||||||
|
% IDs = mnemosyne:eval(query [X.id || X <- table(user_resource),
|
||||||
|
% X.user = User,
|
||||||
|
% X.resource = Resource]
|
||||||
|
% end),
|
||||||
|
%
|
||||||
|
% ID = case IDs of
|
||||||
|
% [Id] -> Id;
|
||||||
|
% [] ->
|
||||||
|
% [CurID] =
|
||||||
|
% mnemosyne:eval(
|
||||||
|
% query [X.id ||
|
||||||
|
% X <- table(user_resource_id_seq)]
|
||||||
|
% end),
|
||||||
|
% mnesia:write(
|
||||||
|
% #user_resource_id_seq{id = CurID + 1}),
|
||||||
|
% mnesia:write(
|
||||||
|
% #user_resource{id = CurID,
|
||||||
|
% user = User,
|
||||||
|
% resource = Resource}),
|
||||||
|
% CurID
|
||||||
|
% end,
|
||||||
|
% Es = mnesia:read({mysession, ID}),
|
||||||
|
% mnesia:write(#mysession{id = ID,
|
||||||
|
% info = #mysession_info{pid = Pid}}),
|
||||||
|
% Es
|
||||||
|
% end,
|
||||||
|
% case mnesia:transaction(F) of
|
||||||
|
% {atomic, Rs} ->
|
||||||
|
% lists:foreach(
|
||||||
|
% fun(R) ->
|
||||||
|
% (R#mysession.info)#mysession_info.pid ! replaced
|
||||||
|
% end, Rs);
|
||||||
|
% _ ->
|
||||||
|
% false
|
||||||
|
% end.
|
||||||
|
|
||||||
|
|
||||||
|
clean_table_from_bad_node(Node) ->
|
||||||
|
F = fun() ->
|
||||||
|
Es = mnesia:index_read(s2s, Node, #s2s.node),
|
||||||
|
lists:foreach(fun(E) ->
|
||||||
|
mnesia:delete_object(s2s, E, write)
|
||||||
|
end, Es)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
have_connection(Server) ->
|
||||||
|
F = fun() ->
|
||||||
|
[E] = mnesia:read({s2s, Server})
|
||||||
|
end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, _} ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_key(Server) ->
|
||||||
|
F = fun() ->
|
||||||
|
[E] = mnesia:read({s2s, Server}),
|
||||||
|
E
|
||||||
|
end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, E} ->
|
||||||
|
E#s2s.key;
|
||||||
|
_ ->
|
||||||
|
""
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
do_route(From, To, Packet) ->
|
||||||
|
?DEBUG("s2s manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
|
||||||
|
[From, To, Packet, 8]),
|
||||||
|
{User, Server, Resource} = To,
|
||||||
|
Key = lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])),
|
||||||
|
F = fun() ->
|
||||||
|
case mnesia:read({mys2s, Server}) of
|
||||||
|
[] ->
|
||||||
|
case mnesia:read({s2s, Server}) of
|
||||||
|
[Er] ->
|
||||||
|
{remote, Er#s2s.node};
|
||||||
|
[] ->
|
||||||
|
% TODO
|
||||||
|
mnesia:write(#s2s{server = Server,
|
||||||
|
node = node(),
|
||||||
|
key = Key}),
|
||||||
|
new
|
||||||
|
end;
|
||||||
|
[El] ->
|
||||||
|
{local, El#mys2s.pid}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, {local, Pid}} ->
|
||||||
|
?DEBUG("sending to process ~p~n", [Pid]),
|
||||||
|
% TODO
|
||||||
|
{xmlelement, Name, Attrs, Els} = Packet,
|
||||||
|
NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||||
|
jlib:jid_to_string(To),
|
||||||
|
Attrs),
|
||||||
|
send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
|
||||||
|
ok;
|
||||||
|
{atomic, {remote, Node}} ->
|
||||||
|
?DEBUG("sending to node ~p~n", [Node]),
|
||||||
|
{ejabberd_s2s, Node} ! {route, From, To, Packet},
|
||||||
|
ok;
|
||||||
|
{atomic, new} ->
|
||||||
|
?DEBUG("starting new s2s connection~n", []),
|
||||||
|
Pid = ejabberd_s2s_out:start(Server, {new, Key}),
|
||||||
|
mnesia:transaction(fun() -> mnesia:write(#mys2s{server = Server,
|
||||||
|
pid = Pid}) end),
|
||||||
|
{xmlelement, Name, Attrs, Els} = Packet,
|
||||||
|
NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||||
|
jlib:jid_to_string(To),
|
||||||
|
Attrs),
|
||||||
|
send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
|
||||||
|
ok;
|
||||||
|
{atomic, not_exists} ->
|
||||||
|
?DEBUG("packet droped~n", []),
|
||||||
|
ok;
|
||||||
|
{aborted, Reason} ->
|
||||||
|
?DEBUG("delivery failed: ~p~n", [Reason]),
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
send_element(Pid, El) ->
|
||||||
|
Pid ! {send_element, El}.
|
@ -16,15 +16,19 @@
|
|||||||
-export([start/1, receiver/2, send_text/2, send_element/2]).
|
-export([start/1, receiver/2, send_text/2, send_element/2]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1, wait_for_stream/2, wait_for_key/2, session_established/2,
|
-export([init/1,
|
||||||
|
wait_for_stream/2,
|
||||||
|
wait_for_key/2,
|
||||||
|
wait_for_verification/2,
|
||||||
|
stream_established/2,
|
||||||
handle_info/3,
|
handle_info/3,
|
||||||
terminate/3]).
|
terminate/3]).
|
||||||
|
|
||||||
-record(state, {socket, receiver, streamid,
|
|
||||||
myself = "localhost", server}).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
|
-record(state, {socket, receiver, streamid,
|
||||||
|
myself = ?MYNAME, server, queue}).
|
||||||
|
|
||||||
-define(DBGFSM, true).
|
-define(DBGFSM, true).
|
||||||
|
|
||||||
-ifdef(DBGFSM).
|
-ifdef(DBGFSM).
|
||||||
@ -70,7 +74,8 @@ init([Socket]) ->
|
|||||||
ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]),
|
ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]),
|
||||||
{ok, wait_for_stream, #state{socket = Socket,
|
{ok, wait_for_stream, #state{socket = Socket,
|
||||||
receiver = ReceiverPid,
|
receiver = ReceiverPid,
|
||||||
streamid = new_id()}}.
|
streamid = new_id(),
|
||||||
|
queue = queue:new()}}.
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
%% Func: StateName/2
|
%% Func: StateName/2
|
||||||
@ -100,12 +105,24 @@ wait_for_key({xmlstreamelement, El}, StateData) ->
|
|||||||
case is_key_packet(El) of
|
case is_key_packet(El) of
|
||||||
{key, To, From, Id, Key} ->
|
{key, To, From, Id, Key} ->
|
||||||
io:format("GET KEY: ~p~n", [{To, From, Id, Key}]),
|
io:format("GET KEY: ~p~n", [{To, From, Id, Key}]),
|
||||||
% TODO
|
|
||||||
ejabberd_s2s_out:start(From, {verify, self(), Key}),
|
ejabberd_s2s_out:start(From, {verify, self(), Key}),
|
||||||
{next_state, wait_for_key, StateData};
|
{next_state,
|
||||||
|
wait_for_verification,
|
||||||
|
StateData#state{server = From}};
|
||||||
{verify, To, From, Id, Key} ->
|
{verify, To, From, Id, Key} ->
|
||||||
io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]),
|
io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]),
|
||||||
% TODO
|
Key1 = ejabberd_s2s:get_key(From),
|
||||||
|
Type = if Key == Key1 -> "valid";
|
||||||
|
true -> "invalid"
|
||||||
|
end,
|
||||||
|
send_element(StateData#state.socket,
|
||||||
|
{xmlelement,
|
||||||
|
"db:verify",
|
||||||
|
[{"from", ?MYNAME},
|
||||||
|
{"to", From},
|
||||||
|
{"id", Id},
|
||||||
|
{"type", Type}],
|
||||||
|
[]}),
|
||||||
{next_state, wait_for_key, StateData};
|
{next_state, wait_for_key, StateData};
|
||||||
_ ->
|
_ ->
|
||||||
{next_state, wait_for_key, StateData}
|
{next_state, wait_for_key, StateData}
|
||||||
@ -118,28 +135,85 @@ wait_for_key({xmlstreamend, Name}, StateData) ->
|
|||||||
wait_for_key(closed, StateData) ->
|
wait_for_key(closed, StateData) ->
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
session_established({xmlstreamelement, El}, StateData) ->
|
wait_for_verification(valid, StateData) ->
|
||||||
|
send_element(StateData#state.socket,
|
||||||
|
{xmlelement,
|
||||||
|
"db:result",
|
||||||
|
[{"from", ?MYNAME},
|
||||||
|
{"to", StateData#state.server},
|
||||||
|
{"type", "valid"}],
|
||||||
|
[]}),
|
||||||
|
send_queue(StateData#state.socket, StateData#state.queue),
|
||||||
|
{next_state, stream_established, StateData};
|
||||||
|
|
||||||
|
wait_for_verification(invalid, StateData) ->
|
||||||
|
send_element(StateData#state.socket,
|
||||||
|
{xmlelement,
|
||||||
|
"db:result",
|
||||||
|
[{"from", ?MYNAME},
|
||||||
|
{"to", StateData#state.server},
|
||||||
|
{"type", "invalid"}],
|
||||||
|
[]}),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_verification({xmlstreamelement, El}, StateData) ->
|
||||||
|
case is_key_packet(El) of
|
||||||
|
{verify, To, From, Id, Key} ->
|
||||||
|
io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]),
|
||||||
|
Key1 = ejabberd_s2s:get_key(From),
|
||||||
|
Type = if Key == Key1 -> "valid";
|
||||||
|
true -> "invalid"
|
||||||
|
end,
|
||||||
|
send_element(StateData#state.socket,
|
||||||
|
{xmlelement,
|
||||||
|
"db:verify",
|
||||||
|
[{"from", ?MYNAME},
|
||||||
|
{"to", From},
|
||||||
|
{"id", Id},
|
||||||
|
{"type", Type}],
|
||||||
|
[]}),
|
||||||
|
{next_state, wait_for_verification, StateData};
|
||||||
|
_ ->
|
||||||
|
{next_state, wait_for_verification, StateData}
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_verification({xmlstreamend, Name}, StateData) ->
|
||||||
|
% TODO
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_verification(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
stream_established({xmlstreamelement, El}, StateData) ->
|
||||||
{xmlelement, Name, Attrs, Els} = El,
|
{xmlelement, Name, Attrs, Els} = El,
|
||||||
% TODO
|
% TODO
|
||||||
FromJID = {StateData#state.server},
|
From = xml:get_attr_s("from", Attrs),
|
||||||
|
FromJID1 = jlib:string_to_jid(From),
|
||||||
|
FromJID = case FromJID1 of
|
||||||
|
{Node, Server, Resource} ->
|
||||||
|
if Server == StateData#state.server -> FromJID1;
|
||||||
|
true -> error
|
||||||
|
end;
|
||||||
|
_ -> error
|
||||||
|
end,
|
||||||
To = xml:get_attr_s("to", Attrs),
|
To = xml:get_attr_s("to", Attrs),
|
||||||
ToJID = case To of
|
ToJID = case To of
|
||||||
"" ->
|
"" -> error;
|
||||||
{"", StateData#state.server, ""};
|
_ -> jlib:string_to_jid(To)
|
||||||
_ ->
|
|
||||||
jlib:string_to_jid(To)
|
|
||||||
end,
|
end,
|
||||||
case ToJID of
|
if ((Name == "iq") or (Name == "message") or (Name == "presence")) and
|
||||||
error ->
|
(ToJID /= error) and (FromJID /= error) ->
|
||||||
% TODO
|
ejabberd_router:route(FromJID, ToJID, El);
|
||||||
error;
|
true ->
|
||||||
_ ->
|
error
|
||||||
%?DEBUG("FromJID=~w, ToJID=~w, El=~w~n", [FromJID, ToJID, El]),
|
|
||||||
ejabberd_router:route(FromJID, ToJID, El)
|
|
||||||
end,
|
end,
|
||||||
{next_state, session_established, StateData};
|
{next_state, stream_established, StateData};
|
||||||
|
|
||||||
session_established(closed, StateData) ->
|
stream_established({xmlstreamend, Name}, StateData) ->
|
||||||
|
% TODO
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
stream_established(closed, StateData) ->
|
||||||
% TODO
|
% TODO
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
@ -188,7 +262,17 @@ handle_sync_event(Event, From, StateName, StateData) ->
|
|||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
handle_info({send_text, Text}, StateName, StateData) ->
|
handle_info({send_text, Text}, StateName, StateData) ->
|
||||||
send_text(StateData#state.socket, Text),
|
send_text(StateData#state.socket, Text),
|
||||||
{next_state, StateName, StateData}.
|
{next_state, StateName, StateData};
|
||||||
|
handle_info({send_element, El}, StateName, StateData) ->
|
||||||
|
case StateName of
|
||||||
|
stream_established ->
|
||||||
|
send_element(StateData#state.socket, El),
|
||||||
|
{next_state, StateName, StateData};
|
||||||
|
_ ->
|
||||||
|
Q = queue:in(El, StateData#state.queue),
|
||||||
|
{next_state, StateName, StateData#state{queue = Q}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
%% Func: terminate/3
|
%% Func: terminate/3
|
||||||
@ -203,6 +287,7 @@ terminate(Reason, StateName, StateData) ->
|
|||||||
% %ejabberd_sm:close_session(StateData#state.user,
|
% %ejabberd_sm:close_session(StateData#state.user,
|
||||||
% % StateData#state.resource)
|
% % StateData#state.resource)
|
||||||
% end,
|
% end,
|
||||||
|
%ejabberd_s2s ! {closed_conection, StateData#state.server},
|
||||||
gen_tcp:close(StateData#state.socket),
|
gen_tcp:close(StateData#state.socket),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -231,6 +316,16 @@ send_text(Socket, Text) ->
|
|||||||
send_element(Socket, El) ->
|
send_element(Socket, El) ->
|
||||||
send_text(Socket, xml:element_to_string(El)).
|
send_text(Socket, xml:element_to_string(El)).
|
||||||
|
|
||||||
|
send_queue(Socket, Q) ->
|
||||||
|
case queue:out(Q) of
|
||||||
|
{{value, El}, Q1} ->
|
||||||
|
send_element(Socket, El),
|
||||||
|
send_queue(Socket, Q1);
|
||||||
|
{empty, Q1} ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
new_id() ->
|
new_id() ->
|
||||||
lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])).
|
lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])).
|
||||||
|
|
||||||
@ -250,25 +345,5 @@ is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
|
|||||||
is_key_packet(_) ->
|
is_key_packet(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
get_auth_tags([{xmlelement, Name, Attrs, Els}| L], U, P, D, R) ->
|
|
||||||
CData = xml:get_cdata(Els),
|
|
||||||
case Name of
|
|
||||||
"username" ->
|
|
||||||
get_auth_tags(L, CData, P, D, R);
|
|
||||||
"password" ->
|
|
||||||
get_auth_tags(L, U, CData, D, R);
|
|
||||||
"digest" ->
|
|
||||||
get_auth_tags(L, U, P, CData, R);
|
|
||||||
"resource" ->
|
|
||||||
get_auth_tags(L, U, P, D, CData);
|
|
||||||
_ ->
|
|
||||||
get_auth_tags(L, U, P, D, R)
|
|
||||||
end;
|
|
||||||
get_auth_tags([_ | L], U, P, D, R) ->
|
|
||||||
get_auth_tags(L, U, P, D, R);
|
|
||||||
get_auth_tags([], U, P, D, R) ->
|
|
||||||
{U, P, D, R}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,18 +17,19 @@
|
|||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
|
open_socket/2,
|
||||||
wait_for_stream/2,
|
wait_for_stream/2,
|
||||||
wait_for_key/2,
|
wait_for_validation/2,
|
||||||
wait_for_verification/2,
|
wait_for_verification/2,
|
||||||
session_established/2,
|
stream_established/2,
|
||||||
handle_info/3,
|
handle_info/3,
|
||||||
terminate/3]).
|
terminate/3]).
|
||||||
|
|
||||||
-record(state, {socket, receiver, streamid,
|
|
||||||
myself = "localhost", server, type, xmlpid}).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
|
-record(state, {socket, receiver, streamid,
|
||||||
|
myself = ?MYNAME, server, type, xmlpid, queue}).
|
||||||
|
|
||||||
-define(DBGFSM, true).
|
-define(DBGFSM, true).
|
||||||
|
|
||||||
-ifdef(DBGFSM).
|
-ifdef(DBGFSM).
|
||||||
@ -57,7 +58,8 @@
|
|||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
start(Host, Type) ->
|
start(Host, Type) ->
|
||||||
gen_fsm:start(ejabberd_s2s_out, [Host, Type], ?FSMOPTS).
|
{ok, Pid} = gen_fsm:start(ejabberd_s2s_out, [Host, Type], ?FSMOPTS),
|
||||||
|
Pid.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Callback functions from gen_fsm
|
%%% Callback functions from gen_fsm
|
||||||
@ -70,16 +72,10 @@ start(Host, Type) ->
|
|||||||
%% ignore |
|
%% ignore |
|
||||||
%% {stop, StopReason}
|
%% {stop, StopReason}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
init([Host, Type]) ->
|
init([Server, Type]) ->
|
||||||
{ok, Socket} = gen_tcp:connect(Host, 5569,
|
gen_fsm:send_event(self(), init),
|
||||||
[binary, {packet, 0}]),
|
{ok, open_socket, #state{queue = queue:new(),
|
||||||
XMLStreamPid = xml_stream:start(self()),
|
server = Server,
|
||||||
%ReceiverPid = spawn(?MODULE, receiver, [Socket, self()]),
|
|
||||||
send_text(Socket, ?STREAM_HEADER),
|
|
||||||
{ok, wait_for_stream, #state{socket = Socket,
|
|
||||||
xmlpid = XMLStreamPid,
|
|
||||||
streamid = new_id(),
|
|
||||||
server = Host,
|
|
||||||
type = Type}}.
|
type = Type}}.
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
@ -88,22 +84,46 @@ init([Host, Type]) ->
|
|||||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||||
%% {stop, Reason, NewStateData}
|
%% {stop, Reason, NewStateData}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
state_name(Event, StateData) ->
|
open_socket(init, StateData) ->
|
||||||
{next_state, state_name, StateData}.
|
case gen_tcp:connect(StateData#state.server, 5569,
|
||||||
|
[binary, {packet, 0}]) of
|
||||||
|
{ok, Socket} ->
|
||||||
|
XMLStreamPid = xml_stream:start(self()),
|
||||||
|
send_text(Socket, ?STREAM_HEADER),
|
||||||
|
{next_state, wait_for_stream,
|
||||||
|
StateData#state{socket = Socket,
|
||||||
|
xmlpid = XMLStreamPid,
|
||||||
|
streamid = new_id()}};
|
||||||
|
{error, Reason} ->
|
||||||
|
?DEBUG("s2s_out: connect return ~p~n", [Reason]),
|
||||||
|
Text = case Reason of
|
||||||
|
timeout -> "Server Connect Timeout";
|
||||||
|
_ -> "Server Connect Failed"
|
||||||
|
end,
|
||||||
|
bounce_messages(Text),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||||
% TODO
|
% TODO
|
||||||
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
|
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
|
||||||
{"jabber:server", "jabber:server:dialback"} ->
|
{"jabber:server", "jabber:server:dialback"} ->
|
||||||
case StateData#state.type of
|
case StateData#state.type of
|
||||||
new ->
|
{new, Key} ->
|
||||||
|
send_element(StateData#state.socket,
|
||||||
|
{xmlelement,
|
||||||
|
"db:result",
|
||||||
|
[{"from", ?MYNAME},
|
||||||
|
{"to", StateData#state.server}],
|
||||||
|
[{xmlcdata, Key}]}),
|
||||||
% TODO
|
% TODO
|
||||||
{next_state, wait_for_key, StateData};
|
{next_state, wait_for_validation, StateData};
|
||||||
{verify, Pid, Key} ->
|
{verify, Pid, Key} ->
|
||||||
send_element(StateData#state.socket,
|
send_element(StateData#state.socket,
|
||||||
{xmlelement,
|
{xmlelement,
|
||||||
"db:verify",
|
"db:verify",
|
||||||
[{"from", "127.0.0.1"},
|
[{"from", ?MYNAME},
|
||||||
{"to", StateData#state.server}],
|
{"to", StateData#state.server}],
|
||||||
[{xmlcdata, Key}]}),
|
[{xmlcdata, Key}]}),
|
||||||
{next_state, wait_for_verification, StateData}
|
{next_state, wait_for_verification, StateData}
|
||||||
@ -117,37 +137,43 @@ wait_for_stream(closed, StateData) ->
|
|||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
wait_for_key({xmlstreamelement, El}, StateData) ->
|
|
||||||
case is_key_packet(El) of
|
|
||||||
{key, To, From, Id, Key} ->
|
|
||||||
io:format("GET KEY: ~p~n", [{To, From, Id, Key}]),
|
|
||||||
% TODO
|
|
||||||
{next_state, wait_for_key, StateData};
|
|
||||||
{verify, To, From, Id, Key} ->
|
|
||||||
io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]),
|
|
||||||
% TODO
|
|
||||||
{next_state, wait_for_key, StateData};
|
|
||||||
_ ->
|
|
||||||
{next_state, wait_for_key, StateData}
|
|
||||||
end;
|
|
||||||
|
|
||||||
wait_for_key({xmlstreamend, Name}, StateData) ->
|
wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||||
% TODO
|
|
||||||
{stop, normal, StateData};
|
|
||||||
|
|
||||||
wait_for_key(closed, StateData) ->
|
|
||||||
{stop, normal, StateData}.
|
|
||||||
|
|
||||||
wait_for_verification({xmlstreamelement, El}, StateData) ->
|
|
||||||
case is_verify_res(El) of
|
case is_verify_res(El) of
|
||||||
{result, To, From, Id, Type} ->
|
{result, To, From, Id, Type} ->
|
||||||
case Type of
|
case Type of
|
||||||
"valid" ->
|
"valid" ->
|
||||||
io:format("VALID KEY~n", []);
|
% TODO
|
||||||
|
send_queue(StateData#state.socket, StateData#state.queue),
|
||||||
|
{next_state, stream_established, StateData};
|
||||||
|
_ ->
|
||||||
|
% TODO: bounce packets
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{next_state, wait_for_validation, StateData}
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_validation({xmlstreamend, Name}, StateData) ->
|
||||||
|
% TODO
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_validation(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_verification({xmlstreamelement, El}, StateData) ->
|
||||||
|
case is_verify_res(El) of
|
||||||
|
{result, To, From, Id, Type} ->
|
||||||
|
{verify, Pid, Key} = StateData#state.type,
|
||||||
|
case Type of
|
||||||
|
"valid" ->
|
||||||
|
io:format("VALID KEY~n", []),
|
||||||
|
gen_fsm:send_event(Pid, valid);
|
||||||
% TODO
|
% TODO
|
||||||
_ ->
|
_ ->
|
||||||
% TODO
|
% TODO
|
||||||
ok
|
gen_fsm:send_event(Pid, invalid)
|
||||||
end,
|
end,
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
_ ->
|
_ ->
|
||||||
@ -161,28 +187,37 @@ wait_for_verification({xmlstreamend, Name}, StateData) ->
|
|||||||
wait_for_verification(closed, StateData) ->
|
wait_for_verification(closed, StateData) ->
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
session_established({xmlstreamelement, El}, StateData) ->
|
|
||||||
|
stream_established({xmlstreamelement, El}, StateData) ->
|
||||||
{xmlelement, Name, Attrs, Els} = El,
|
{xmlelement, Name, Attrs, Els} = El,
|
||||||
% TODO
|
% TODO
|
||||||
FromJID = {StateData#state.server},
|
From = xml:get_attr_s("from", Attrs),
|
||||||
|
FromJID1 = jlib:string_to_jid(From),
|
||||||
|
FromJID = case FromJID1 of
|
||||||
|
{Node, Server, Resource} ->
|
||||||
|
if Server == StateData#state.server -> FromJID1;
|
||||||
|
true -> error
|
||||||
|
end;
|
||||||
|
_ -> error
|
||||||
|
end,
|
||||||
To = xml:get_attr_s("to", Attrs),
|
To = xml:get_attr_s("to", Attrs),
|
||||||
ToJID = case To of
|
ToJID = case To of
|
||||||
"" ->
|
"" -> error;
|
||||||
{"", StateData#state.server, ""};
|
_ -> jlib:string_to_jid(To)
|
||||||
_ ->
|
|
||||||
jlib:string_to_jid(To)
|
|
||||||
end,
|
end,
|
||||||
case ToJID of
|
if ((Name == "iq") or (Name == "message") or (Name == "presence")) and
|
||||||
error ->
|
(ToJID /= error) and (FromJID /= error) ->
|
||||||
% TODO
|
ejabberd_router:route(FromJID, ToJID, El);
|
||||||
error;
|
true ->
|
||||||
_ ->
|
error
|
||||||
%?DEBUG("FromJID=~w, ToJID=~w, El=~w~n", [FromJID, ToJID, El]),
|
|
||||||
ejabberd_router:route(FromJID, ToJID, El)
|
|
||||||
end,
|
end,
|
||||||
{next_state, session_established, StateData};
|
{next_state, stream_established, StateData};
|
||||||
|
|
||||||
session_established(closed, StateData) ->
|
stream_established({xmlstreamend, Name}, StateData) ->
|
||||||
|
% TODO
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
stream_established(closed, StateData) ->
|
||||||
% TODO
|
% TODO
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
@ -232,14 +267,23 @@ handle_sync_event(Event, From, StateName, StateData) ->
|
|||||||
handle_info({send_text, Text}, StateName, StateData) ->
|
handle_info({send_text, Text}, StateName, StateData) ->
|
||||||
send_text(StateData#state.socket, Text),
|
send_text(StateData#state.socket, Text),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
|
handle_info({send_element, El}, StateName, StateData) ->
|
||||||
|
case StateName of
|
||||||
|
stream_established ->
|
||||||
|
send_element(StateData#state.socket, El),
|
||||||
|
{next_state, StateName, StateData};
|
||||||
|
_ ->
|
||||||
|
Q = queue:in(El, StateData#state.queue),
|
||||||
|
{next_state, StateName, StateData#state{queue = Q}}
|
||||||
|
end;
|
||||||
handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
||||||
xml_stream:send_text(StateData#state.xmlpid, Data),
|
xml_stream:send_text(StateData#state.xmlpid, Data),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
||||||
self() ! closed,
|
gen_fsm:send_event(self(), closed),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||||
self() ! closed,
|
gen_fsm:send_event(self(), closed),
|
||||||
{next_state, StateName, StateData}.
|
{next_state, StateName, StateData}.
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
@ -248,14 +292,19 @@ handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
|||||||
%% Returns: any
|
%% Returns: any
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
terminate(Reason, StateName, StateData) ->
|
terminate(Reason, StateName, StateData) ->
|
||||||
% case StateData#state.user of
|
?DEBUG("s2s_out: terminate ~p~n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!~n", [[Reason, StateName, StateData]]),
|
||||||
% "" ->
|
case StateData#state.type of
|
||||||
% ok;
|
{new, Key} ->
|
||||||
% _ ->
|
ejabberd_s2s ! {closed_conection, StateData#state.server};
|
||||||
% %ejabberd_sm:close_session(StateData#state.user,
|
_ ->
|
||||||
% % StateData#state.resource)
|
ok
|
||||||
% end,
|
end,
|
||||||
gen_tcp:close(StateData#state.socket),
|
case StateData#state.socket of
|
||||||
|
undefined ->
|
||||||
|
ok;
|
||||||
|
Socket ->
|
||||||
|
gen_tcp:close(Socket)
|
||||||
|
end,
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -283,9 +332,36 @@ send_text(Socket, Text) ->
|
|||||||
send_element(Socket, El) ->
|
send_element(Socket, El) ->
|
||||||
send_text(Socket, xml:element_to_string(El)).
|
send_text(Socket, xml:element_to_string(El)).
|
||||||
|
|
||||||
|
send_queue(Socket, Q) ->
|
||||||
|
case queue:out(Q) of
|
||||||
|
{{value, El}, Q1} ->
|
||||||
|
send_element(Socket, El),
|
||||||
|
send_queue(Socket, Q1);
|
||||||
|
{empty, Q1} ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
new_id() ->
|
new_id() ->
|
||||||
lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])).
|
lists:flatten(io_lib:format("~p", [random:uniform(65536*65536)])).
|
||||||
|
|
||||||
|
bounce_messages(Reason) ->
|
||||||
|
receive
|
||||||
|
{send_element, El} ->
|
||||||
|
{xmlelement, Name, Attrs, SubTags} = El,
|
||||||
|
case xml:get_attr_s("type", Attrs) of
|
||||||
|
"error" ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
Err = jlib:make_error_reply(El,
|
||||||
|
"502", Reason),
|
||||||
|
From = jlib:string_to_jid(xml:get_attr_s("from", Attrs)),
|
||||||
|
To = jlib:string_to_jid(xml:get_attr_s("to", Attrs)),
|
||||||
|
ejabberd_router ! {route, To, From, Err}
|
||||||
|
end,
|
||||||
|
bounce_messages(Reason)
|
||||||
|
after 0 ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
||||||
{key,
|
{key,
|
||||||
@ -317,25 +393,3 @@ is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
|
|||||||
is_verify_res(_) ->
|
is_verify_res(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
get_auth_tags([{xmlelement, Name, Attrs, Els}| L], U, P, D, R) ->
|
|
||||||
CData = xml:get_cdata(Els),
|
|
||||||
case Name of
|
|
||||||
"username" ->
|
|
||||||
get_auth_tags(L, CData, P, D, R);
|
|
||||||
"password" ->
|
|
||||||
get_auth_tags(L, U, CData, D, R);
|
|
||||||
"digest" ->
|
|
||||||
get_auth_tags(L, U, P, CData, R);
|
|
||||||
"resource" ->
|
|
||||||
get_auth_tags(L, U, P, D, CData);
|
|
||||||
_ ->
|
|
||||||
get_auth_tags(L, U, P, D, R)
|
|
||||||
end;
|
|
||||||
get_auth_tags([_ | L], U, P, D, R) ->
|
|
||||||
get_auth_tags(L, U, P, D, R);
|
|
||||||
get_auth_tags([], U, P, D, R) ->
|
|
||||||
{U, P, D, R}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +45,6 @@ init() ->
|
|||||||
{local_content, true},
|
{local_content, true},
|
||||||
{attributes, record_info(fields, mysession)}]),
|
{attributes, record_info(fields, mysession)}]),
|
||||||
mnesia:subscribe(system),
|
mnesia:subscribe(system),
|
||||||
%ejabberd_router:register_local_route("localhost"),
|
|
||||||
loop().
|
loop().
|
||||||
|
|
||||||
loop() ->
|
loop() ->
|
||||||
|
Loading…
Reference in New Issue
Block a user