diff options
Diffstat (limited to 'src/mod_pubsub/pubsub_odbc.patch')
-rw-r--r-- | src/mod_pubsub/pubsub_odbc.patch | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/src/mod_pubsub/pubsub_odbc.patch b/src/mod_pubsub/pubsub_odbc.patch new file mode 100644 index 000000000..a23147bc5 --- /dev/null +++ b/src/mod_pubsub/pubsub_odbc.patch @@ -0,0 +1,761 @@ +--- mod_pubsub.erl 2009-10-20 16:33:47.000000000 +0200 ++++ mod_pubsub_odbc.erl 2009-10-20 16:33:26.000000000 +0200 +@@ -42,7 +42,7 @@ + %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see + %%% XEP-0060 section 12.18. + +--module(mod_pubsub). ++-module(mod_pubsub_odbc). + -author('christophe.romain@process-one.net'). + -version('1.13-0'). + +@@ -54,9 +54,9 @@ + -include("jlib.hrl"). + -include("pubsub.hrl"). + +--define(STDTREE, "tree"). +--define(STDNODE, "flat"). +--define(PEPNODE, "pep"). ++-define(STDTREE, "tree_odbc"). ++-define(STDNODE, "flat_odbc"). ++-define(PEPNODE, "pep_odbc"). + + %% exports for hooks + -export([presence_probe/3, +@@ -102,7 +102,7 @@ + string_to_affiliation/1, + extended_error/2, + extended_error/3, +- rename_default_nodeplugin/0 ++ escape/1 + ]). + + %% API and gen_server callbacks +@@ -121,7 +121,7 @@ + -export([send_loop/1 + ]). + +--define(PROCNAME, ejabberd_mod_pubsub). ++-define(PROCNAME, ejabberd_mod_pubsub_odbc). + -define(PLUGIN_PREFIX, "node_"). + -define(TREE_PREFIX, "nodetree_"). + +@@ -215,8 +215,6 @@ + ok + end, + ejabberd_router:register_route(Host), +- update_node_database(Host, ServerHost), +- update_state_database(Host, ServerHost), + init_nodes(Host, ServerHost, NodeTree, Plugins), + State = #state{host = Host, + server_host = ServerHost, +@@ -269,207 +267,14 @@ + + init_nodes(Host, ServerHost, _NodeTree, Plugins) -> + %% TODO, this call should be done plugin side +- case lists:member("hometree", Plugins) of ++ case lists:member("hometree_odbc", Plugins) of + true -> +- create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree"), +- create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree"); ++ create_node(Host, ServerHost, string_to_node("/home"), service_jid(Host), "hometree_odbc"), ++ create_node(Host, ServerHost, string_to_node("/home/"++ServerHost), service_jid(Host), "hometree_odbc"); + false -> + ok + end. + +-update_node_database(Host, ServerHost) -> +- mnesia:del_table_index(pubsub_node, type), +- mnesia:del_table_index(pubsub_node, parentid), +- case catch mnesia:table_info(pubsub_node, attributes) of +- [host_node, host_parent, info] -> +- ?INFO_MSG("upgrade node pubsub tables",[]), +- F = fun() -> +- {Result, LastIdx} = lists:foldl( +- fun({pubsub_node, NodeId, ParentId, {nodeinfo, Items, Options, Entities}}, {RecList, NodeIdx}) -> +- ItemsList = +- lists:foldl( +- fun({item, IID, Publisher, Payload}, Acc) -> +- C = {unknown, Publisher}, +- M = {now(), Publisher}, +- mnesia:write( +- #pubsub_item{itemid = {IID, NodeIdx}, +- creation = C, +- modification = M, +- payload = Payload}), +- [{Publisher, IID} | Acc] +- end, [], Items), +- Owners = +- dict:fold( +- fun(JID, {entity, Aff, Sub}, Acc) -> +- UsrItems = +- lists:foldl( +- fun({P, I}, IAcc) -> +- case P of +- JID -> [I | IAcc]; +- _ -> IAcc +- end +- end, [], ItemsList), +- mnesia:write({pubsub_state, +- {JID, NodeIdx}, +- UsrItems, +- Aff, +- Sub}), +- case Aff of +- owner -> [JID | Acc]; +- _ -> Acc +- end +- end, [], Entities), +- mnesia:delete({pubsub_node, NodeId}), +- {[#pubsub_node{nodeid = NodeId, +- id = NodeIdx, +- parents = [element(2, ParentId)], +- owners = Owners, +- options = Options} | +- RecList], NodeIdx + 1} +- end, {[], 1}, +- mnesia:match_object( +- {pubsub_node, {Host, '_'}, '_', '_'})), +- mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []}), +- Result +- end, +- {atomic, NewRecords} = mnesia:transaction(F), +- {atomic, ok} = mnesia:delete_table(pubsub_node), +- {atomic, ok} = mnesia:create_table(pubsub_node, +- [{disc_copies, [node()]}, +- {attributes, record_info(fields, pubsub_node)}]), +- FNew = fun() -> lists:foreach(fun(Record) -> +- mnesia:write(Record) +- end, NewRecords) +- end, +- case mnesia:transaction(FNew) of +- {atomic, Result} -> +- ?INFO_MSG("Pubsub node tables updated correctly: ~p", [Result]); +- {aborted, Reason} -> +- ?ERROR_MSG("Problem updating Pubsub node tables:~n~p", [Reason]) +- end; +- [nodeid, parentid, type, owners, options] -> +- F = fun({pubsub_node, NodeId, {_, Parent}, Type, Owners, Options}) -> +- #pubsub_node{ +- nodeid = NodeId, +- id = 0, +- parents = [Parent], +- type = Type, +- owners = Owners, +- options = Options} +- end, +- mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]), +- FNew = fun() -> +- LastIdx = lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) -> +- mnesia:write(PubsubNode#pubsub_node{id = NodeIdx}), +- lists:foreach(fun(#pubsub_state{stateid = StateId} = State) -> +- {JID, _} = StateId, +- mnesia:delete({pubsub_state, StateId}), +- mnesia:write(State#pubsub_state{stateid = {JID, NodeIdx}}) +- end, mnesia:match_object(#pubsub_state{stateid = {'_', NodeId}, _ = '_'})), +- lists:foreach(fun(#pubsub_item{itemid = ItemId} = Item) -> +- {IID, _} = ItemId, +- {M1, M2} = Item#pubsub_item.modification, +- {C1, C2} = Item#pubsub_item.creation, +- mnesia:delete({pubsub_item, ItemId}), +- mnesia:write(Item#pubsub_item{itemid = {IID, NodeIdx}, +- modification = {M2, M1}, +- creation = {C2, C1}}) +- end, mnesia:match_object(#pubsub_item{itemid = {'_', NodeId}, _ = '_'})), +- NodeIdx + 1 +- end, 1, mnesia:match_object( +- {pubsub_node, {Host, '_'}, '_', '_', '_', '_', '_'}) +- ++ mnesia:match_object( +- {pubsub_node, {{'_', ServerHost, '_'}, '_'}, '_', '_', '_', '_', '_'})), +- mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []}) +- end, +- case mnesia:transaction(FNew) of +- {atomic, Result} -> +- rename_default_nodeplugin(), +- ?INFO_MSG("Pubsub node tables updated correctly: ~p", [Result]); +- {aborted, Reason} -> +- ?ERROR_MSG("Problem updating Pubsub node tables:~n~p", [Reason]) +- end; +- [nodeid, id, parent, type, owners, options] -> +- F = fun({pubsub_node, NodeId, Id, Parent, Type, Owners, Options}) -> +- #pubsub_node{ +- nodeid = NodeId, +- id = Id, +- parents = [Parent], +- type = Type, +- owners = Owners, +- options = Options} +- end, +- mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]), +- rename_default_nodeplugin(); +- _ -> +- ok +- end, +- mnesia:transaction(fun() -> +- case catch mnesia:first(pubsub_node) of +- {_, L} when is_list(L) -> +- lists:foreach( +- fun({H, N}) when is_list(N) -> +- [Node] = mnesia:read({pubsub_node, {H, N}}), +- Type = Node#pubsub_node.type, +- BN = element(2, node_call(Type, path_to_node, [N])), +- BP = case [element(2, node_call(Type, path_to_node, [P])) || P <- Node#pubsub_node.parents] of +- [<<>>] -> []; +- Parents -> Parents +- end, +- mnesia:write(Node#pubsub_node{nodeid={H, BN}, parents=BP}), +- mnesia:delete({pubsub_node, {H, N}}); +- (_) -> +- ok +- end, mnesia:all_keys(pubsub_node)); +- _ -> +- ok +- end +- end). +- +-rename_default_nodeplugin() -> +- lists:foreach(fun(Node) -> +- mnesia:dirty_write(Node#pubsub_node{type = "hometree"}) +- end, mnesia:dirty_match_object(#pubsub_node{type = "default", _ = '_'})). +- +-update_state_database(_Host, _ServerHost) -> +- case catch mnesia:table_info(pubsub_state, attributes) of +- [stateid, items, affiliation, subscription] -> +- ?INFO_MSG("upgrade state pubsub tables", []), +- F = fun ({pubsub_state, {JID, NodeID}, Items, Aff, Sub}, Acc) -> +- Subs = case Sub of +- none -> +- []; +- _ -> +- {result, SubID} = pubsub_subscription:subscribe_node(JID, NodeID, []), +- [{Sub, SubID}] +- end, +- NewState = #pubsub_state{stateid = {JID, NodeID}, +- items = Items, +- affiliation = Aff, +- subscriptions = Subs}, +- [NewState | Acc] +- end, +- {atomic, NewRecs} = mnesia:transaction(fun mnesia:foldl/3, +- [F, [], pubsub_state]), +- {atomic, ok} = mnesia:delete_table(pubsub_state), +- {atomic, ok} = mnesia:create_table(pubsub_state, +- [{disc_copies, [node()]}, +- {attributes, record_info(fields, pubsub_state)}]), +- FNew = fun () -> +- lists:foreach(fun mnesia:write/1, NewRecs) +- end, +- case mnesia:transaction(FNew) of +- {atomic, Result} -> +- ?INFO_MSG("Pubsub state tables updated correctly: ~p", +- [Result]); +- {aborted, Reason} -> +- ?ERROR_MSG("Problem updating Pubsub state tables:~n~p", +- [Reason]) +- end; +- _ -> +- ok +- end. +- + send_queue(State, Msg) -> + Pid = State#state.send_loop, + case is_process_alive(Pid) of +@@ -492,17 +297,15 @@ + %% for each node From is subscribed to + %% and if the node is so configured, send the last published item to From + lists:foreach(fun(PType) -> +- {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, JID]), ++ Subscriptions = case catch node_action(Host, PType, get_entity_subscriptions_for_send_last, [Host, JID]) of ++ {result, S} -> S; ++ _ -> [] ++ end, + lists:foreach( + fun({Node, subscribed, _, SubJID}) -> + if (SubJID == LJID) or (SubJID == BJID) -> +- #pubsub_node{nodeid = {H, N}, type = Type, id = NodeId, options = Options} = Node, +- case get_option(Options, send_last_published_item) of +- on_sub_and_presence -> +- send_items(H, N, NodeId, Type, LJID, last); +- _ -> +- ok +- end; ++ #pubsub_node{nodeid = {H, N}, type = Type, id = NodeId} = Node, ++ send_items(H, N, NodeId, Type, LJID, last); + true -> + % resource not concerned about that subscription + ok +@@ -825,10 +628,10 @@ + {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Subscriber]), + lists:foreach(fun + ({Node, subscribed, _, JID}) -> +- #pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId} = Node, ++ #pubsub_node{options = Options, type = Type, id = NodeId} = Node, + case get_option(Options, access_model) of + presence -> +- case lists:member(BJID, Owners) of ++ case lists:member(BJID, node_owners(Host, Type, NodeId)) of + true -> + node_action(Host, Type, unsubscribe_node, [NodeId, Subscriber, JID, all]); + false -> +@@ -943,7 +746,8 @@ + sub_el = SubEl} = IQ -> + {xmlelement, _, QAttrs, _} = SubEl, + Node = xml:get_attr_s("node", QAttrs), +- Res = case iq_disco_items(Host, Node, From) of ++ Rsm = jlib:rsm_decode(IQ), ++ Res = case iq_disco_items(Host, Node, From, Rsm) of + {result, IQRes} -> + jlib:iq_to_xml( + IQ#iq{type = result, +@@ -1048,7 +852,7 @@ + [] -> + ["leaf"]; %% No sub-nodes: it's a leaf node + _ -> +- case node_call(Type, get_items, [NodeId, From]) of ++ case node_call(Type, get_items, [NodeId, From, none]) of + {result, []} -> ["collection"]; + {result, _} -> ["leaf", "collection"]; + _ -> [] +@@ -1064,8 +868,9 @@ + []; + true -> + [{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []} | +- lists:map(fun(T) -> +- {xmlelement, "feature", [{"var", ?NS_PUBSUB++"#"++T}], []} ++ lists:map(fun ++ ("rsm")-> {xmlelement, "feature", [{"var", ?NS_RSM}], []}; ++ (T) -> {xmlelement, "feature", [{"var", ?NS_PUBSUB++"#"++T}], []} + end, features(Type))] + end, + %% TODO: add meta-data info (spec section 5.4) +@@ -1093,21 +898,22 @@ + {xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []}, + {xmlelement, "feature", [{"var", ?NS_PUBSUB}], []}, + {xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++ +- lists:map(fun(Feature) -> +- {xmlelement, "feature", [{"var", ?NS_PUBSUB++"#"++Feature}], []} ++ lists:map(fun ++ ("rsm")-> {xmlelement, "feature", [{"var", ?NS_RSM}], []}; ++ (T) -> {xmlelement, "feature", [{"var", ?NS_PUBSUB++"#"++T}], []} + end, features(Host, Node))}; + _ -> + node_disco_info(Host, Node, From) + end. + +-iq_disco_items(Host, [], From) -> ++iq_disco_items(Host, [], From, _RSM) -> + {result, lists:map( + fun(#pubsub_node{nodeid = {_, SubNode}, type = Type}) -> + {result, Path} = node_call(Type, node_to_path, [SubNode]), + [Name|_] = lists:reverse(Path), + {xmlelement, "item", [{"jid", Host}, {"name", Name}|nodeAttr(SubNode)], []} + end, tree_action(Host, get_subnodes, [Host, <<>>, From]))}; +-iq_disco_items(Host, Item, From) -> ++iq_disco_items(Host, Item, From, RSM) -> + case string:tokens(Item, "!") of + [_SNode, _ItemID] -> + {result, []}; +@@ -1115,10 +921,10 @@ + Node = string_to_node(SNode), + Action = + fun(#pubsub_node{type = Type, id = NodeId}) -> +- % TODO call get_items/6 instead for access control (EJAB-1033) +- NodeItems = case node_call(Type, get_items, [NodeId, From]) of ++ %% TODO call get_items/6 instead for access control (EJAB-1033) ++ {NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of + {result, I} -> I; +- _ -> [] ++ _ -> {[], none} + end, + Nodes = lists:map( + fun(#pubsub_node{nodeid = {_, SubNode}}) -> +@@ -1129,7 +935,7 @@ + {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]), + {xmlelement, "item", [{"jid", Host}, {"name", Name}], []} + end, NodeItems), +- {result, Nodes ++ Items} ++ {result, Nodes ++ Items ++ jlib:rsm_encode(RsmOut)} + end, + case transaction(Host, Node, Action, sync_dirty) of + {result, {_, Result}} -> {result, Result}; +@@ -1258,7 +1064,8 @@ + (_, Acc) -> + Acc + end, [], xml:remove_cdata(Els)), +- get_items(Host, Node, From, SubId, MaxItems, ItemIDs); ++ RSM = jlib:rsm_decode(SubEl), ++ get_items(Host, Node, From, SubId, MaxItems, ItemIDs, RSM); + {get, "subscriptions"} -> + get_subscriptions(Host, Node, From, Plugins); + {get, "affiliations"} -> +@@ -1281,7 +1088,9 @@ + + iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) -> + {xmlelement, _, _, SubEls} = SubEl, +- Action = xml:remove_cdata(SubEls), ++ Action = lists:filter(fun({xmlelement, "set", _, _}) -> false; ++ (_) -> true ++ end, xml:remove_cdata(SubEls)), + case Action of + [{xmlelement, Name, Attrs, Els}] -> + Node = string_to_node(xml:get_attr_s("node", Attrs)), +@@ -1404,7 +1213,8 @@ + _ -> [] + end + end, +- case transaction(fun () -> {result, lists:flatmap(Tr, Plugins)} end, ++ case transaction(Host, ++ fun () -> {result, lists:flatmap(Tr, Plugins)} end, + sync_dirty) of + {result, Res} -> Res; + Err -> Err +@@ -1444,7 +1254,7 @@ + + %%% authorization handling + +-send_authorization_request(#pubsub_node{owners = Owners, nodeid = {Host, Node}}, Subscriber) -> ++send_authorization_request(#pubsub_node{nodeid = {Host, Node}, type = Type, id = NodeId}, Subscriber) -> + Lang = "en", %% TODO fix + Stanza = {xmlelement, "message", + [], +@@ -1473,7 +1283,7 @@ + [{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]}, + lists:foreach(fun(Owner) -> + ejabberd_router ! {route, service_jid(Host), jlib:make_jid(Owner), Stanza} +- end, Owners). ++ end, node_owners(Host, Type, NodeId)). + + find_authorization_response(Packet) -> + {xmlelement, _Name, _Attrs, Els} = Packet, +@@ -1537,8 +1347,8 @@ + "true" -> true; + _ -> false + end, +- Action = fun(#pubsub_node{type = Type, owners = Owners, id = NodeId}) -> +- IsApprover = lists:member(jlib:jid_tolower(jlib:jid_remove_resource(From)), Owners), ++ Action = fun(#pubsub_node{type = Type, id = NodeId}) -> ++ IsApprover = lists:member(jlib:jid_tolower(jlib:jid_remove_resource(From)), node_owners_call(Type, NodeId)), + {result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]), + if + not IsApprover -> +@@ -1729,7 +1539,7 @@ + Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], + [{xmlelement, "create", nodeAttr(Node), + []}]}], +- case transaction(CreateNode, transaction) of ++ case transaction(Host, CreateNode, transaction) of + {result, {Result, broadcast}} -> + %%Lang = "en", %% TODO: fix + %%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), +@@ -1837,7 +1647,7 @@ + %%<li>The node does not exist.</li> + %%</ul> + subscribe_node(Host, Node, From, JID, Configuration) -> +- SubOpts = case pubsub_subscription:parse_options_xform(Configuration) of ++ SubOpts = case pubsub_subscription_odbc:parse_options_xform(Configuration) of + {result, GoodSubOpts} -> GoodSubOpts; + _ -> invalid + end, +@@ -1845,7 +1655,7 @@ + error -> {"", "", ""}; + J -> jlib:jid_tolower(J) + end, +- Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) -> ++ Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> + Features = features(Type), + SubscribeFeature = lists:member("subscribe", Features), + OptionsFeature = lists:member("subscription-options", Features), +@@ -1864,9 +1674,13 @@ + {"", "", ""} -> + {false, false}; + _ -> +- {OU, OS, _} = Owner, +- get_roster_info(OU, OS, +- Subscriber, AllowedGroups) ++ case node_owners_call(Type, NodeId) of ++ [{OU, OS, _}|_] -> ++ get_roster_info(OU, OS, ++ Subscriber, AllowedGroups); ++ _ -> ++ {false, false} ++ end + end + end, + if +@@ -2197,7 +2011,7 @@ + %% <p>The permission are not checked in this function.</p> + %% @todo We probably need to check that the user doing the query has the right + %% to read the items. +-get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) -> ++get_items(Host, Node, From, SubId, SMaxItems, ItemIDs, RSM) -> + MaxItems = + if + SMaxItems == "" -> get_max_items_node(Host); +@@ -2236,11 +2050,11 @@ + node_call(Type, get_items, + [NodeId, From, + AccessModel, PresenceSubscription, RosterGroup, +- SubId]) ++ SubId, RSM]) + end + end, + case transaction(Host, Node, Action, sync_dirty) of +- {result, {_, Items}} -> ++ {result, {_, {Items, RSMOut}}} -> + SendItems = case ItemIDs of + [] -> + Items; +@@ -2253,7 +2067,8 @@ + %% number of items sent to MaxItems: + {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], + [{xmlelement, "items", nodeAttr(Node), +- itemsEls(lists:sublist(SendItems, MaxItems))}]}]}; ++ itemsEls(lists:sublist(SendItems, MaxItems))} ++ | jlib:rsm_encode(RSMOut)]}]}; + Error -> + Error + end +@@ -2285,16 +2100,27 @@ + %% @doc <p>Resend the items of a node to the user.</p> + %% @todo use cache-last-item feature + send_items(Host, Node, NodeId, Type, LJID, last) -> +- case get_cached_item(Host, NodeId) of ++ Stanza = case get_cached_item(Host, NodeId) of + undefined -> +- send_items(Host, Node, NodeId, Type, LJID, 1); ++ % special ODBC optimization, works only with node_hometree_odbc, node_flat_odbc and node_pep_odbc ++ case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of ++ {result, [LastItem]} -> ++ {ModifNow, ModifLjid} = LastItem#pubsub_item.modification, ++ event_stanza_with_delay( ++ [{xmlelement, "items", nodeAttr(Node), ++ itemsEls([LastItem])}], ModifNow, ModifLjid); ++ _ -> ++ event_stanza( ++ [{xmlelement, "items", nodeAttr(Node), ++ itemsEls([])}]) ++ end; + LastItem -> + {ModifNow, ModifLjid} = LastItem#pubsub_item.modification, +- Stanza = event_stanza_with_delay( ++ event_stanza_with_delay( + [{xmlelement, "items", nodeAttr(Node), +- itemsEls([LastItem])}], ModifNow, ModifLjid), +- ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza} +- end; ++ itemsEls([LastItem])}], ModifNow, ModifLjid) ++ end, ++ ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza}; + send_items(Host, Node, NodeId, Type, LJID, Number) -> + ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of + {result, []} -> +@@ -2420,29 +2246,12 @@ + error -> + {error, ?ERR_BAD_REQUEST}; + _ -> +- Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) -> +- case lists:member(Owner, Owners) of ++ Action = fun(#pubsub_node{type = Type, id = NodeId}) -> ++ case lists:member(Owner, node_owners_call(Type, NodeId)) of + true -> + lists:foreach( + fun({JID, Affiliation}) -> +- node_call(Type, set_affiliation, [NodeId, JID, Affiliation]), +- case Affiliation of +- owner -> +- NewOwner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), +- NewOwners = [NewOwner|Owners], +- tree_call(Host, set_node, [N#pubsub_node{owners = NewOwners}]); +- none -> +- OldOwner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), +- case lists:member(OldOwner, Owners) of +- true -> +- NewOwners = Owners--[OldOwner], +- tree_call(Host, set_node, [N#pubsub_node{owners = NewOwners}]); +- _ -> +- ok +- end; +- _ -> +- ok +- end ++ node_call(Type, set_affiliation, [NodeId, JID, Affiliation]) + end, Entities), + {result, []}; + _ -> +@@ -2495,11 +2304,11 @@ + end. + + read_sub(Subscriber, Node, NodeID, SubID, Lang) -> +- case pubsub_subscription:get_subscription(Subscriber, NodeID, SubID) of ++ case pubsub_subscription_odbc:get_subscription(Subscriber, NodeID, SubID) of + {error, notfound} -> + {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + {result, #pubsub_subscription{options = Options}} -> +- {result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options), ++ {result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options), + OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)}, + {"subid", SubID}|nodeAttr(Node)], + [XdataEl]}, +@@ -2525,7 +2334,7 @@ + end. + + set_options_helper(Configuration, JID, NodeID, SubID, Type) -> +- SubOpts = case pubsub_subscription:parse_options_xform(Configuration) of ++ SubOpts = case pubsub_subscription_odbc:parse_options_xform(Configuration) of + {result, GoodSubOpts} -> GoodSubOpts; + _ -> invalid + end, +@@ -2554,7 +2363,7 @@ + write_sub(_Subscriber, _NodeID, _SubID, invalid) -> + {error, extended_error(?ERR_BAD_REQUEST, "invalid-options")}; + write_sub(Subscriber, NodeID, SubID, Options) -> +- case pubsub_subscription:set_subscription(Subscriber, NodeID, SubID, Options) of ++ case pubsub_subscription_odbc:set_subscription(Subscriber, NodeID, SubID, Options) of + {error, notfound} -> + {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + {result, _} -> +@@ -2722,8 +2531,8 @@ + {"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]}, + ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza} + end, +- Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}) -> +- case lists:member(Owner, Owners) of ++ Action = fun(#pubsub_node{type = Type, id = NodeId}) -> ++ case lists:member(Owner, node_owners_call(Type, NodeId)) of + true -> + Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> + +@@ -3007,7 +2816,7 @@ + {Depth, [{N, get_node_subs(N)} || N <- Nodes]} + end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} + end, +- case transaction(Action, sync_dirty) of ++ case transaction(Host, Action, sync_dirty) of + {result, CollSubs} -> CollSubs; + _ -> [] + end. +@@ -3021,9 +2830,9 @@ + + get_options_for_subs(NodeID, Subs) -> + lists:foldl(fun({JID, subscribed, SubID}, Acc) -> +- case pubsub_subscription:read_subscription(JID, NodeID, SubID) of ++ case pubsub_subscription_odbc:get_subscription(JID, NodeID, SubID) of + {error, notfound} -> [{JID, SubID, []} | Acc]; +- #pubsub_subscription{options = Options} -> [{JID, SubID, Options} | Acc]; ++ {result, #pubsub_subscription{options = Options}} -> [{JID, SubID, Options} | Acc]; + _ -> Acc + end; + (_, Acc) -> +@@ -3221,6 +3030,30 @@ + Result + end. + ++%% @spec (Host, Type, NodeId) -> [ljid()] ++%% NodeId = pubsubNodeId() ++%% @doc <p>Return list of node owners.</p> ++node_owners(Host, Type, NodeId) -> ++ case node_action(Host, Type, get_node_affiliations, [NodeId]) of ++ {result, Affiliations} -> ++ lists:foldl( ++ fun({LJID, owner}, Acc) -> [LJID|Acc]; ++ (_, Acc) -> Acc ++ end, [], Affiliations); ++ _ -> ++ [] ++ end. ++node_owners_call(Type, NodeId) -> ++ case node_call(Type, get_node_affiliations, [NodeId]) of ++ {result, Affiliations} -> ++ lists:foldl( ++ fun({LJID, owner}, Acc) -> [LJID|Acc]; ++ (_, Acc) -> Acc ++ end, [], Affiliations); ++ _ -> ++ [] ++ end. ++ + %% @spec (Host, Options) -> MaxItems + %% Host = host() + %% Options = [Option] +@@ -3607,7 +3440,13 @@ + tree_action(Host, Function, Args) -> + ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), + Fun = fun() -> tree_call(Host, Function, Args) end, +- catch mnesia:sync_dirty(Fun). ++ case catch ejabberd_odbc:sql_bloc(odbc_conn(Host), Fun) of ++ {atomic, Result} -> ++ Result; ++ {aborted, Reason} -> ++ ?ERROR_MSG("transaction return internal error: ~p~n",[{aborted, Reason}]), ++ {error, ?ERR_INTERNAL_SERVER_ERROR} ++ end. + + %% @doc <p>node plugin call.</p> + node_call(Type, Function, Args) -> +@@ -3627,13 +3466,13 @@ + + node_action(Host, Type, Function, Args) -> + ?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]), +- transaction(fun() -> ++ transaction(Host, fun() -> + node_call(Type, Function, Args) + end, sync_dirty). + + %% @doc <p>plugin transaction handling.</p> + transaction(Host, Node, Action, Trans) -> +- transaction(fun() -> ++ transaction(Host, fun() -> + case tree_call(Host, get_node, [Host, Node]) of + N when is_record(N, pubsub_node) -> + case Action(N) of +@@ -3646,8 +3485,14 @@ + end + end, Trans). + +-transaction(Fun, Trans) -> +- case catch mnesia:Trans(Fun) of ++transaction(Host, Fun, Trans) -> ++ transaction_retry(Host, Fun, Trans, 2). ++transaction_retry(Host, Fun, Trans, Count) -> ++ SqlFun = case Trans of ++ transaction -> sql_transaction; ++ _ -> sql_bloc ++ end, ++ case catch ejabberd_odbc:SqlFun(odbc_conn(Host), Fun) of + {result, Result} -> {result, Result}; + {error, Error} -> {error, Error}; + {atomic, {result, Result}} -> {result, Result}; +@@ -3655,6 +3500,15 @@ + {aborted, Reason} -> + ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), + {error, ?ERR_INTERNAL_SERVER_ERROR}; ++ {'EXIT', {timeout, _} = Reason} -> ++ case Count of ++ 0 -> ++ ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), ++ {error, ?ERR_INTERNAL_SERVER_ERROR}; ++ N -> ++ erlang:yield(), ++ transaction_retry(Host, Fun, Trans, N-1) ++ end; + {'EXIT', Reason} -> + ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), + {error, ?ERR_INTERNAL_SERVER_ERROR}; +@@ -3663,6 +3517,17 @@ + {error, ?ERR_INTERNAL_SERVER_ERROR} + end. + ++odbc_conn({_U, Host, _R})-> ++ Host; ++odbc_conn(Host) -> ++ Host--"pubsub.". %% TODO, improve that for custom host ++ ++%% escape value for database storage ++escape({_U, _H, _R}=JID)-> ++ ejabberd_odbc:escape(jlib:jid_to_string(JID)); ++escape(Value)-> ++ ejabberd_odbc:escape(Value). ++ + %%%% helpers + + %% Add pubsub-specific error element |