diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 63d136a2f..7e0aa4284 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -20,11 +20,12 @@ set_password/2, check_password/2, check_password/4, - try_register/2]). + try_register/2, + dirty_get_registered_users/0, + is_user_exists/1]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, - dirty_get_registered_users/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). -record(state, {}). @@ -162,3 +163,19 @@ try_register(User, Password) -> dirty_get_registered_users() -> mnesia:dirty_all_keys(passwd). +is_user_exists(User) -> + LUser = jlib:tolower(User), + F = fun() -> + case mnesia:read({passwd, LUser}) of + [] -> + false; + [_] -> + true + end + end, + case mnesia:transaction(F) of + {atomic, Res} -> + Res; + _ -> + false + end. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index a5709bf7e..98144665c 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -492,7 +492,9 @@ presence_update(From, Packet, StateData) -> if FromUnavail -> % TODO: watching ourself - + + mod_offline:resend_offline_messages( + StateData#state.user), presence_broadcast_first(From, StateData, Packet); true -> presence_broadcast_to_trusted(From, diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 0bb2181ea..496bf3442 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -24,6 +24,7 @@ start() -> mod_roster:start(), mod_disco:start(), mod_vcard:start(), + mod_offline:start(), ok. init() -> diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index fd8ede288..87c111393 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -18,8 +18,8 @@ -include_lib("mnemosyne/include/mnemosyne.hrl"). -include("ejabberd.hrl"). --record(s2s, {server, node, key}). --record(mys2s, {server, pid}). +-record(s2s, {fromto, node, key}). +-record(mys2s, {fromto, pid}). start() -> @@ -43,8 +43,8 @@ loop() -> % replace_and_register_my_connection(User, Resource, From), % replace_alien_connection(User, Resource), % loop(); - {closed_conection, Server} -> - remove_connection(Server), + {closed_conection, FromTo} -> + remove_connection(FromTo), loop(); %{replace, User, Resource} -> % replace_my_connection(User, Resource), @@ -67,10 +67,10 @@ loop() -> % ejabberd_s2s ! {close_session, User, Resource}. -remove_connection(Server) -> +remove_connection(FromTo) -> F = fun() -> - mnesia:delete({mys2s, Server}), - mnesia:delete({s2s, Server}) + mnesia:delete({mys2s, FromTo}), + mnesia:delete({s2s, FromTo}) end, mnesia:transaction(F). @@ -85,9 +85,9 @@ clean_table_from_bad_node(Node) -> end, mnesia:transaction(F). -have_connection(Server) -> +have_connection(FromTo) -> F = fun() -> - [E] = mnesia:read({s2s, Server}) + [E] = mnesia:read({s2s, FromTo}) end, case mnesia:transaction(F) of {atomic, _} -> @@ -96,9 +96,9 @@ have_connection(Server) -> false end. -get_key(Server) -> +get_key(FromTo) -> F = fun() -> - [E] = mnesia:read({s2s, Server}), + [E] = mnesia:read({s2s, FromTo}), E end, case mnesia:transaction(F) of @@ -108,15 +108,15 @@ get_key(Server) -> "" end. -try_register(Server) -> +try_register(FromTo) -> Key = randoms:get_string(), F = fun() -> - case mnesia:read({s2s, Server}) of + case mnesia:read({s2s, FromTo}) of [] -> - mnesia:write(#s2s{server = Server, + mnesia:write(#s2s{fromto = FromTo, node = node(), key = Key}), - mnesia:write(#mys2s{server = Server, + mnesia:write(#mys2s{fromto = FromTo, pid = self()}), {key, Key}; _ -> @@ -139,16 +139,17 @@ do_route(From, To, Packet) -> [From, To, Packet, 8]), {_, MyServer, _} = From, {User, Server, Resource} = To, + FromTo = {MyServer, Server}, Key = randoms:get_string(), F = fun() -> - case mnesia:read({mys2s, Server}) of + case mnesia:read({mys2s, FromTo}) of [] -> - case mnesia:read({s2s, Server}) of + case mnesia:read({s2s, FromTo}) of [Er] -> {remote, Er#s2s.node}; [] -> % TODO - mnesia:write(#s2s{server = Server, + mnesia:write(#s2s{fromto = FromTo, node = node(), key = Key}), new @@ -174,7 +175,7 @@ do_route(From, To, Packet) -> {atomic, new} -> ?DEBUG("starting new s2s connection~n", []), Pid = ejabberd_s2s_out:start(MyServer, Server, {new, Key}), - mnesia:transaction(fun() -> mnesia:write(#mys2s{server = Server, + mnesia:transaction(fun() -> mnesia:write(#mys2s{fromto = FromTo, pid = Pid}) end), {xmlelement, Name, Attrs, Els} = Packet, NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From), diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index f4c6f0fed..02b66dc62 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -129,7 +129,7 @@ wait_for_key({xmlstreamelement, El}, StateData) -> StateData#state{server = From}}; {verify, To, From, Id, Key} -> io:format("VERIFY KEY: ~p~n", [{To, From, Id, Key}]), - Key1 = ejabberd_s2s:get_key(From), + Key1 = ejabberd_s2s:get_key({StateData#state.myname, From}), Type = if Key == Key1 -> "valid"; true -> "invalid" end, @@ -178,7 +178,7 @@ 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), + Key1 = ejabberd_s2s:get_key({StateData#state.myname, From}), Type = if Key == Key1 -> "valid"; true -> "invalid" end, @@ -209,7 +209,8 @@ stream_established({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), + Key1 = ejabberd_s2s:get_key({StateData#state.myname, + From}), Type = if Key == Key1 -> "valid"; true -> "invalid" end, diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 71dd5e176..0ab41cc1f 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -122,7 +122,9 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> Server = StateData#state.server, New = case StateData#state.new of false -> - case ejabberd_s2s:try_register(Server) of + case + ejabberd_s2s:try_register( + {StateData#state.myname, Server}) of {key, Key} -> Key; false -> @@ -331,7 +333,8 @@ terminate(Reason, StateName, StateData) -> false -> ok; Key -> - ejabberd_s2s ! {closed_conection, StateData#state.server} + ejabberd_s2s ! {closed_conection, {StateData#state.myname, + StateData#state.server}} end, case StateData#state.socket of undefined -> diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 5383240da..95166445b 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -23,7 +23,6 @@ -record(session, {ur, user, node}). -record(mysession, {ur, pid}). -record(presence, {ur, user, priority}). --record(offline_msg, {user, timestamp, xml}). start() -> spawn(ejabberd_sm, init, []). @@ -226,20 +225,9 @@ do_route(From, To, Packet) -> ok end; "message" -> - case catch lists:max(get_user_present_resources(User)) of - {'EXIT', _} -> - % TODO - ok; - {_, R} -> - ejabberd_sm ! {route, - From, - {User, Server, R}, - Packet} - end; + route_message(From, To, Packet); "iq" -> - process_iq(From, To, Packet), - % TODO - ok; + process_iq(From, To, Packet); "broadcast" -> lists:foreach( fun(R) -> @@ -262,8 +250,12 @@ do_route(From, To, Packet) -> {ejabberd_sm, Node} ! {route, From, To, Packet}, ok; {atomic, not_exists} -> - % TODO - ?DEBUG("packet droped~n", []), + if + Name == "message" -> + route_message(From, To, Packet); + true -> + ?DEBUG("packet droped~n", []) + end, ok; {aborted, Reason} -> ?DEBUG("delivery failed: ~p~n", [Reason]), @@ -271,6 +263,24 @@ do_route(From, To, Packet) -> end end. +route_message(From, To, Packet) -> + {User, Server, Resource} = To, + case catch lists:max(get_user_present_resources(User)) of + {'EXIT', _} -> + case ejabberd_auth:is_user_exists(User) of + true -> + mod_offline:store_packet(From, To, Packet); + _ -> + ?DEBUG("packet droped~n", []) + end; + {_, R} -> + ejabberd_sm ! {route, + From, + {User, Server, R}, + Packet} + end. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get_user_resources(User) -> diff --git a/src/jlib.erl b/src/jlib.erl index b2c798cb6..2dbd3f4d7 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -26,8 +26,10 @@ iq_query_info/1, is_iq_request_type/1, iq_to_xml/1, - parse_xdata_submit/1]). + parse_xdata_submit/1, + timestamp_to_xml/1]). +-include("namespaces.hrl"). %send_iq(From, To, ID, SubTags) -> % ok. @@ -289,3 +291,12 @@ parse_xdata_values([{xmlelement, Name, Attrs, SubEls} | Els], Res) -> end; parse_xdata_values([_ | Els], Res) -> parse_xdata_values(Els, Res). + + +timestamp_to_xml({{Year, Month, Day}, {Hour, Minute, Second}}) -> + {xmlelement, "x", + [{"xmlns", ?NS_DELAY}, + {"stamp", lists:flatten( + io_lib:format("~4..0w~2..0w~2..0wT~2..0w:~2..0w:~2..0w", + [Year, Month, Day, Hour, Minute, Second]))}], + []}. diff --git a/src/mod_offline.erl b/src/mod_offline.erl new file mode 100644 index 000000000..e6d340f10 --- /dev/null +++ b/src/mod_offline.erl @@ -0,0 +1,112 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_offline.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 5 Jan 2003 by Alexey Shchepin +%%%---------------------------------------------------------------------- + +-module(mod_offline). +-author('alexey@sevcom.net'). + +-export([start/0, + store_packet/3, + resend_offline_messages/1]). + +-include("namespaces.hrl"). + +-record(offline_msg, {user, timestamp, from, to, packet}). + + +start() -> + mnesia:create_table(offline_msg, + [{disc_only_copies, [node()]}, + {type, bag}, + {attributes, record_info(fields, offline_msg)}]). + + +store_packet(From, To, Packet) -> + case check_event(From, To, Packet) of + true -> + {User, Server, Resource} = To, + LUser = jlib:tolower(User), + TimeStamp = calendar:universal_time(), + F = fun() -> + mnesia:write(#offline_msg{user = LUser, + timestamp = TimeStamp, + from = From, + to = To, + packet = Packet}) + end, + mnesia:transaction(F); + _ -> + ok + end. + +check_event(From, To, Packet) -> + {xmlelement, Name, Attrs, Els} = Packet, + case find_x_event(Els) of + false -> + true; + El -> + case xml:get_subtag(El, "id") of + false -> + case xml:get_subtag(El, "offline") of + false -> + true; + _ -> + ID = case xml:get_tag_attr_s("id", Packet) of + "" -> + {xmlelement, "id", [], []}; + S -> + {xmlelement, "id", [], + [{xmlcdata, S}]} + end, + ejabberd_router:route( + To, From, {xmlelement, Name, Attrs, + [{xmlelement, "x", + [{"xmlns", ?NS_EVENT}], + [ID, + {xmlelement, "offline", [], []}]}] + }), + true + end; + _ -> + false + end + end. + +find_x_event([]) -> + false; +find_x_event([El | Els]) -> + case xml:get_tag_attr_s("xmlns", El) of + ?NS_EVENT -> + El; + _ -> + find_x_event(Els) + end. + + +resend_offline_messages(User) -> + LUser = jlib:tolower(User), + F = fun() -> + Rs = mnesia:read({offline_msg, LUser}), + mnesia:delete({offline_msg, LUser}), + Rs + end, + case mnesia:transaction(F) of + {atomic, Rs} -> + lists:foreach( + fun(R) -> + {xmlelement, Name, Attrs, Els} = R#offline_msg.packet, + ejabberd_sm ! + {route, + R#offline_msg.from, + R#offline_msg.to, + {xmlelement, Name, Attrs, + Els ++ + [jlib:timestamp_to_xml(R#offline_msg.timestamp)]}} + end, + lists:keysort(#offline_msg.timestamp, Rs)); + _ -> + ok + end. diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 796ad58a8..48a58c02e 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -128,8 +128,8 @@ set_vcard(LUser, VCARD) -> 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, "CTRY"}, cdata]), - %locality = xml:get_path_s(VCARD, [{elem, "FN"}, 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]), @@ -140,6 +140,8 @@ set_vcard(LUser, VCARD) -> LMiddle = jlib:tolower(Middle), LNickname = jlib:tolower(Nickname), LBDay = jlib:tolower(BDay), + LCTRY = jlib:tolower(CTRY), + LLocality = jlib:tolower(Locality), LEMail = jlib:tolower(EMail), LOrgName = jlib:tolower(OrgName), LOrgUnit = jlib:tolower(OrgUnit), @@ -153,8 +155,8 @@ set_vcard(LUser, VCARD) -> middle = LMiddle, nickname = LNickname, bday = LBDay, - %ctry = LCTRY, - %locality = LLocality, + ctry = LCTRY, + locality = LLocality, email = LEMail, orgname = LOrgName, orgunit = LOrgUnit @@ -162,6 +164,11 @@ set_vcard(LUser, VCARD) -> end, mnesia:transaction(F). +-define(TLFIELD(Type, Label, Var), + {xmlelement, "field", [{"type", Type}, + {"label", Label}, + {"var", Var}], []}). + -define(FORM, [{xmlelement, "instructions", [], @@ -171,42 +178,18 @@ set_vcard(LUser, VCARD) -> {xmlelement, "instructions", [], [{xmlcdata, "Fill in fields to search " "for any matching Jabber User"}]}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "User"}, - {"var", "user"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Full Name"}, - {"var", "fn"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Name"}, - {"var", "given"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Middle Name"}, - {"var", "middle"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Family Name"}, - {"var", "family"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Nickname"}, - {"var", "nickname"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Birthday"}, - {"var", "bday"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Country"}, - {"var", "ctry"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "City"}, - {"var", "locality"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "email"}, - {"var", "email"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Organization Name"}, - {"var", "orgname"}], []}, - {xmlelement, "field", [{"type", "text-single"}, - {"label", "Organization Unit"}, - {"var", "orgunit"}], []} + ?TLFIELD("text-single", "User", "user"), + ?TLFIELD("text-single", "Full Name", "fn"), + ?TLFIELD("text-single", "Name", "given"), + ?TLFIELD("text-single", "Middle Name", "middle"), + ?TLFIELD("text-single", "Family Name", "family"), + ?TLFIELD("text-single", "Nickname", "nickname"), + ?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") ]}]). @@ -325,23 +308,24 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) -> find_xdata_el1([_ | Els]) -> find_xdata_el1(Els). +-define(LFIELD(Label, Var), + {xmlelement, "field", [{"label", Label}, {"var", Var}], []}). + search_result(Data) -> [{xmlelement, "title", [], [{xmlcdata, "Users Search Results"}]}, {xmlelement, "reported", [], - [{xmlelement, "field", [{"label", "JID"}, {"var", "jid"}], []}, - {xmlelement, "field", [{"label", "Full Name"}, {"var", "fn"}], []}, - {xmlelement, "field", [{"label", "Name"}, {"var", "given"}], []}, - {xmlelement, "field", [{"label", "Middle Name"}, {"var", "middle"}], []}, - {xmlelement, "field", [{"label", "Family Name"}, {"var", "family"}], []}, - {xmlelement, "field", [{"label", "Nickname"}, {"var", "nickname"}], []}, - {xmlelement, "field", [{"label", "Birthday"}, {"var", "bday"}], []}, - {xmlelement, "field", [{"label", "Country"}, {"var", "ctry"}], []}, - {xmlelement, "field", [{"label", "City"}, {"var", "locality"}], []}, - {xmlelement, "field", [{"label", "email"}, {"var", "email"}], []}, - {xmlelement, "field", [{"label", "Organization Name"}, - {"var", "orgname"}], []}, - {xmlelement, "field", [{"label", "Organization Unit"}, - {"var", "orgunit"}], []} + [?LFIELD("JID", "jid"), + ?LFIELD("Full Name", "fn"), + ?LFIELD("Name", "given"), + ?LFIELD("Middle Name", "middle"), + ?LFIELD("Family Name", "family"), + ?LFIELD("Nickname", "nickname"), + ?LFIELD("Birthday", "bday"), + ?LFIELD("Country", "ctry"), + ?LFIELD("City", "locality"), + ?LFIELD("email", "email"), + ?LFIELD("Organization Name", "orgname"), + ?LFIELD("Organization Unit", "orgunit") ]}] ++ lists:map(fun record_to_item/1, search(Data)). -define(FIELD(Var, Val), @@ -402,19 +386,20 @@ filter_fields([], Match) -> Match; filter_fields([{SVar, [Val]} | Ds], Match) when is_list(Val) and (Val /= "") -> + LVal = jlib:tolower(Val), NewMatch = case SVar of - "user" -> Match#vcard_search{user = Val}; - "fn" -> Match#vcard_search{fn = Val}; - "family" -> Match#vcard_search{family = Val}; - "given" -> Match#vcard_search{given = Val}; - "middle" -> Match#vcard_search{middle = Val}; - "nickname" -> Match#vcard_search{nickname = Val}; - "bday" -> Match#vcard_search{bday = Val}; - "ctry" -> Match#vcard_search{ctry = Val}; - "locality" -> Match#vcard_search{locality = Val}; - "email" -> Match#vcard_search{email = Val}; - "orgname" -> Match#vcard_search{orgname = Val}; - "orgunit" -> Match#vcard_search{orgunit = Val}; + "user" -> Match#vcard_search{user = LVal}; + "fn" -> Match#vcard_search{fn = LVal}; + "family" -> Match#vcard_search{family = LVal}; + "given" -> Match#vcard_search{given = LVal}; + "middle" -> Match#vcard_search{middle = LVal}; + "nickname" -> Match#vcard_search{nickname = LVal}; + "bday" -> Match#vcard_search{bday = LVal}; + "ctry" -> Match#vcard_search{ctry = LVal}; + "locality" -> Match#vcard_search{locality = LVal}; + "email" -> Match#vcard_search{email = LVal}; + "orgname" -> Match#vcard_search{orgname = LVal}; + "orgunit" -> Match#vcard_search{orgunit = LVal}; _ -> Match end, filter_fields(Ds, NewMatch); diff --git a/src/namespaces.hrl b/src/namespaces.hrl index 15b3353ca..222b2d8be 100644 --- a/src/namespaces.hrl +++ b/src/namespaces.hrl @@ -10,5 +10,7 @@ -define(NS_VCARD, "vcard-temp"). -define(NS_SEARCH, "jabber:iq:search"). -define(NS_XDATA, "jabber:x:data"). +-define(NS_DELAY, "jabber:x:delay"). +-define(NS_EVENT, "jabber:x:event").