From 44797c9524663c38600743ce3947d80472c849ae Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 2 Jan 2003 21:01:12 +0000 Subject: [PATCH] *** empty log message *** SVN Revision: 25 --- src/ejabberd_c2s.erl | 23 +++- src/ejabberd_local.erl | 1 + src/ejabberd_router.erl | 13 ++- src/jlib.erl | 17 +-- src/mod_disco.erl | 13 ++- src/mod_register.erl | 6 +- src/mod_vcard.erl | 239 ++++++++++++++++++++++++++++++++++++++++ src/namespaces.hrl | 14 +++ src/xml.erl | 33 +++++- 9 files changed, 330 insertions(+), 29 deletions(-) create mode 100644 src/mod_vcard.erl create mode 100644 src/namespaces.hrl diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 170cfcec8..a61890356 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -24,6 +24,7 @@ terminate/3]). -include("ejabberd.hrl"). +-include("namespaces.hrl"). -define(SETS, gb_sets). @@ -288,6 +289,18 @@ handle_info({route, From, To, Packet}, StateName, StateData) -> StateData end, {false, Attrs, NewSt}; + "iq" -> + IQ = jlib:iq_query_info(Packet), + case IQ of + {iq, ID, Type, ?NS_VCARD, SubEl} -> + ResIQ = mod_vcard:process_sm_iq(From, To, IQ), + ejabberd_router:route(To, + From, + jlib:iq_to_xml(ResIQ)), + {false, Attrs, StateData}; + _ -> + {true, Attrs, StateData} + end; _ -> {true, Attrs, StateData} end, @@ -315,6 +328,14 @@ terminate(Reason, StateName, StateData) -> ejabberd_sm:close_session(StateData#state.user, StateData#state.resource) end, + From = {StateData#state.user, + StateData#state.server, + StateData#state.resource}, + Packet = {xmlelement, "presence", [{"type", "unavailable"}], []}, + ejabberd_sm:unset_presence(StateData#state.user, + StateData#state.resource), + presence_broadcast(From, StateData#state.pres_a, Packet), + presence_broadcast(From, StateData#state.pres_i, Packet), StateData#state.sender ! close, ok. @@ -463,7 +484,7 @@ presence_update(From, Packet, StateData) -> "unsubscribed" -> StateData; _ -> - update_priority(jlib:get_subtag(Packet, "priority"), StateData), + update_priority(xml:get_subtag(Packet, "priority"), StateData), FromUnavail = (StateData#state.pres_last == undefined) or StateData#state.pres_invis, ?DEBUG("from unavail = ~p~n", [FromUnavail]), diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index c42a8a019..0bb2181ea 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -23,6 +23,7 @@ start() -> mod_register:start(), mod_roster:start(), mod_disco:start(), + mod_vcard:start(), ok. init() -> diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index e28eeb741..b58bbdb9a 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -10,7 +10,8 @@ -author('alexey@sevcom.net'). -vsn('$Revision$ '). --export([route/3, register_route/1, register_local_route/1]). +-export([route/3, register_route/1, register_local_route/1, + dirty_get_all_routes/0]). -export([start/0, init/0]). @@ -90,7 +91,7 @@ do_route(From, To, Packet) -> [] -> case mnesia:read({route, DstDomain}) of [] -> - error; + false; [R] -> {ok, R#route.node, R#route.pid} end; @@ -99,7 +100,7 @@ do_route(From, To, Packet) -> end end, case mnesia:transaction(F) of - {atomic, error} -> + {atomic, false} -> ejabberd_s2s ! {route, From, To, Packet}; {atomic, {ok, Node, Pid}} -> case node() of @@ -125,3 +126,9 @@ register_route(Domain) -> register_local_route(Domain) -> ejabberd_router ! {register_local_route, Domain, self()}. + +dirty_get_all_routes() -> + lists:delete(?MYNAME, + lists:umerge(lists:sort(mnesia:dirty_all_keys(route)), + lists:sort(mnesia:dirty_all_keys(local_route)))). + diff --git a/src/jlib.erl b/src/jlib.erl index 974117fde..6cafb9598 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -25,8 +25,7 @@ get_iq_namespace/1, iq_query_info/1, is_iq_request_type/1, - iq_to_xml/1, - get_subtag/2]). + iq_to_xml/1]). %send_iq(From, To, ID, SubTags) -> @@ -249,17 +248,3 @@ iq_to_xml({iq, ID, Type, _, SubEl}) -> end. -get_subtag({xmlelement, _, _, Els}, Name) -> - get_subtag1(Els, Name). - -get_subtag1([El | Els], Name) -> - case El of - {xmlelement, Name, _, _} -> - El; - _ -> - get_subtag1(Els, Name) - end; -get_subtag1([], _) -> - false. - - diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 16512ddf5..b49ef1be8 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -2,7 +2,7 @@ %%% File : mod_disco.erl %%% Author : Alexey Shchepin %%% Purpose : -%%% Created : 1 Jan 2003 by Alexey Shchepin +%%% Created : 1 Jan 2003 by Alexey Shchepin %%% Id : $Id$ %%%---------------------------------------------------------------------- @@ -18,9 +18,7 @@ register_feature/1]). -include("ejabberd.hrl"). - --define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items"). --define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info"). +-include("namespaces.hrl"). -define(EMPTY_INFO_RESULT, {iq, ID, result, XMLNS, [{xmlelement, "query", @@ -56,7 +54,9 @@ process_local_iq_items(From, To, {iq, ID, Type, XMLNS, SubEl}) -> get -> case xml:get_tag_attr_s("node", SubEl) of "" -> - Domains = [], + Domains = + lists:map(fun domain_to_xml/1, + ejabberd_router:dirty_get_all_routes()), {iq, ID, result, XMLNS, [{xmlelement, "query", @@ -124,6 +124,9 @@ process_local_iq_info(From, To, {iq, ID, Type, XMLNS, SubEl}) -> feature_to_xml({Feature}) -> {xmlelement, "feature", [{"var", Feature}], []}. +domain_to_xml(Domain) -> + {xmlelement, "item", [{"jid", Domain}], []}. + get_online_users() -> case catch ejabberd_sm:dirty_get_sessions_list() of diff --git a/src/mod_register.erl b/src/mod_register.erl index b9d5be87f..457ca31fc 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -25,9 +25,9 @@ init() -> process_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> case Type of set -> - UTag = jlib:get_subtag(SubEl, "username"), - PTag = jlib:get_subtag(SubEl, "password"), - RTag = jlib:get_subtag(SubEl, "remove"), + UTag = xml:get_subtag(SubEl, "username"), + PTag = xml:get_subtag(SubEl, "password"), + RTag = xml:get_subtag(SubEl, "remove"), if (UTag /= false) and (RTag /= false) -> {iq, ID, error, XMLNS, diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl new file mode 100644 index 000000000..bdef5db63 --- /dev/null +++ b/src/mod_vcard.erl @@ -0,0 +1,239 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_vcard.erl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 2 Jan 2003 by Alexey Shchepin +%%% Id : $Id$ +%%%---------------------------------------------------------------------- + +-module(mod_vcard). +-author('alexey@sevcom.net'). +-vsn('$Revision$ '). + +-export([start/0, init/0, + process_local_iq/3, + process_sm_iq/3]). + +-include("ejabberd.hrl"). +-include("namespaces.hrl"). + + +-record(vcard_search, {user, fn, family, given, middle, nickname, + bday, ctry, locality, email, + orgname, orgunit}). +-record(vcard, {user, vcard}). + + +start() -> + mnesia:create_table(vcard, [{disc_only_copies, [node()]}, + {attributes, record_info(fields, vcard)}]), + mnesia:create_table(vcard_search, + [{disc_copies, [node()]}, + {attributes, record_info(fields, vcard_search)}]), + %mnesia:add_table_index(vcard_search, fn), + %mnesia:add_table_index(vcard_search, n), + %mnesia:add_table_index(vcard_search, nickname), + %mnesia:add_table_index(vcard_search, bday), + %mnesia:add_table_index(vcard_search, ctry), + %mnesia:add_table_index(vcard_search, locality), + %mnesia:add_table_index(vcard_search, email), + %mnesia:add_table_index(vcard_search, orgname), + %mnesia:add_table_index(vcard_search, orgunit), + + + ejabberd_local:register_iq_handler(?NS_VCARD, + ?MODULE, process_local_iq), + ejabberd_sm:register_iq_handler(?NS_VCARD, + ?MODULE, process_sm_iq), + spawn(?MODULE, init, []). + +init() -> + ejabberd_router:register_local_route("ejud." ++ ?MYNAME), + loop(). + +loop() -> + receive + {route, From, To, Packet} -> + do_route(From, To, Packet), + loop(); + _ -> + loop() + end. + + +process_local_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case Type of + set -> + {User, Server, _} = From, + LUser = jlib:tolower(User), + LServer = jlib:tolower(Server), + case ?MYNAME of + LServer -> + set_vcard(LUser, SubEl), + {iq, ID, result, XMLNS, []}; + _ -> + {iq, ID, error, XMLNS, + [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]} + end; + get -> + {iq, ID, result, XMLNS, + [{xmlelement, "vCard", + [{"xmlns", ?NS_VCARD}], + [{xmlelement, "FN", [], + [{xmlcdata, "ejabberd"}]}, + {xmlelement, "URL", [], + [{xmlcdata, + "http://www.jabber.ru/projects/ejabberd/"}]}, + {xmlelement, "DESC", [], + [{xmlcdata, "Erlang Jabber Server\n" + "Copyright (c) 2002, 2003 Alexey Shchepin"}]}, + {xmlelement, "BDAY", [], + [{xmlcdata, "20021116"}]} + ]}]} + end. + + +process_sm_iq(From, To, {iq, ID, Type, XMLNS, SubEl}) -> + case Type of + set -> + {iq, ID, error, XMLNS, [SubEl, {xmlelement, "error", + [{"code", "405"}], + [{xmlcdata, "Not Allowed"}]}]}; + get -> + {User, _, _} = To, + LUser = jlib:tolower(User), + F = fun() -> + mnesia:read({vcard, LUser}) + end, + Els = case mnesia:transaction(F) of + {atomic, Rs} -> + lists:map(fun(R) -> + R#vcard.vcard + end, Rs); + {aborted, Reason} -> + [] + end, + {iq, ID, result, XMLNS, Els} + end. + + +set_vcard(LUser, VCARD) -> + FN = xml:get_path_s(VCARD, [{elem, "FN"}, cdata]), + Family = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "FAMILY"}, cdata]), + Given = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "GIVEN"}, cdata]), + Middle = xml:get_path_s(VCARD, [{elem, "N"}, {elem, "MIDDLE"}, cdata]), + Nickname = xml:get_path_s(VCARD, [{elem, "NICKNAME"}, cdata]), + BDay = xml:get_path_s(VCARD, [{elem, "BDAY"}, cdata]), + %ctry = xml:get_path_s(VCARD, [{elem, "CTRY"}, cdata]), + %locality = xml:get_path_s(VCARD, [{elem, "FN"}, 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]), + F = fun() -> + mnesia:write(#vcard{user = LUser, vcard = VCARD}), + mnesia:write(#vcard_search{user = LUser, + fn = FN, + family = Family, + given = Given, + middle = Middle, + nickname = Nickname, + bday = BDay, + %ctry = CTRY, + %locality = Locality, + email = EMail, + orgname = OrgName, + orgunit = OrgUnit + }) + end, + mnesia:transaction(F). + +-define(FORM, + [{xmlelement, "instructions", [], + [{xmlcdata, "You need a x:data capable client to search"}]}, + {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], + [{xmlelement, "title", [], [{xmlcdata, "Users Search"}]}, + {xmlelement, "instructions", [], + [{xmlcdata, "Fill in fields to search " + "for any matching Jabber User"}]}, + {xmlelement, "field", [{"type", "text-single"}, + {"label", "Full Name"}, + {"var", "fn"}], []} + ]}]). + + + + +do_route(From, To, Packet) -> + {User, Server, Resource} = To, + if + (User /= "") or (Resource /= "") -> + Err = jlib:make_error_reply(Packet, "503", "Service Unavailable"), + ejabberd_router ! {route, To, From, Err}; + true -> + IQ = jlib:iq_query_info(Packet), + case IQ of + {iq, ID, Type, ?NS_SEARCH, SubEl} -> + case Type of + set -> + % TODO + Err = jlib:make_error_reply( + Packet, "501", "Not Implemented"), + ejabberd_router:route(To, From, Err); + get -> + ResIQ = {iq, ID, result, ?NS_SEARCH, + [{xmlelement, + "query", + [{"xmlns", ?NS_SEARCH}], + ?FORM + }]}, + ejabberd_router:route(To, + From, + jlib:iq_to_xml(ResIQ)) + end; + {iq, ID, Type, ?NS_DISCO_INFO, SubEl} -> + case Type of + set -> + Err = jlib:make_error_reply( + Packet, "405", "Not Allowed"), + ejabberd_router:route(To, From, Err); + get -> + ResIQ = {iq, ID, result, ?NS_DISCO_INFO, + [{xmlelement, + "query", + [{"xmlns", ?NS_DISCO_INFO}], + [{xmlelement, "identity", + [{"category", "directory"}, + {"type", "user"}, + {"name", "EJUD"}], []}, + {xmlelement, "feature", + [{"var", ?NS_SEARCH}], []} + ] + }]}, + ejabberd_router:route(To, + From, + jlib:iq_to_xml(ResIQ)) + end; + {iq, ID, Type, ?NS_DISCO_ITEMS, SubEl} -> + case Type of + set -> + Err = jlib:make_error_reply( + Packet, "405", "Not Allowed"), + ejabberd_router:route(To, From, Err); + get -> + ResIQ = {iq, ID, result, ?NS_DISCO_INFO, + [{xmlelement, + "query", + [{"xmlns", ?NS_DISCO_INFO}], []}]}, + ejabberd_router:route(To, + From, + jlib:iq_to_xml(ResIQ)) + end; + _ -> + Err = jlib:make_error_reply(Packet, + "503", "Service Unavailable"), + ejabberd_router:route(To, From, Err) + end + end. + diff --git a/src/namespaces.hrl b/src/namespaces.hrl new file mode 100644 index 000000000..15b3353ca --- /dev/null +++ b/src/namespaces.hrl @@ -0,0 +1,14 @@ +%%%---------------------------------------------------------------------- +%%% File : namespaces.hrl +%%% Author : Alexey Shchepin +%%% Purpose : +%%% Created : 2 Jan 2003 by Alexey Shchepin +%%%---------------------------------------------------------------------- + +-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items"). +-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info"). +-define(NS_VCARD, "vcard-temp"). +-define(NS_SEARCH, "jabber:iq:search"). +-define(NS_XDATA, "jabber:x:data"). + + diff --git a/src/xml.erl b/src/xml.erl index 171607361..00b7087d5 100644 --- a/src/xml.erl +++ b/src/xml.erl @@ -13,7 +13,9 @@ -export([element_to_string/1, crypt/1, remove_cdata/1, get_cdata/1, get_tag_cdata/1, get_attr/2, get_attr_s/2, - get_tag_attr/2, get_tag_attr_s/2]). + get_tag_attr/2, get_tag_attr_s/2, + get_subtag/2, + get_path_s/2]). element_to_string(El) -> case El of @@ -100,3 +102,32 @@ get_tag_attr(AttrName, {xmlelement, Name, Attrs, Els}) -> get_tag_attr_s(AttrName, {xmlelement, Name, Attrs, Els}) -> get_attr_s(AttrName, Attrs). + +get_subtag({xmlelement, _, _, Els}, Name) -> + get_subtag1(Els, Name). + +get_subtag1([El | Els], Name) -> + case El of + {xmlelement, Name, _, _} -> + El; + _ -> + get_subtag1(Els, Name) + end; +get_subtag1([], _) -> + false. + + +get_path_s(El, []) -> + El; +get_path_s(El, [{elem, Name} | Path]) -> + case get_subtag(El, Name) of + false -> + ""; + SubEl -> + get_path_s(SubEl, Path) + end; +get_path_s(El, [{attr, Name}]) -> + get_tag_attr_s(Name, El); +get_path_s(El, [cdata]) -> + get_tag_cdata(El). +