diff --git a/ChangeLog b/ChangeLog index 22274b464..ba97da968 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2008-12-16 Jean-Sébastien Pédron + + Merge from trunk (r1709 to r1730). + + Ejabberd should be usable again. + 2008-12-16 Jean-Sébastien Pédron * src/mod_pubsub/mod_pubsub.erl: Convert to exmpp the parts recently @@ -9,6 +15,10 @@ * src/mod_pubsub/mod_pubsub.erl: Fix get_item_name deadlock on transaction +2008-12-12 Alexey Shchepin + + * src/ejabberd_c2s.erl: Bugfix in "from" attribute checking + 2008-12-10 Jean-Sébastien Pédron * src/eldap/eldap_utils.erl (case_insensitive_match/2): Replace @@ -18,9 +28,46 @@ * src/mod_pubsub: Merge from latest trunk (r1716). +2008-12-09 Christophe Romain + + * src/mod_pubsub/mod_pubsub.erl: prevent publish items with invalid + XML schema bugfix (EJAB-699) (previous commit was uncomplete) + and fix bug injected in previous commit + +2008-12-08 Christophe Romain + + * src/ejabberd_c2s.erl: Reduce memory consumption due to caps handling + * src/mod_pubsub/mod_pubsub.erl: Likewise + * src/mod_caps.erl: Likewise + + * src/mod_pubsub/mod_pubsub.erl: ignore unknown configuration fields + (EJAB-762) (thanks to Jack Moffitt) + + * src/mod_pubsub/mod_pubsub.erl: fix node authorization bug (EJAB-798) + send authorization update event (EJAB-799) (thanks to Brian Cully) + + * src/mod_pubsub/mod_pubsub.erl: add nodetree filtering and + authorization (EJAB-813) (thanks to Brian Cully) + * src/mod_pubsub/nodetree_default.erl: Likewise + * src/mod_pubsub/nodetree_virtual.erl: Likewise + * src/mod_pubsub/gen_pubsub_nodetree.erl: Likewise + + * src/mod_pubsub/mod_pubsub.erl: prevent publish items with invalid + XML schema (EJAB-699) + + * src/mod_pubsub/pubsub.hrl: remove unused pubsub_presence record + + * src/mod_pubsub/node_flat.erl: renamed from node_zoo + + * src/mod_pubsub/mod_pubsub.erl: reply to suscriptions options queries + with unsupported feature error (EJAB-713) + + * src/mod_pubsub/node_default.erl: remove pubsub_state record when + unsubscribing node without affiliation (EJAB-776) + 2008-12-08 Jean-Sébastien Pédron - * src/mod_pubsub: Merge from trunk (r1692 to r1709). + Merge from trunk (r1692 to r1709). 2008-12-08 Jean-Sébastien Pédron diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 39c6c6d3a..0b40b5be7 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -37,8 +37,7 @@ send_element/2, socket_type/0, get_presence/1, - get_subscribed/1, - get_subscribed_and_online/1]). + get_subscribed/1]). %% gen_fsm callbacks -export([init/1, @@ -85,7 +84,6 @@ pres_f = ?SETS:new(), pres_a = ?SETS:new(), pres_i = ?SETS:new(), - pres_available = ?DICT:new(), pres_last, pres_pri, pres_timestamp, pres_invis = false, @@ -205,14 +203,8 @@ init([{SockMod, Socket}, Opts]) -> end. %% Return list of all available resources of contacts, -%% in form [{JID, Caps}]. get_subscribed(FsmRef) -> - gen_fsm:sync_send_all_state_event( - FsmRef, get_subscribed, 1000). -get_subscribed_and_online(FsmRef) -> - gen_fsm:sync_send_all_state_event( - FsmRef, get_subscribed_and_online, 1000). - + gen_fsm:sync_send_all_state_event(FsmRef, get_subscribed, 1000). %%---------------------------------------------------------------------- %% Func: StateName/2 @@ -951,29 +943,8 @@ handle_sync_event({get_presence}, _From, StateName, StateData) -> fsm_reply(Reply, StateName, StateData); handle_sync_event(get_subscribed, _From, StateName, StateData) -> - Subscribed = StateData#state.pres_f, - Online = StateData#state.pres_available, - Pred = fun({U, S, _} = User, _Caps) -> - ?SETS:is_element({U, S, undefined}, - Subscribed) orelse - ?SETS:is_element(User, Subscribed) - end, - SubscribedAndOnline = ?DICT:filter(Pred, Online), - SubscribedWithCaps = ?SETS:fold(fun(User, Acc) -> - [{User, undefined}|Acc] - end, ?DICT:to_list(SubscribedAndOnline), Subscribed), - {reply, SubscribedWithCaps, StateName, StateData}; - -handle_sync_event(get_subscribed_and_online, _From, StateName, StateData) -> - Subscribed = StateData#state.pres_f, - Online = StateData#state.pres_available, - Pred = fun({U, S, _R} = User, _Caps) -> - ?SETS:is_element({U, S, undefined}, - Subscribed) orelse - ?SETS:is_element(User, Subscribed) - end, - SubscribedAndOnline = ?DICT:filter(Pred, Online), - {reply, ?DICT:to_list(SubscribedAndOnline), StateName, StateData}; + Subscribed = ?SETS:to_list(StateData#state.pres_f), + {reply, Subscribed, StateName, StateData}; handle_sync_event(_Event, _From, StateName, StateData) -> Reply = ok, @@ -1065,42 +1036,39 @@ handle_info({route, From, To, Packet}, StateName, StateData) -> LFrom = jlib:short_prepd_jid(From), LBFrom = jlib:short_prepd_bare_jid(From), %% Note contact availability - Els = Packet#xmlel.children, - Caps = mod_caps:read_caps(Els), - mod_caps:note_caps(StateData#state.server, From, Caps), - NewAvailable = case exmpp_presence:get_type(Packet) of - 'unavailable' -> - ?DICT:erase(LFrom, StateData#state.pres_available); - _ -> - ?DICT:store(LFrom, Caps, StateData#state.pres_available) - end, - NewStateData = StateData#state{pres_available = NewAvailable}, + case exmpp_presence:get_type(Packet) of + 'unavailable' -> + mod_caps:clear_caps(From); + _ -> + Caps = mod_caps:read_caps(Packet#xmlel.children), + mod_caps:note_caps(StateData#state.server, From, Caps) + end, case ?SETS:is_element( - LFrom, NewStateData#state.pres_a) orelse + LFrom, StateData#state.pres_a) orelse ?SETS:is_element( - LBFrom, NewStateData#state.pres_a) of + LBFrom, StateData#state.pres_a) of true -> - {true, Attrs, NewStateData}; + {true, Attrs, StateData}; false -> case ?SETS:is_element( - LFrom, NewStateData#state.pres_f) of + LFrom, StateData#state.pres_f) of true -> A = ?SETS:add_element( LFrom, - NewStateData#state.pres_a), + StateData#state.pres_a), {true, Attrs, - NewStateData#state{pres_a = A}}; + StateData#state{pres_a = A}}; false -> case ?SETS:is_element( - LBFrom, NewStateData#state.pres_f) of + LBFrom, StateData#state.pres_f) of true -> A = ?SETS:add_element( LBFrom, - NewStateData#state.pres_a), + StateData#state.pres_a), {true, Attrs, - NewStateData#state{pres_a = A}}; + StateData#state{pres_a = A}}; false -> - {true, Attrs, NewStateData} + {true, Attrs, StateData} end end end; @@ -1907,28 +1875,15 @@ is_ip_blacklisted({IP,_Port}) -> check_from(El, FromJID) -> case exmpp_stanza:get_sender(El) of undefined -> - exmpp_stanza:set_sender(El, FromJID); - JIDElString -> + El; + SJID -> try - JIDEl = exmpp_jid:list_to_jid(JIDElString), - case JIDEl#jid.lresource of - undefined -> - %% Matching JID: The stanza is ok - if JIDEl#jid.lnode == FromJID#jid.lnode andalso - JIDEl#jid.ldomain == FromJID#jid.ldomain -> - El; - true -> - 'invalid-from' - end; - _ -> - %% Matching JID: The stanza is ok - if JIDEl#jid.lnode == FromJID#jid.lnode andalso - JIDEl#jid.ldomain == FromJID#jid.ldomain andalso - JIDEl#jid.lresource == FromJID#jid.lresource -> - El; - true -> - 'invalid-from' - end + JID = exmpp_jid:list_to_jid(SJID), + case exmpp_jid:compare_jids(JID, FromJID) of + true -> + El; + false -> + 'invalid-from' end catch _:_ -> diff --git a/src/mod_caps.erl b/src/mod_caps.erl index d58b23587..98ebe1ec3 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -31,7 +31,9 @@ -behaviour(gen_mod). -export([read_caps/1, + get_caps/1, note_caps/3, + clear_caps/1, get_features/2, handle_disco_response/3]). @@ -58,6 +60,7 @@ -record(caps, {node, version, exts}). -record(caps_features, {node_pair, features}). +-record(user_caps, {jid, caps}). -record(state, {host, disco_requests = ?DICT:new(), feature_queries = []}). @@ -80,12 +83,26 @@ read_caps([_ | Tail], Result) -> read_caps([], Result) -> Result. +%% get_caps reads user caps from database +get_caps(JID) -> + case catch mnesia:dirty_read({user_caps, list_to_binary(exmpp_jid:jid_to_list(JID))}) of + [#user_caps{caps=Caps}] -> + Caps; + _ -> + nothing + end. + +%% clear_caps removes user caps from database +clear_caps(JID) -> + catch mnesia:dirty_delete({user_caps, list_to_binary(exmpp_jid:jid_to_list(JID))}). + %% note_caps should be called to make the module request disco %% information. Host is the host that asks, From is the full JID that %% sent the caps packet, and Caps is what read_caps returned. note_caps(Host, From, Caps) -> case Caps of - nothing -> ok; + nothing -> + ok; _ -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:cast(Proc, {note_caps, From, Caps}) @@ -129,7 +146,9 @@ init([Host, _Opts]) -> mnesia:create_table(caps_features, [{ram_copies, [node()]}, {attributes, record_info(fields, caps_features)}]), - mnesia:add_table_copy(caps_features, node(), ram_copies), + mnesia:create_table(user_caps, + [{disc_copies, [node()]}, + {attributes, record_info(fields, user_caps)}]), {ok, #state{host = Host}}. maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) -> @@ -177,10 +196,12 @@ handle_call(stop, _From, State) -> {stop, normal, ok, State}. handle_cast({note_caps, From, - #caps{node = Node, version = Version, exts = Exts}}, + #caps{node = Node, version = Version, exts = Exts} = Caps}, #state{host = Host, disco_requests = Requests} = State) -> %% XXX: this leads to race conditions where ejabberd will send %% lots of caps disco requests. + mnesia:dirty_write(#user_caps{jid = list_to_binary(exmpp_jid:jid_to_list(From)), + caps = Caps}), SubNodes = [Version | Exts], %% Now, find which of these are not already in the database. Fun = fun() -> @@ -195,11 +216,9 @@ handle_cast({note_caps, From, end, case mnesia:transaction(Fun) of {atomic, Missing} -> - %% For each unknown caps "subnode", we send a disco - %% request. - NewRequests = - lists:foldl( - fun(SubNode, Dict) -> + %% For each unknown caps "subnode", we send a disco request. + NewRequests = lists:foldl( + fun(SubNode, Dict) -> ID = randoms:get_string(), Query = exmpp_xml:set_attribute( #xmlel{ns = ?NS_DISCO_INFO, name = 'query'}, diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 41da14324..754d947a6 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -480,9 +480,9 @@ handle_cast({presence, JID, Pid}, State) -> case catch ejabberd_c2s:get_subscribed(Pid) of Contacts when is_list(Contacts) -> lists:foreach( - fun({{User, Server, _}, _}) -> + fun({User, Server, _}) -> Owner = {User, Server, undefined}, - lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options} = PN) -> + lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, options = Options}) -> case get_option(Options, send_last_published_item) of on_sub_and_presence -> case is_caps_notify(ServerHost, Node, LJID) of