ejabberd_auth:start(),
ejabberd_router:start(),
ejabberd_sm:start(),
+ ejabberd_local:start(),
ejabberd_listener:start(),
loop(Port).
-behaviour(gen_fsm).
%% External exports
--export([start/1, receiver/2, sender/1, send_text/2]).
+-export([start/1, receiver/2, sender/1, send_text/2, send_element/2]).
%% gen_fsm callbacks
%-export([init/1, state_name/2, state_name/3, handle_event/3,
% handle_sync_event/4, handle_info/3, terminate/3]).
%
-export([init/1, wait_for_stream/2, wait_for_auth/2, session_established/2,
+ handle_info/3,
terminate/3]).
-record(state, {socket, sender, receiver, streamid,
case ejabberd_auth:check_password(U, P) of
true ->
% TODO
+ ejabberd_sm:open_session(U, R),
+ Res = jlib:make_result_iq_reply(El),
+ send_element(StateData#state.sender, Res),
{next_state, session_established,
StateData#state{user = U, resource = R}};
_ ->
session_established({xmlstreamelement, El}, StateData) ->
{xmlelement, Name, Attrs, Els} = El,
% TODO
- FromJID = {StateData#state.user, "localhost", StateData#state.resource},
- ToJID = jlib:string_to_jid(xml:get_attr_s("to", Attrs)),
- ejabberd_router:route(FromJID, ToJID, El),
+ FromJID = {StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource},
+ To = xml:get_attr_s("to", Attrs),
+ ToJID = case To of
+ "" ->
+ {"", StateData#state.server, ""};
+ _ ->
+ jlib:string_to_jid(To)
+ end,
+ case ToJID of
+ error ->
+ % TODO
+ error;
+ _ ->
+ %?DEBUG("FromJID=~w, ToJID=~w, El=~w~n", [FromJID, ToJID, El]),
+ ejabberd_router:route(FromJID, ToJID, El)
+ end,
{next_state, session_established, StateData};
session_established(closed, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
-handle_info(Info, StateName, StateData) ->
+handle_info({send_text, Text}, StateName, StateData) ->
+ send_text(StateData#state.sender, Text),
{next_state, StateName, StateData}.
%%----------------------------------------------------------------------
%% Returns: any
%%----------------------------------------------------------------------
terminate(Reason, StateName, StateData) ->
+ case StateData#state.user of
+ "" ->
+ ok;
+ _ ->
+ ejabberd_sm:close_session(StateData#state.user,
+ StateData#state.resource)
+ end,
StateData#state.sender ! close,
ok.
sender(Socket) ->
receive
- {text, Text} ->
+ {send_text, Text} ->
gen_tcp:send(Socket,Text),
sender(Socket);
close ->
end.
send_text(Pid, Text) ->
- Pid ! {text, Text}.
+ Pid ! {send_text, Text}.
send_element(Pid, El) ->
send_text(Pid, xml:element_to_string(El)).
--- /dev/null
+%%%----------------------------------------------------------------------
+%%% File : ejabberd_local.erl
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose :
+%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id : $Id$
+%%%----------------------------------------------------------------------
+
+-module(ejabberd_local).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+%%-export([Function/Arity, ...]).
+
+-export([start/0,init/0]).
+
+-include("ejabberd.hrl").
+
+
+start() ->
+ spawn(ejabberd_local, init, []).
+
+init() ->
+ ejabberd_router:register_local_route("localhost"),
+ loop().
+
+loop() ->
+ receive
+ {route, From, To, Packet} ->
+ do_route(From, To, Packet),
+ loop()
+ end.
+
+
+do_route(From, To, Packet) ->
+ ?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
+ [From, To, Packet, 8]),
+ case To of
+ {"", _, _} ->
+ ok;
+ _ ->
+ ejabberd_sm ! {route, From, To, Packet}
+ end,
+ ok.
+
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
--export([route/3]).
+-export([route/3, register_route/1, register_local_route/1]).
-export([start/0, init/0]).
-include("ejabberd.hrl").
-record(route, {domain, node, pid}).
+-record(local_route, {domain, pid}).
start() ->
[{ram_copies, [node()]},
{attributes,
record_info(fields, route)}]),
+ mnesia:create_table(local_route,
+ [{ram_copies, [node()]},
+ {local_content, true},
+ {attributes,
+ record_info(fields, local_route)}]),
loop().
loop() ->
end,
mnesia:transaction(F),
loop();
+ {register_local_route, Domain, Pid} ->
+ F = fun() ->
+ mnesia:write(#local_route{domain = Domain,
+ pid = Pid})
+ end,
+ mnesia:transaction(F),
+ loop();
{unregister_route, Domain} ->
F = fun() ->
case mnesia:wread({route, Domain}) of
?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket ~p~n", [From, To, Packet]),
{DstNode, DstDomain, DstResourse} = To,
F = fun() ->
- case mnesia:read({route, DstDomain}) of
+ case mnesia:read({local_route, DstDomain}) of
[] ->
- error;
+ case mnesia:read({route, DstDomain}) of
+ [] ->
+ error;
+ [R] ->
+ {ok, R#route.node, R#route.pid}
+ end;
[R] ->
- {ok, R#route.node, R#route.pid}
+ {ok, node(), R#local_route.pid}
end
end,
case mnesia:transaction(F) of
ok;
_ ->
Err = jlib:make_error_reply(Packet,
- 502, "Service Unavailable"),
+ "502", "Service Unavailable"),
ejabberd_router ! {route, To, From, Err}
end;
{atomic, {ok, Node, Pid}} ->
- {Pid, Node} ! {packet, From, To, Packet};
+ case node() of
+ Node ->
+ ?DEBUG("routed to process ~p~n", [Pid]),
+ Pid ! {route, From, To, Packet};
+ _ ->
+ ?DEBUG("routed to node ~p~n", [Node]),
+ {ejabberd_router, Node} ! {route, From, To, Packet}
+ end;
_ ->
% TODO
error
route(From, To, Packet) ->
ejabberd_router ! {route, From, To, Packet}.
+register_route(Domain) ->
+ ejabberd_router ! {register_route, Domain, self(), node()}.
+
+register_local_route(Domain) ->
+ ejabberd_router ! {register_local_route, Domain, self()}.
+
-module(ejabberd_sm).
-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
--export([start/0, init/0, open_session/2]).
+-export([start/0, init/0, open_session/2, close_session/2]).
-include_lib("mnemosyne/include/mnemosyne.hrl").
+-include("ejabberd.hrl").
-record(user_resource, {id, user, resource}).
-record(user_resource_id_seq, {name = value, id}).
{local_content, true},
{attributes, record_info(fields, mysession)}]),
mnesia:subscribe(system),
+ %ejabberd_router:register_local_route("localhost"),
loop().
loop() ->
replace_and_register_my_connection(User, Resource, From),
replace_alien_connection(User, Resource),
loop();
+ {close_session, User, Resource} ->
+ remove_connection(User, Resource),
+ 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_sm ! {open_session, User, Resource, self()}.
+close_session(User, Resource) ->
+ ejabberd_sm ! {close_session, User, Resource}.
+
replace_alien_connection(User, Resource) ->
F = fun() ->
[ID] = mnemosyne:eval(query [X.id || X <- table(user_resource),
false
end.
+remove_connection(User, Resource) ->
+ F = fun() ->
+ [ID] = mnemosyne:eval(query [X.id || X <- table(user_resource),
+ X.user = User,
+ X.resource = Resource]
+ end),
+
+ mnesia:delete({mysession, ID}),
+ mnesia:delete({session, ID}),
+ mnesia:delete({user_resource, ID})
+ end,
+ mnesia:transaction(F).
+
replace_and_register_my_connection(User, Resource, Pid) ->
F = fun() ->
IDs = mnemosyne:eval(query [X.id || X <- table(user_resource),
end,
mnesia:transaction(F).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+do_route(From, To, Packet) ->
+ ?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n",
+ [From, To, Packet, 8]),
+ {User, _, Resource} = To,
+ F = fun() ->
+ IDs = mnemosyne:eval(query [X.id || X <- table(user_resource),
+ X.user = User,
+ X.resource = Resource]
+ end),
+ case IDs of
+ [] ->
+ not_exists;
+ [ID] ->
+ case mnesia:read({mysession, ID}) of
+ [] ->
+ [Er] = mnesia:read({session, ID}),
+ {remote, Er#session.node};
+ [El] ->
+ {local, (El#mysession.info)#mysession_info.pid}
+ end
+ 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),
+ ejabberd_c2s:send_element(Pid, {xmlelement, Name, NewAttrs, Els}),
+ ?DEBUG("sended~n", []),
+ ok;
+ {atomic, {remote, Node}} ->
+ ?DEBUG("sending to node ~p~n", [Node]),
+ {ejabberd_sm, Node} ! {route, From, To, Packet},
+ ok;
+ {atomic, not_exists} ->
+ ?DEBUG("packet droped~n", []),
+ ok;
+ {aborted, Reason} ->
+ ?DEBUG("delivery failed: ~p~n", [Reason]),
+ false
+ end.
+
-author('alexey@sevcom.net').
-vsn('$Revision$ ').
--export([make_error_reply/3, make_correct_from_to_attrs/3,
- replace_from_to_attrs/3, string_to_jid/1, tolower/1]).
+-export([make_result_iq_reply/1,
+ make_error_reply/3,
+ make_correct_from_to_attrs/3,
+ replace_from_to_attrs/3,
+ string_to_jid/1,
+ jid_to_string/1,
+ tolower/1]).
%send_iq(From, To, ID, SubTags) ->
% ok.
+make_result_iq_reply({xmlelement, Name, Attrs, SubTags}) ->
+ NewAttrs = make_result_iq_reply_attrs(Attrs),
+ {xmlelement, Name, NewAttrs, SubTags}.
+
+make_result_iq_reply_attrs(Attrs) ->
+ To = xml:get_attr("to", Attrs),
+ From = xml:get_attr("from", Attrs),
+ Attrs1 = lists:keydelete("to", 1, Attrs),
+ Attrs2 = lists:keydelete("from", 1, Attrs1),
+ Attrs3 = case To of
+ {value, ToVal} ->
+ [{"from", ToVal} | Attrs2];
+ _ ->
+ Attrs2
+ end,
+ Attrs4 = case From of
+ {value, FromVal} ->
+ [{"to", FromVal} | Attrs3];
+ _ ->
+ Attrs3
+ end,
+ Attrs5 = lists:keydelete("type", 1, Attrs4),
+ Attrs6 = [{"type", "result"} | Attrs5],
+ Attrs6.
+
make_error_reply({xmlelement, Name, Attrs, SubTags}, Code, Desc) ->
NewAttrs = make_error_reply_attrs(Attrs),
{xmlelement, Name, NewAttrs, SubTags ++ [{xmlelement, "error",
Attrs3.
-replace_from_to_attrs(From,To,Attrs) ->
+replace_from_to_attrs(From, To, Attrs) ->
Attrs1 = lists:keydelete("to", 1, Attrs),
Attrs2 = lists:keydelete("from", 1, Attrs1),
Attrs3 = [{"to", To} | Attrs2],
string_to_jid3([], N, S, R) ->
{N, S, lists:reverse(R)}.
+jid_to_string({Node, Server, Resource}) ->
+ S1 = case Node of
+ "" ->
+ "";
+ _ ->
+ Node ++ "@"
+ end,
+ S2 = S1 ++ Server,
+ S3 = case Resource of
+ "" ->
+ S2;
+ _ ->
+ S2 ++ "/" ++ Resource
+ end,
+ S3.
+
+
% TODO: UNICODE support
tolower_c(C) when C >= $A, C =< $Z ->