From e710a24860af9cb127478c8735b08f4bf1ff3697 Mon Sep 17 00:00:00 2001
From: Christophe Romain <christophe.romain@process-one.net>
Date: Sat, 3 Jan 2009 00:25:40 +0000
Subject: [PATCH] PubSub cleanup, EJAB-827 fix, EJAB-701 partial fix

SVN Revision: 1766
---
 ChangeLog                       |  18 ++
 src/mod_pubsub/mod_pubsub.erl   | 422 +++++++++++++-------------------
 src/mod_pubsub/node_default.erl | 213 +++++++---------
 src/mod_pubsub/node_pep.erl     |  12 +-
 src/mod_pubsub/pubsub.hrl       |  10 +-
 5 files changed, 284 insertions(+), 391 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f7f84b8c2..0440b6a29 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2009-01-03  Christophe Romain <christophe.romain@process-one.net>
+
+	* src/mod_pubsub/mod_pubsub.erl: deliver notification depending on
+	presence-based-delivery configuration (EJAB-827). notification code
+	rewrite.
+
+	* src/mod_pubsub/mod_pubsub.erl: code cleanning, minor bugfixes
+	* src/mod_pubsub/node_default.erl: Likewise
+	* src/mod_pubsub/node_pep.erl: Likewise
+	* src/mod_pubsub/pubsub.hrl: Likewise
+
+	* src/mod_pubsub/mod_pubsub.erl: prevent subscribing with full jid,
+	waiting for full jid support (EJAB-701)
+
+	* src/mod_pubsub/mod_pubsub.erl: use of delete-any feature instead of
+	delete-nodes for delete item use case (fix from erroneous definition
+	in XEP-0060)
+
 2008-12-29  Alexey Shchepin  <alexey@process-one.net>
 
 	* src/ejabberd_c2s.erl: Bugfix in "from" attribute checking
diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl
index e90822081..ceadbd5e6 100644
--- a/src/mod_pubsub/mod_pubsub.erl
+++ b/src/mod_pubsub/mod_pubsub.erl
@@ -30,7 +30,7 @@
 %%%
 %%% @reference See <a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060: Pubsub</a> for
 %%% the latest version of the PubSub specification.
-%%% This module uses version 1.11 of the specification as a base.
+%%% This module uses version 1.12 of the specification as a base.
 %%% Most of the specification is implemented.
 %%% Functions concerning configuration should be rewritten.
 %%% Code is derivated from the original pubsub v1.7, by Alexey Shchepin <alexey@process-one.net>
@@ -40,7 +40,7 @@
 
 -module(mod_pubsub).
 -author('christophe.romain@process-one.net').
--version('1.11-01').
+-version('1.12-01').
 
 -behaviour(gen_server).
 -behaviour(gen_mod).
@@ -91,9 +91,7 @@
 	 string_to_subscription/1,
 	 string_to_affiliation/1,
 	 extended_error/2,
-	 extended_error/3,
-	 make_stanza/3,
-	 route_stanza/3
+	 extended_error/3
 	]).
 
 %% API and gen_server callbacks
@@ -1084,14 +1082,12 @@ find_authorization_response(Packet) ->
 %%	 Plugins = [Plugin::string()]
 %% @doc Send a message to JID with the supplied Subscription
 send_authorization_approval(Host, JID, Node, Subscription) ->
-    Stanza = {xmlelement, "message",
-	      [],
-	      [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}],
-		[{xmlelement, "subscription",
-		  [{"node", Node},
-		   {"jid", jlib:jid_to_string(JID)},
-		   {"subscription", subscription_to_string(Subscription)}],
-		  []}]}]},
+    Stanza = event_stanza(
+	[{xmlelement, "subscription",
+	 [{"node", Node},
+	  {"jid", jlib:jid_to_string(JID)},
+	  {"subscription", subscription_to_string(Subscription)}],
+	 []}]),
     ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
 
 handle_authorization_response(Host, From, To, Packet, XFields) ->
@@ -1282,7 +1278,6 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
 		    %%	[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}],
 		    %%		[?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI),
 		    %%		?XFIELD("jid-single", "Node Creator", "creator", jlib:jid_to_string(OwnerKey))]}]),
-		    %% todo publish_item(Host, ServerHost, ["pubsub", "nodes"], node_to_string(Node)),
 		    case Result of
 			default -> {result, Reply};
 			_ -> {result, Result}
@@ -1328,14 +1323,17 @@ delete_node(Host, Node, Owner) ->
 	{error, Error} ->
 	    {error, Error};
 	{result, {Result, broadcast, Removed}} ->
-	    broadcast_removed_node(Host, Removed),
-	    %%broadcast_retract_item(Host, ["pubsub", "nodes"], node_to_string(Node)),
+	    lists:foreach(fun(RNode) ->
+		broadcast_removed_node(Host, RNode)
+	    end, Removed),
 	    case Result of
 		default -> {result, Reply};
 		_ -> {result, Result}
 	    end;
 	{result, {Result, Removed}} ->
-	    broadcast_removed_node(Host, Removed),
+	    lists:foreach(fun(RNode) ->
+		broadcast_removed_node(Host, RNode)
+	    end, Removed),
 	    case Result of
 		default -> {result, Reply};
 		_ -> {result, Result}
@@ -1543,23 +1541,17 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
 	{error, Reason} ->
 	    {error, Reason};
 	{result, {Result, broadcast, Removed}} ->
-	    lists:foreach(fun(OldItem) ->
-				  broadcast_retract_item(Host, Node, OldItem)
-			  end, Removed),
+	    broadcast_retract_items(Host, Node, Removed),
 	    broadcast_publish_item(Host, Node, ItemId, jlib:jid_tolower(Publisher), Payload),
 	    case Result of
 		default -> {result, Reply};
 		_ -> {result, Result}
 	    end;
 	{result, default, Removed} ->
-	    lists:foreach(fun(OldItem) ->
-				  broadcast_retract_item(Host, Node, OldItem)
-			  end, Removed),
+	    broadcast_retract_items(Host, Node, Removed),
 	    {result, Reply};
 	{result, Result, Removed} ->
-	    lists:foreach(fun(OldItem) ->
-				  broadcast_retract_item(Host, Node, OldItem)
-			  end, Removed),
+	    broadcast_retract_items(Host, Node, Removed),
 	    {result, Result};
 	{result, default} ->
 	    {result, Reply};
@@ -1590,7 +1582,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
     Action = fun(#pubsub_node{type = Type}) ->
 		     Features = features(Type),
 		     PersistentFeature = lists:member("persistent-items", Features),
-		     DeleteFeature = lists:member("delete-nodes", Features),
+		     DeleteFeature = lists:member("delete-any", Features),
 		     if
 			 %%->   iq_pubsub just does that matchs
 			 %%	%% Request does not specify an item
@@ -1600,7 +1592,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
 			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "persistent-items")};
 			 not DeleteFeature ->
 			     %% Service does not support item deletion
-			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "delete-nodes")};
+			     {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "delete-any")};
 			 true ->
 			     node_call(Type, delete_item, [Host, Node, Publisher, ItemId])
 		     end
@@ -1610,7 +1602,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
 	{error, Reason} ->
 	    {error, Reason};
 	{result, {Result, broadcast}} ->
-	    broadcast_retract_item(Host, Node, ItemId, ForceNotify),
+	    broadcast_retract_items(Host, Node, [ItemId], ForceNotify),
 	    case Result of
 		default -> {result, Reply};
 		_ -> {result, Result}
@@ -1778,12 +1770,10 @@ send_items(Host, Node, LJID, Number) ->
 		    [First|Tail] = Items,
 		    [lists:foldl(
 			fun(CurItem, LastItem) ->
-			    {_, {LMS, LS, LmS}} = LastItem#pubsub_item.creation,
-			    {_, {CMS, CS, CmS}} = CurItem#pubsub_item.creation,
-			    LTimestamp = LMS * 1000000 + LS * 1000 + LmS,
-			    CTimestamp = CMS * 1000000 + CS * 1000 + CmS,
+			    {_, LTimeStamp} = LastItem#pubsub_item.creation,
+			    {_, CTimeStamp} = CurItem#pubsub_item.creation,
 			    if
-				CTimestamp > LTimestamp -> CurItem;
+				CTimeStamp > LTimeStamp -> CurItem;
 				true -> LastItem
 			    end
 			end, First, Tail)];
@@ -1802,10 +1792,9 @@ send_items(Host, Node, LJID, Number) ->
 		    end,
 		    {xmlelement, "item", ItemAttrs, Payload}
 		end, ToSend),
-    Stanza = {xmlelement, "message", [],
-	      [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}],
-		[{xmlelement, "items", [{"node", node_to_string(Node)}],
-		  ItemsEls}]}]},
+    Stanza = event_stanza(
+	[{xmlelement, "items", [{"node", node_to_string(Node)}],
+	 ItemsEls}]),
     ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza}.
 
 %% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
@@ -2136,10 +2125,10 @@ service_jid(Host) ->
 %%	Subscription = atom()
 %%	PresenceDelivery = boolean()
 %% @doc <p>Check if a notification must be delivered or not.</p>
-is_to_delivered(_, none, _) -> false;
-is_to_delivered(_, pending, _) -> false;
-is_to_delivered(_, _, false) -> true;
-is_to_delivered({User, Server, _}, _, true) ->
+is_to_deliver(_, none, _) -> false;
+is_to_deliver(_, pending, _) -> false;
+is_to_deliver(_, _, false) -> true;
+is_to_deliver({User, Server, _}, _, true) ->
     case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
     [] -> false;
     Ss ->
@@ -2157,233 +2146,172 @@ payload_xmlelements([], Count) -> Count;
 payload_xmlelements([{xmlelement, _, _, _}|Tail], Count) -> payload_xmlelements(Tail, Count+1);
 payload_xmlelements([_|Tail], Count) -> payload_xmlelements(Tail, Count).
 
+%% @spec (Els) -> stanza()
+%%	Els = [xmlelement()]
+%% @doc <p>Build pubsub event stanza
+event_stanza(Els) ->
+    {xmlelement, "message", [],
+     [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], Els}]}.
+
 %%%%%% broadcast functions
 
 broadcast_publish_item(Host, Node, ItemId, _From, Payload) ->
     Action =
 	fun(#pubsub_node{options = Options, type = Type}) ->
-		case node_call(Type, get_states, [Host, Node]) of
-		    {error, _} -> {result, false};
-		    {result, []} -> {result, false};
-		    {result, States} ->
-			PresenceDelivery = get_option(Options, presence_based_delivery),
-			BroadcastAll = get_option(Options, broadcast_all_resources),
-			Content = case get_option(Options, deliver_payloads) of
-			    true -> Payload;
-			    false -> []
-			end,
-			ItemAttrs = case ItemId of
-			    "" -> [];
-			    _ -> [{"id", ItemId}]
-			end,
-			Stanza = make_stanza(Node, ItemAttrs, Content),
-			lists:foreach(
-			  fun(#pubsub_state{stateid = {LJID, _},
-					    subscription = Subscription}) ->
-				case is_to_delivered(LJID, Subscription, PresenceDelivery) of
-				    true ->
-					DestJIDs = case BroadcastAll of
-					    true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID));
-					    false -> [LJID]
-					end,
-					route_stanza(Host, DestJIDs, Stanza);
-				    false ->
-					ok
-				end
-			  end, States),
-			broadcast_by_caps(Host, Node, Type, Stanza),
-			{result, true}
-		end
+	    case node_call(Type, get_states, [Host, Node]) of
+		{result, []} -> 
+		    {result, false};
+		{result, States} ->
+		    Content = case get_option(Options, deliver_payloads) of
+			true -> Payload;
+			false -> []
+		    end,
+		    ItemAttrs = case ItemId of
+			"" -> [];
+			_ -> [{"id", ItemId}]
+		    end,
+		    Stanza = event_stanza(
+			[{xmlelement, "items", [{"node", node_to_string(Node)}],
+			 [{xmlelement, "item", ItemAttrs, Content}]}]),
+		    broadcast_stanza(Host, Options, States, Stanza),
+		    broadcast_by_caps(Host, Node, Type, Stanza),
+		    {result, true};
+		_ ->
+		    {result, false}
+	    end
 	end,
     transaction(Host, Node, Action, sync_dirty).
 
-%% ItemAttrs is a list of tuples:
-%% For example: [{"id", ItemId}]
-make_stanza(Node, ItemAttrs, Payload) ->
-    {xmlelement, "message", [],
-     [{xmlelement, "event",
-       [{"xmlns", ?NS_PUBSUB_EVENT}],
-       [{xmlelement, "items", [{"node", node_to_string(Node)}],
-	 [{xmlelement, "item", ItemAttrs, Payload}]}]}]}.
-
-%% DestJIDs = [{LUser, LServer, LResource}]
-route_stanza(Host, DestJIDs, Stanza) ->
-    lists:foreach(
-      fun(DestJID) ->
-	      ejabberd_router ! {route, service_jid(Host), jlib:make_jid(DestJID), Stanza}
-      end, DestJIDs). 
-
-broadcast_retract_item(Host, Node, ItemId) ->
-    broadcast_retract_item(Host, Node, ItemId, false).
-broadcast_retract_item(Host, Node, ItemId, ForceNotify) ->
+broadcast_retract_items(Host, Node, ItemIds) ->
+    broadcast_retract_items(Host, Node, ItemIds, false).
+broadcast_retract_items(Host, Node, ItemIds, ForceNotify) ->
     Action =
 	fun(#pubsub_node{options = Options, type = Type}) ->
-		case node_call(Type, get_states, [Host, Node]) of
-		    {error, _} -> {result, false};
-		    {result, []} -> {result, false};
-		    {result, States} ->
-			Notify = case ForceNotify of
-				     true -> true;
-				     _ -> get_option(Options, notify_retract)
-				 end,
-			ItemAttrs = case ItemId of
-			    "" -> [];
-			    _ -> [{"id", ItemId}]
-			end,
-			Stanza = {xmlelement, "message", [],
-				   [{xmlelement, "event",
-				     [{"xmlns", ?NS_PUBSUB_EVENT}],
-				       [{xmlelement, "items", [{"node", node_to_string(Node)}],
-				         [{xmlelement, "retract", ItemAttrs, []}]}]}]},
-			case Notify of
-			    true ->
-				lists:foreach(
-				  fun(#pubsub_state{stateid = {JID, _},
-						    subscription = Subscription}) ->
-					if (Subscription /= none) and
-					   (Subscription /= pending) ->
-					    ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza};
-					   true ->
-					    ok
-					end
-				  end, States),
-				broadcast_by_caps(Host, Node, Type, Stanza),
-				{result, true};
-			    false ->
-				{result, false}
-			end
-		end
+	    case (get_option(Options, notify_retract) or ForceNotify) of
+		true ->
+		    case node_call(Type, get_states, [Host, Node]) of
+			{result, []} -> 
+			    {result, false};
+			{result, States} ->
+			    RetractEls = lists:map(
+				fun(ItemId) ->
+				    ItemAttrs = case ItemId of
+					"" -> [];
+					_ -> [{"id", ItemId}]
+				    end,
+				    {xmlelement, "retract", ItemAttrs, []}
+				end, ItemIds),
+			    Stanza = event_stanza(
+				[{xmlelement, "items", [{"node", node_to_string(Node)}],
+				 RetractEls}]),
+			    broadcast_stanza(Host, Options, States, Stanza),
+			    broadcast_by_caps(Host, Node, Type, Stanza),
+			    {result, true};
+			_ ->
+			    {result, false}
+		    end;
+		_ ->
+		    {result, false}
+	    end
 	end,
     transaction(Host, Node, Action, sync_dirty).
 
 broadcast_purge_node(Host, Node) ->
     Action =
 	fun(#pubsub_node{options = Options, type = Type}) ->
-		case node_call(Type, get_states, [Host, Node]) of
-		    {error, _} -> {result, false};
-		    {result, []} -> {result, false};
-		    {result, States} ->
-			Stanza = {xmlelement, "message", [],
-				   [{xmlelement, "event",
-				     [{"xmlns", ?NS_PUBSUB_EVENT}],
-				       [{xmlelement, "purge", [{"node", node_to_string(Node)}],
-				         []}]}]},
-			case get_option(Options, notify_retract) of
-			    true ->
-				lists:foreach(
-				  fun(#pubsub_state{stateid = {JID,_},
-						    subscription = Subscription}) ->
-					if (Subscription /= none) and
-					   (Subscription /= pending) ->
-						ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza};
-					   true ->
-					    ok
-					end
-				  end, States),
-				broadcast_by_caps(Host, Node, Type, Stanza),
-				{result, true};
-			    false ->
-				{result, false}
-			end
-		end
+	    case get_option(Options, notify_retract) of
+		true ->
+		    case node_call(Type, get_states, [Host, Node]) of
+			{result, []} -> 
+			    {result, false};
+			{result, States} ->
+			    Stanza = event_stanza(
+				[{xmlelement, "purge", [{"node", node_to_string(Node)}], []}]),
+			    broadcast_stanza(Host, Options, States, Stanza),
+			    broadcast_by_caps(Host, Node, Type, Stanza),
+			    {result, true};
+			_ -> 
+			    {result, false}
+		    end;
+		_ ->
+		    {result, false}
+	    end
 	end,
     transaction(Host, Node, Action, sync_dirty).
 
-broadcast_removed_node(Host, Removed) ->
-    lists:foreach(
-      fun(Node) ->
-	      Action =
-		  fun(#pubsub_node{options = Options, type = Type}) ->
-			Stanza = {xmlelement, "message", [],
-				 [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}],
-				   [{xmlelement, "delete", [{"node", node_to_string(Node)}],
-				     []}]}]},
-			case get_option(Options, notify_delete) of
-			    true ->
-				case node_call(Type, get_states, [Host, Node]) of
-				    {result, States} ->
-					lists:foreach(
-					    fun(#pubsub_state{stateid = {JID, _},
-						subscription = Subscription}) ->
-					    if (Subscription /= none) and
-					       (Subscription /= pending) ->
-						ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza};
-					       true ->
-						ok
-					    end
-					end, States),
-					broadcast_by_caps(Host, Node, Type, Stanza),
-					{result, true};
-				    _ ->
-					{result, false}
-				end;
-			    _ ->
-				{result, false}
-			end
-		  end,
-	      transaction(Host, Node, Action, sync_dirty)
-      end, Removed).
+broadcast_removed_node(Host, Node) ->
+    Action =
+	fun(#pubsub_node{options = Options, type = Type}) ->
+	    case get_option(Options, notify_delete) of
+		true ->
+		    case node_call(Type, get_states, [Host, Node]) of
+			{result, []} -> 
+			    {result, false};
+			{result, States} ->
+			    Stanza = event_stanza(
+				[{xmlelement, "delete", [{"node", node_to_string(Node)}], []}]),
+			    broadcast_stanza(Host, Options, States, Stanza),
+			    broadcast_by_caps(Host, Node, Type, Stanza),
+			    {result, true};
+			_ ->
+			    {result, false}
+		    end;
+		_ ->
+		    {result, false}
+	    end
+	end,
+    transaction(Host, Node, Action, sync_dirty).
 
 broadcast_config_notification(Host, Node, Lang) ->
     Action =
 	fun(#pubsub_node{options = Options, owners = Owners, type = Type}) ->
-		case node_call(Type, get_states, [Host, Node]) of
-		    {error, _} -> {result, false};
-		    {result, []} -> {result, false};
-		    {result, States} ->
-			case get_option(Options, notify_config) of
-			    true ->
-				PresenceDelivery = get_option(Options, presence_based_delivery),
-				Content = case get_option(Options, deliver_payloads) of
-				    true ->
-					[{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
-					get_configure_xfields(Type, Options, Lang, Owners)}];
-				    false ->
-					[]
-				end,
-				Stanza = {xmlelement, "message", [],
-					   [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}],
-					     [{xmlelement, "items", [{"node", node_to_string(Node)}],
-					       [{xmlelement, "item", [{"id", "configuration"}],
-					         Content}]}]}]},
-				lists:foreach(
-				  fun(#pubsub_state{stateid = {LJID, _},
-						    subscription = Subscription}) ->
-					case is_to_delivered(LJID, Subscription, PresenceDelivery) of
-					    true ->
-						ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza};
-					    false ->
-						ok
-					end
-				  end, States),
-				broadcast_by_caps(Host, Node, Type, Stanza),
-				{result, true};
-			    _ ->
-				{result, false}
-			end
-		end
+	    case get_option(Options, notify_config) of
+		true ->
+		    case node_call(Type, get_states, [Host, Node]) of
+			{result, []} -> 
+			    {result, false};
+			{result, States} ->
+			    Content = case get_option(Options, deliver_payloads) of
+				true ->
+				    [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
+				     get_configure_xfields(Type, Options, Lang, Owners)}];
+				false ->
+				    []
+			    end,
+			    Stanza = event_stanza(
+				[{xmlelement, "items", [{"node", node_to_string(Node)}],
+				 [{xmlelement, "item", [{"id", "configuration"}],
+				  Content}]}]),
+			    broadcast_stanza(Host, Options, States, Stanza),
+			    broadcast_by_caps(Host, Node, Type, Stanza),
+			    {result, true};
+			_ -> 
+			    {result, false}
+		    end;
+		_ ->
+		    {result, false}
+	    end
 	end,
     transaction(Host, Node, Action, sync_dirty).
 
-%TODO: simplify broadcast_* using a generic function like that:
-%broadcast(Host, Node, Fun) ->
-%	transaction(fun() ->
-%		case tree_call(Host, get_node, [Host, Node]) of
-%		#pubsub_node{options = Options, owners = Owners, type = Type} ->
-%			case node_call(Type, get_states, [Host, Node]) of
-%			{error, _} -> {result, false};
-%			{result, []} -> {result, false};
-%			{result, States} ->
-%				lists:foreach(fun(#pubsub_state{stateid = {JID,_}, subscription = Subscription}) ->
-%					Fun(Host, Node, Options, Owners, JID, Subscription)
-%				end, States),
-%				{result, true}
-%			end;
-%		Other ->
-%			Other
-%		end
-%	end, sync_dirty).
-
+broadcast_stanza(Host, NodeOpts, States, Stanza) ->
+    PresenceDelivery = get_option(NodeOpts, presence_based_delivery),
+    BroadcastAll = get_option(NodeOpts, broadcast_all_resources),
+    From = service_jid(Host),
+    lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) ->
+	case is_to_deliver(LJID, Subs, PresenceDelivery) of
+	    true ->
+		JIDs = case BroadcastAll of
+		    true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID));
+		    false -> [LJID]
+		end,
+		lists:foreach(fun(JID) ->
+		    ejabberd_router ! {route, From, jlib:make_jid(JID), Stanza}
+		end, JIDs);
+	    false ->
+		ok
+	end
+    end, States).
 
 %% broadcast Stanza to all contacts of the user that are advertising
 %% interest in this kind of Node.
@@ -2734,18 +2662,18 @@ select_type(ServerHost, Host, Node) ->
 
 features() ->
 	[
-	 %"access-authorize",   % OPTIONAL
+	 %TODO "access-authorize",   % OPTIONAL
 	 "access-open",   % OPTIONAL this relates to access_model option in node_default
 	 "access-presence",   % OPTIONAL this relates to access_model option in node_pep
-	 %"access-roster",   % OPTIONAL
-	 %"access-whitelist",   % OPTIONAL
+	 %TODO "access-roster",   % OPTIONAL
+	 %TODO "access-whitelist",   % OPTIONAL
 	 % see plugin "auto-create",   % OPTIONAL
 	 % see plugin "auto-subscribe",   % RECOMMENDED
 	 "collections",   % RECOMMENDED
 	 "config-node",   % RECOMMENDED
 	 "create-and-configure",   % RECOMMENDED
 	 % see plugin "create-nodes",   % RECOMMENDED
-	 %TODO "delete-any",   % OPTIONAL
+	 % see plugin "delete-any",   % RECOMMENDED
 	 % see plugin "delete-nodes",   % RECOMMENDED
 	 % see plugin "filtered-notifications",   % RECOMMENDED
 	 %TODO "get-pending",   % OPTIONAL
diff --git a/src/mod_pubsub/node_default.erl b/src/mod_pubsub/node_default.erl
index 9bf26b992..d7fdb450e 100644
--- a/src/mod_pubsub/node_default.erl
+++ b/src/mod_pubsub/node_default.erl
@@ -159,6 +159,7 @@ features() ->
     ["create-nodes",
      "auto-create",
      "delete-nodes",
+     "delete-any",
      "instant-nodes",
      "manage-subscriptions",
      "modify-affiliations",
@@ -220,8 +221,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
 %% @doc <p></p>
 create_node(Host, Node, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    mnesia:write(#pubsub_state{stateid = {OwnerKey, {Host, Node}},
-			       affiliation = owner, subscription = none}),
+    set_state(#pubsub_state{stateid = {OwnerKey, {Host, Node}}, affiliation = owner}),
     {result, {default, broadcast}}.
 
 
@@ -234,12 +234,8 @@ delete_node(Host, Removed) ->
       fun(Node) ->
 	      lists:foreach(
 		fun(#pubsub_state{stateid = StateId, items = Items}) ->
-			lists:foreach(
-			  fun(ItemId) ->
-				  mnesia:delete(
-				    {pubsub_item, {ItemId, {Host, Node}}})
-			  end, Items),
-			mnesia:delete({pubsub_state, StateId})
+			del_items(Host, Node, Items),
+			del_state(StateId)
 		end,
 		mnesia:match_object(
 		  #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}))
@@ -281,14 +277,9 @@ delete_node(Host, Removed) ->
 %% <p>In the default plugin module, the record is unchanged.</p>
 subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
 	       SendLast, PresenceSubscription, RosterGroup) ->
-    SenderKey = jlib:jid_tolower(Sender),
-    Authorized = (jlib:jid_remove_resource(SenderKey) == jlib:jid_remove_resource(Subscriber)),
-						% TODO add some acl check for Authorized ?
-    State = case get_state(Host, Node, Subscriber) of
-		{error, ?ERR_ITEM_NOT_FOUND} ->
-		    #pubsub_state{stateid = {Subscriber, {Host, Node}}}; % TODO: bug on Key ?
-		{result, S} -> S
-	    end,
+    SubscriberKey = jlib:jid_tolower(jlib:jid_remove_resource(Subscriber)),
+    Authorized = (jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == SubscriberKey),
+    State = get_state(Host, Node, SubscriberKey),
     #pubsub_state{affiliation = Affiliation,
 		  subscription = Subscription} = State,
     if
@@ -324,7 +315,6 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
 		if
 		    AccessModel == authorize ->
 			pending;
-		    %%TODO Affiliation == none -> ?
 		    %%NeedConfiguration ->
 		    %%	unconfigured
 		    true ->
@@ -352,43 +342,29 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
 %%	 Reason = mod_pubsub:stanzaError()
 %% @doc <p>Unsubscribe the <tt>Subscriber</tt> from the <tt>Node</tt>.</p>
 unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) ->
-    SenderKey = jlib:jid_tolower(Sender),
-    Match = jlib:jid_remove_resource(SenderKey) == jlib:jid_remove_resource(Subscriber),
-    Authorized = case Match of
-		     true ->
-			 true;
-		     false ->
-			 case get_state(Host, Node, SenderKey) of % TODO: bug on Key ?
-			     {result, #pubsub_state{affiliation=owner}} -> true;
-			     _ -> false
-			 end
-		 end,
-    case get_state(Host, Node, Subscriber) of
-	{error, ?ERR_ITEM_NOT_FOUND} ->
-	    %% Requesting entity is not a subscriber
+    SubscriberKey = jlib:jid_tolower(jlib:jid_remove_resource(Subscriber)),
+    Authorized = (jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == SubscriberKey),
+    State = get_state(Host, Node, SubscriberKey),
+    if
+	%% Entity did not specify SubID
+	%%SubID == "", ?? ->
+	%%	{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+	%% Invalid subscription identifier
+	%%InvalidSubID ->
+	%%	{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+	%% Requesting entity is not a subscriber
+	State#pubsub_state.subscription == none ->
 	    {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")};
-	{result, State} ->
-	    if
-		%% Entity did not specify SubID
-		%%SubID == "", ?? ->
-		%%	{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-		%% Invalid subscription identifier
-		%%InvalidSubID ->
-		%%	{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-		%% Requesting entity is not a subscriber
-		State#pubsub_state.subscription == none ->
-		    {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")};
-		%% Requesting entity is prohibited from unsubscribing entity
-		not Authorized ->
-		    {error, ?ERR_FORBIDDEN};
-		%% Was just subscriber, remove the record
-		State#pubsub_state.affiliation == none ->
-		    mnesia:delete({pubsub_state, State#pubsub_state.stateid}),
-		    {result, default};
-		true ->
-		    set_state(State#pubsub_state{subscription = none}),
-		    {result, default}
-	    end
+	%% Requesting entity is prohibited from unsubscribing entity
+	(not Authorized) and (State#pubsub_state.affiliation =/= owner) ->
+	    {error, ?ERR_FORBIDDEN};
+	%% Was just subscriber, remove the record
+	State#pubsub_state.affiliation == none ->
+	    del_state(State#pubsub_state.stateid),
+	    {result, default};
+	true ->
+	    set_state(State#pubsub_state{subscription = none}),
+	    {result, default}
     end.
 
 %% @spec (Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
@@ -432,10 +408,7 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) ->
 %% <p>In the default plugin module, the record is unchanged.</p>
 publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
     PublisherKey = jlib:jid_tolower(jlib:jid_remove_resource(Publisher)),
-    State = case get_state(Host, Node, PublisherKey) of
-		{error, ?ERR_ITEM_NOT_FOUND} -> #pubsub_state{stateid={PublisherKey, {Host, Node}}};
-		{result, S} -> S
-	    end,
+    State = get_state(Host, Node, PublisherKey),
     #pubsub_state{affiliation = Affiliation,
 		  subscription = Subscription} = State,
     if
@@ -447,17 +420,17 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
 	    %% Entity does not have sufficient privileges to publish to node
 	    {error, ?ERR_FORBIDDEN};
 	true ->
-	    PubId = {PublisherKey, now()},
+	    PubId = {PublisherKey, now()}, %% TODO, uses {now(),PublisherKey} for sorting (EJAB-824)
 	    %% TODO: check creation, presence, roster (EJAB-663)
 	    Item = case get_item(Host, Node, ItemId) of
-		       {error, ?ERR_ITEM_NOT_FOUND} ->
+		       {result, OldItem} ->
+			   OldItem#pubsub_item{modification = PubId,
+					       payload = Payload};
+		       _ ->
 			   #pubsub_item{itemid = {ItemId, {Host, Node}},
 					creation = PubId,
 					modification = PubId,
-					payload = Payload};
-		       {result, OldItem} ->
-			   OldItem#pubsub_item{modification = PubId,
-					       payload = Payload}
+					payload = Payload}
 		   end,
 	    Items = [ItemId | State#pubsub_state.items--[ItemId]],
 	    {result, {NI, OI}} = remove_extra_items(
@@ -491,9 +464,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) ->
     NewItems = lists:sublist(ItemIds, MaxItems),
     OldItems = lists:nthtail(length(NewItems), ItemIds),
     %% Remove extra items:
-    lists:foreach(fun(ItemId) ->
-			  mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})
-		  end, OldItems),
+    del_items(Host, Node, OldItems),
     %% Return the new items list:
     {result, {NewItems, OldItems}}.
 
@@ -509,12 +480,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) ->
 %% or a publisher.</p>
 delete_item(Host, Node, Publisher, ItemId) ->
     PublisherKey = jlib:jid_tolower(jlib:jid_remove_resource(Publisher)),
-    State = case get_state(Host, Node, PublisherKey) of
-		{error, ?ERR_ITEM_NOT_FOUND} ->
-		    #pubsub_state{stateid = {PublisherKey, {Host, Node}}};
-		{result, S} ->
-		    S
-	    end,
+    State = get_state(Host, Node, PublisherKey),
     #pubsub_state{affiliation = Affiliation, items = Items} = State,
     Allowed = (Affiliation == publisher) orelse (Affiliation == owner)
 	orelse case get_item(Host, Node, ItemId) of
@@ -528,7 +494,7 @@ delete_item(Host, Node, Publisher, ItemId) ->
 	true ->
 	    case get_item(Host, Node, ItemId) of
 		{result, _} ->
-		    mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}),
+		    del_item(Host, Node, ItemId),
 		    NewItems = lists:delete(ItemId, Items),
 		    set_state(State#pubsub_state{items = NewItems}),
 		    {result, {default, broadcast}};
@@ -547,16 +513,12 @@ delete_item(Host, Node, Publisher, ItemId) ->
 purge_node(Host, Node, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
     case get_state(Host, Node, OwnerKey) of
-	{result, #pubsub_state{items = Items, affiliation = owner}} ->
-	    lists:foreach(fun(ItemId) ->
-				  mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})
-			  end, Items),
+	#pubsub_state{items = Items, affiliation = owner} ->
+	    del_items(Host, Node, Items),
 	    {result, {default, broadcast}};
-	{result, _} ->
-	    %% Entity is not owner
-	    {error, ?ERR_FORBIDDEN};
 	_ ->
-	    {error, ?ERR_ITEM_NOT_FOUND}
+	    %% Entity is not owner
+	    {error, ?ERR_FORBIDDEN}
     end.
 
 %% @spec (Host, JID) -> [{Node,Affiliation}]
@@ -572,8 +534,7 @@ purge_node(Host, Node, Owner) ->
 get_entity_affiliations(Host, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
     States = mnesia:match_object(
-	       #pubsub_state{stateid = {OwnerKey, {Host, '_'}},
-			     _ = '_'}),
+	       #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}),
     Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) ->
 		 {N, A}
 	 end,
@@ -581,8 +542,7 @@ get_entity_affiliations(Host, Owner) ->
 
 get_node_affiliations(Host, Node) ->
     States = mnesia:match_object(
-	       #pubsub_state{stateid = {'_', {Host, Node}},
-			     _ = '_'}),
+	       #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}),
     Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) ->
 		 {J, A}
 	 end,
@@ -590,22 +550,13 @@ get_node_affiliations(Host, Node) ->
 
 get_affiliation(Host, Node, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Affiliation = case get_state(Host, Node, OwnerKey) of
-		      {result, #pubsub_state{affiliation = A}} -> A;
-		      _ -> none
-		  end,
-    {result, Affiliation}.
+    State = get_state(Host, Node, OwnerKey),
+    {result, State#pubsub_state.affiliation}.
 
 set_affiliation(Host, Node, Owner, Affiliation) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Record = case get_state(Host, Node, OwnerKey) of
-		 {error, ?ERR_ITEM_NOT_FOUND} ->
-		     #pubsub_state{stateid = {OwnerKey, {Host, Node}},
-				   affiliation = Affiliation};
-		 {result, State} ->
-		     State#pubsub_state{affiliation = Affiliation}
-	     end,
-    set_state(Record),
+    State = get_state(Host, Node, OwnerKey),
+    set_state(State#pubsub_state{affiliation = Affiliation}),
     ok.
 
 %% @spec (Host, Owner) -> [{Node,Subscription}]
@@ -622,8 +573,7 @@ set_affiliation(Host, Node, Owner, Affiliation) ->
 get_entity_subscriptions(Host, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
     States = mnesia:match_object(
-	       #pubsub_state{stateid = {OwnerKey, {Host, '_'}},
-			     _ = '_'}),
+	       #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}),
     Tr = fun(#pubsub_state{stateid = {_, {_, N}}, subscription = S}) ->
 		 {N, S}
 	 end,
@@ -631,8 +581,7 @@ get_entity_subscriptions(Host, Owner) ->
 
 get_node_subscriptions(Host, Node) ->
     States = mnesia:match_object(
-	       #pubsub_state{stateid = {'_', {Host, Node}},
-			     _ = '_'}),
+	       #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}),
     Tr = fun(#pubsub_state{stateid = {J, {_, _}}, subscription = S}) ->
 		 {J, S}
 	 end,
@@ -640,22 +589,13 @@ get_node_subscriptions(Host, Node) ->
 
 get_subscription(Host, Node, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Subscription = case get_state(Host, Node, OwnerKey) of
-		       {result, #pubsub_state{subscription = S}} -> S;
-		       _ -> none
-		   end,
-    {result, Subscription}.
+    State = get_state(Host, Node, OwnerKey),
+    {result, State#pubsub_state.subscription}.
 
 set_subscription(Host, Node, Owner, Subscription) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Record = case get_state(Host, Node, OwnerKey) of
-		 {error, ?ERR_ITEM_NOT_FOUND} ->
-		     #pubsub_state{stateid = {OwnerKey, {Host, Node}},
-				   subscription = Subscription};
-		 {result, State} ->
-		     State#pubsub_state{subscription = Subscription}
-	     end,
-    set_state(Record),
+    State = get_state(Host, Node, OwnerKey),
+    set_state(State#pubsub_state{subscription = Subscription}),
     ok.
 
 %% @spec (Host, Node) -> [States] | []
@@ -684,11 +624,10 @@ get_states(Host, Node) ->
 %%	 State = mod_pubsub:pubsubItems()
 %% @doc <p>Returns a state (one state list), given its reference.</p>
 get_state(Host, Node, JID) ->
-    case mnesia:read({pubsub_state, {JID, {Host, Node}}}) of
-	[State] when is_record(State, pubsub_state) ->
-	    {result, State};
-	_ ->
-	    {error, ?ERR_ITEM_NOT_FOUND}
+    StateId = {JID, {Host, Node}},
+    case mnesia:read({pubsub_state, StateId}) of
+	[State] when is_record(State, pubsub_state) -> State;
+	_ -> #pubsub_state{stateid=StateId}
     end.
 
 %% @spec (State) -> ok | {error, Reason::stanzaError()}
@@ -699,6 +638,12 @@ set_state(State) when is_record(State, pubsub_state) ->
 set_state(_) ->
     {error, ?ERR_INTERNAL_SERVER_ERROR}.
 
+%% @spec (StateId) -> ok | {error, Reason::stanzaError()}
+%%	 StateId = mod_pubsub:pubsubStateId()
+%% @doc <p>Delete a state from database.</p>
+del_state(StateId) ->
+    mnesia:delete({pubsub_state, StateId}).
+
 %% @spec (Host, Node) -> [Items] | []
 %%	 Host = mod_pubsub:host()
 %%	 Node = mod_pubsub:pubsubNode()
@@ -718,11 +663,9 @@ get_items(Host, Node, _From) ->
 	      #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}),
     {result, Items}.
 get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
-    {Affiliation, Subscription} = 
-	case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of
-	{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; 
-	_ -> {none, none}
-	end, 
+    State = get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))),
+    #pubsub_state{affiliation = Affiliation,
+		  subscription = Subscription} = State,
     Subscribed = not ((Subscription == none) or (Subscription == pending)),
     if
 	%%SubID == "", ?? ->
@@ -770,11 +713,9 @@ get_item(Host, Node, ItemId) ->
 	    {error, ?ERR_ITEM_NOT_FOUND}
     end.
 get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
-    {Affiliation, Subscription} = 
-	case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of
-	{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; 
-	_ -> {none, none}
-	end, 
+    State = get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))),
+    #pubsub_state{affiliation = Affiliation,
+		  subscription = Subscription} = State,
     Subscribed = not ((Subscription == none) or (Subscription == pending)),
     if
 	%%SubID == "", ?? ->
@@ -816,6 +757,18 @@ set_item(Item) when is_record(Item, pubsub_item) ->
 set_item(_) ->
     {error, ?ERR_INTERNAL_SERVER_ERROR}.
 
+%% @spec (ItemId) -> ok | {error, Reason::stanzaError()}
+%%	 Host = mod_pubsub:host()
+%%	 Node = mod_pubsub:pubsubNode()
+%%	 ItemId = string()
+%% @doc <p>Delete an item from database.</p>
+del_item(Host, Node, ItemId) ->
+    mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}).
+del_items(Host, Node, ItemIds) ->
+    lists:foreach(fun(ItemId) ->
+	del_item(Host, Node, ItemId)
+    end, ItemIds).
+
 %% @doc <p>Return the name of the node if known: Default is to return
 %% node id.</p>
 get_item_name(_Host, _Node, Id) ->
diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl
index 1c1bff7d7..ec9e6c7e2 100644
--- a/src/mod_pubsub/node_pep.erl
+++ b/src/mod_pubsub/node_pep.erl
@@ -180,15 +180,9 @@ get_affiliation(_Host, Node, Owner) ->
 
 set_affiliation(_Host, Node, Owner, Affiliation) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Record = case get_state(OwnerKey, Node, OwnerKey) of
-	    {error, ?ERR_ITEM_NOT_FOUND} ->
-		#pubsub_state{stateid = {OwnerKey, {OwnerKey, Node}},
-			      affiliation = Affiliation};
-	    {result, State} ->
-		State#pubsub_state{affiliation = Affiliation}
-    end,    
-    set_state(Record),
-    ok.
+    State = get_state(OwnerKey, Node, OwnerKey),
+    set_state(State#pubsub_state{affiliation = Affiliation}),
+    ok. 
 
 get_entity_subscriptions(_Host, _Owner) ->
     {result, []}.
diff --git a/src/mod_pubsub/pubsub.hrl b/src/mod_pubsub/pubsub.hrl
index b4ceed4fa..61ed88dbe 100644
--- a/src/mod_pubsub/pubsub.hrl
+++ b/src/mod_pubsub/pubsub.hrl
@@ -72,7 +72,7 @@
 %%%    lserver = string(),
 %%%    lresource = string()}.
 
-%%% @type usr() = {User::string(), Server::string(), Resource::string()}.
+%%% @type ljid() = {User::string(), Server::string(), Resource::string()}.
 
 %%% @type affiliation() = none | owner | publisher | outcast.
 %%% @type subscription() = none | pending | unconfigured | subscribed.
@@ -81,7 +81,7 @@
 %%%    nodeid = {Host::host(), Node::pubsubNode()},
 %%%    parentid = {Host::host(), Node::pubsubNode()},
 %%%    type = nodeType(),
-%%%    owners = [usr()],
+%%%    owners = [ljid()],
 %%%    options = [nodeOption()]}.
 %%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
 %%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
@@ -94,7 +94,7 @@
 		     }).
 
 %%% @type pubsubState() = #pubsub_state{
-%%%    stateid = {jid(), {Host::host(), Node::pubsubNode()}},
+%%%    stateid = {ljid(), {Host::host(), Node::pubsubNode()}},
 %%%    items = [ItemId::string()],
 %%%    affiliation = affiliation(),
 %%%    subscription = subscription()}.
@@ -108,8 +108,8 @@
 
 %% @type pubsubItem() = #pubsub_item{
 %%        itemid = {ItemId::string(), {Host::host(),Node::pubsubNode()}},
-%%     creation = {JID::jid(), now()},
-%%     modification = {JID::jid(), now()},
+%%     creation = {ljid(), now()},
+%%     modification = {ljid(), now()},
 %%     payload = XMLContent::string()}.
 %%% <p>This is the format of the <tt>published items</tt> table. The type of the
 %%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
-- 
2.40.0