diff options
Diffstat (limited to 'ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl')
-rw-r--r-- | ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl | 1698 |
1 files changed, 0 insertions, 1698 deletions
diff --git a/ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl b/ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl deleted file mode 100644 index 37eb87c17..000000000 --- a/ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl +++ /dev/null @@ -1,1698 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_pubsub.erl -%%% Author : Alexey Shchepin <alexey@sevcom.net> -%%% Purpose : Pub/sub support (JEP-0060) -%%% Created : 4 Jul 2003 by Alexey Shchepin <alexey@sevcom.net> -%%% Id : $Id$ -%%%---------------------------------------------------------------------- - --module(mod_pubsub). --author('alexey@sevcom.net'). --vsn('$Revision$ '). - --behaviour(gen_server). --behaviour(gen_mod). - -%% API --export([start_link/2, - start/2, - stop/1]). - --export([delete_item/3, - set_entities/4, - delete_node/2, - create_new_node/2, - subscribe_node/3, - get_node_config/4, - set_node_config/4]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --include("ejabberd.hrl"). --include("jlib.hrl"). - --record(state, {host, server_host, access}). - --define(DICT, dict). --define(MAXITEMS, 20). --define(MAX_PAYLOAD_SIZE, 100000). - --record(pubsub_node, {host_node, host_parent, info}). --record(nodeinfo, {items = [], - options = [], - entities = ?DICT:new() - }). --record(entity, {affiliation = none, - subscription = none}). --record(item, {id, publisher, payload}). - --define(PROCNAME, ejabberd_mod_pubsub). --define(MYJID, #jid{user = "", server = Host, resource = "", - luser = "", lserver = Host, lresource = ""}). - -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- -start_link(Host, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). - -start(Host, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = - {Proc, - {?MODULE, start_link, [Host, Opts]}, - temporary, - 1000, - worker, - [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec). - -stop(Host) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:call(Proc, stop), - supervisor:stop_child(ejabberd_sup, Proc). - -delete_item(From, Node, ItemID) -> - delete_item(get_host(), From, Node, ItemID). - -delete_node(From, Node) -> - delete_node(get_host(), From, Node). - -create_new_node(Node, From) -> - create_new_node(get_host(), Node, From). - -subscribe_node(From, JID, Node) -> - subscribe_node(get_host(), From, JID, Node). - -set_node_config(From, Node, Els, Lang) -> - set_node_config(get_host(), From, Node, Els, Lang). - -get_host() -> - ejabberd_mod_pubsub ! {get_host, self()}, - receive - {pubsub_host, Host} -> - Host - after 5000 -> - timeout - end. - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -init([ServerHost, Opts]) -> - mnesia:create_table(pubsub_node, - [{disc_only_copies, [node()]}, - {attributes, record_info(fields, pubsub_node)}]), - Host = gen_mod:get_opt(host, Opts, "pubsub." ++ ServerHost), - update_table(Host), - mnesia:add_table_index(pubsub_node, host_parent), - ServedHosts = gen_mod:get_opt(served_hosts, Opts, []), - Access = gen_mod:get_opt(access_createnode, Opts, all), - - ejabberd_router:register_route(Host), - create_new_node(Host, ["pubsub"], ?MYJID), - create_new_node(Host, ["pubsub", "nodes"], ?MYJID), - create_new_node(Host, ["home"], ?MYJID), - create_new_node(Host, ["home", ServerHost], ?MYJID), - lists:foreach(fun(H) -> - create_new_node(Host, ["home", H], ?MYJID) - end, ServedHosts), - ets:new(gen_mod:get_module_proc(Host, pubsub_presence), - [set, named_table]), - {ok, #state{host = Host, server_host = ServerHost, access = Access}}. - -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call(stop, _From, State) -> - {stop, normal, ok, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- -handle_info({route, From, To, Packet}, -#state{server_host = ServerHost, access = Access} = State) -> - case catch do_route(To#jid.lserver, ServerHost, Access, From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end, - {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- -terminate(_Reason, State) -> - ejabberd_router:unregister_route(State#state.host), - ok. - -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- -do_route(Host, ServerHost, Access, From, To, Packet) -> - {xmlelement, Name, Attrs, Els} = Packet, - case To of - #jid{luser = "", lresource = ""} -> - case Name of - "iq" -> - case jlib:iq_query_info(Packet) of - #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, - sub_el = SubEl} = IQ -> - {xmlelement, _, QAttrs, _} = SubEl, - Node = xml:get_attr_s("node", QAttrs), - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - QAttrs, - iq_disco_info(Node)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS, - sub_el = SubEl} = IQ -> - {xmlelement, _, QAttrs, _} = SubEl, - Node = xml:get_attr_s("node", QAttrs), - Res = - case iq_disco_items(Host, From, Node) of - {result, IQRes} -> - jlib:iq_to_xml( - IQ#iq{type = result, - sub_el = [{xmlelement, "query", - QAttrs, - IQRes}]}); - {error, Error} -> - jlib:make_error_reply( - Packet, Error) - end, - ejabberd_router:route(To, From, Res); - #iq{type = Type, xmlns = ?NS_PUBSUB = XMLNS, - sub_el = SubEl} = IQ -> - Res = - case iq_pubsub(Host, ServerHost, From, Type, SubEl, Access) of - {result, IQRes} -> - jlib:iq_to_xml( - IQ#iq{type = result, - sub_el = IQRes}); - {error, Error} -> - jlib:make_error_reply( - Packet, Error) - end, - ejabberd_router:route(To, From, Res); - #iq{type = Type, xmlns = ?NS_PUBSUB_OWNER = XMLNS, - lang = Lang, sub_el = SubEl} = IQ -> - Res = - case iq_pubsub_owner( - Host, From, Type, Lang, SubEl) of - {result, IQRes} -> - jlib:iq_to_xml( - IQ#iq{type = result, - sub_el = IQRes}); - {error, Error} -> - jlib:make_error_reply( - Packet, Error) - end, - ejabberd_router:route(To, From, Res); - #iq{type = get, xmlns = ?NS_VCARD = XMLNS, - lang = Lang, sub_el = SubEl} = IQ -> - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "vCard", - [{"xmlns", XMLNS}], - iq_get_vcard(Lang)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{} -> - Err = jlib:make_error_reply( - Packet, - ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - _ -> - ok - end; - "presence" -> - Type = xml:get_attr_s("type", Attrs), - if - (Type == "unavailable") or (Type == "error") -> - ets:delete( - gen_mod:get_module_proc(Host, pubsub_presence), - {From#jid.luser, From#jid.lserver}); - true -> - ets:insert( - gen_mod:get_module_proc(Host, pubsub_presence), - {{From#jid.luser, From#jid.lserver}, []}) - end, - ok; - _ -> - ok - end; - _ -> - case xml:get_attr_s("type", Attrs) of - "error" -> - ok; - "result" -> - ok; - _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_ITEM_NOT_FOUND), - ejabberd_router:route(To, From, Err) - end - end. - - - -node_to_string(Node) -> - string:strip(lists:flatten(lists:map(fun(S) -> [S, "/"] end, Node)), - right, $/). - - -iq_disco_info(SNode) -> - Node = string:tokens(SNode, "/"), - case Node of - [] -> - [{xmlelement, "identity", - [{"category", "pubsub"}, - {"type", "generic"}, - {"name", "Publish-Subscribe"}], []}, - {xmlelement, "feature", [{"var", ?NS_PUBSUB}], []}, - {xmlelement, "feature", [{"var", ?NS_PUBSUB_EVENT}], []}, - {xmlelement, "feature", [{"var", ?NS_PUBSUB_OWNER}], []}, - {xmlelement, "feature", [{"var", ?NS_VCARD}], []}]; - _ -> - % TODO - [] - end. - -iq_disco_items(Host, From, SNode) -> - {Node,ItemID} = case SNode of - [] -> - {[],none}; - _ -> - Tokens = string:tokens(SNode, "!"), - NodeList = string:tokens(lists:nth(1, Tokens), "/"), - ItemName = case length(Tokens) of - 2 -> lists:nth(2, Tokens); - _ -> none - end, - {NodeList, ItemName} - end, - NodeFull = string:tokens(SNode,"/"), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info}] -> - case ItemID of - none -> - SubNodes = mnesia:index_read(pubsub_node, - {Host, Node}, - #pubsub_node.host_parent), - SubItems = lists:map(fun(#pubsub_node{host_node = {_, N}}) -> - SN = node_to_string(N), - {xmlelement, "item", - [{"jid", Host}, - {"node", SN}, - {"name", lists:last(N)}], []} - end, SubNodes), - SN = node_to_string(Node), - Items = lists:map(fun(#item{id = Name}) -> - RealName = case Name of - [] -> "item"; - _ -> Name - end, - {xmlelement, "item", - [{"jid", Host}, - {"node", SN ++ "!" ++ Name}, - {"name", RealName}], []} - end, Info#nodeinfo.items), - SubItems ++ Items; - _ -> - [] - end; - [] -> - case Node of - [] -> - SubNodes = mnesia:index_read( - pubsub_node, - {Host, Node}, - #pubsub_node.host_parent), - lists:map( - fun(#pubsub_node{host_node = {_, N}}) -> - SN = node_to_string(N), - {xmlelement, "item", - [{"jid", Host}, - {"node", SN}, - {"name", lists:last(N)}], - []} - end, SubNodes) ; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, Res} -> - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - -iq_get_vcard(Lang) -> - [{xmlelement, "FN", [], - [{xmlcdata, "ejabberd/mod_pubsub"}]}, - {xmlelement, "URL", [], - [{xmlcdata, - "http://ejabberd.jabberstudio.org/"}]}, - {xmlelement, "DESC", [], - [{xmlcdata, translate:translate( - Lang, - "ejabberd pub/sub module\n" - "Copyright (c) 2003-2006 Alexey Shchepin")}]}]. - - -iq_pubsub(Host, ServerHost, From, Type, SubEl, Access) -> - {xmlelement, _, _, SubEls} = SubEl, - case xml:remove_cdata(SubEls) of - [{xmlelement, Name, Attrs, Els}] -> - SNode = xml:get_attr_s("node", Attrs), - Node = string:tokens(SNode, "/"), - case {Type, Name} of - {set, "create"} -> - create_new_node(Host, Node, From, ServerHost, Access); - {set, "publish"} -> - case xml:remove_cdata(Els) of - [{xmlelement, "item", ItemAttrs, Payload}] -> - ItemID = xml:get_attr_s("id", ItemAttrs), - publish_item(Host, From, Node, ItemID, Payload); - _ -> - {error, ?ERR_BAD_REQUEST} - end; - {set, "retract"} -> - case xml:remove_cdata(Els) of - [{xmlelement, "item", ItemAttrs, _}] -> - ItemID = xml:get_attr_s("id", ItemAttrs), - delete_item(Host, From, Node, ItemID); - _ -> - {error, ?ERR_BAD_REQUEST} - end; - {set, "subscribe"} -> - JID = xml:get_attr_s("jid", Attrs), - subscribe_node(Host, From, JID, Node); - {set, "unsubscribe"} -> - JID = xml:get_attr_s("jid", Attrs), - unsubscribe_node(Host, From, JID, Node); - {get, "items"} -> - MaxItems = xml:get_attr_s("max_items", Attrs), - get_items(Host, From, Node, MaxItems); - {set, "delete"} -> - delete_node(Host, From, Node); - {set, "purge"} -> - purge_node(Host, From, Node); - {get, "entities"} -> - get_entities(Host, From, Node); - {set, "entities"} -> - set_entities(Host, From, Node, xml:remove_cdata(Els)); - {get, "affiliations"} -> - get_affiliations(Host, From); - _ -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED} - end; - _ -> - {error, ?ERR_BAD_REQUEST} - end. - - --define(XFIELD(Type, Label, Var, Val), - {xmlelement, "field", [{"type", Type}, - {"label", translate:translate(Lang, Label)}, - {"var", Var}], - [{xmlelement, "value", [], [{xmlcdata, Val}]}]}). - --define(BOOLXFIELD(Label, Var, Val), - ?XFIELD("boolean", Label, Var, - case Val of - true -> "1"; - _ -> "0" - end)). - --define(STRINGXFIELD(Label, Var, Val), - ?XFIELD("text-single", Label, Var, Val)). - --define(XFIELDOPT(Type, Label, Var, Val, Opts), - {xmlelement, "field", [{"type", Type}, - {"label", translate:translate(Lang, Label)}, - {"var", Var}], - lists:map(fun(Opt) -> - {xmlelement, "option", [], - [{xmlelement, "value", [], - [{xmlcdata, Opt}]}]} - end, Opts) ++ - [{xmlelement, "value", [], [{xmlcdata, Val}]}]}). - --define(LISTXFIELD(Label, Var, Val, Opts), - ?XFIELDOPT("list-single", Label, Var, Val, Opts)). - - - -%% Create new pubsub nodes -%% This function is used during init to create the first bootstrap nodes -create_new_node(Host, Node, Owner) -> - %% This is the case use during "bootstrapping to create the initial - %% hierarchy. Should always be ... undefined,all - create_new_node(Host, Node, Owner, undefined, all). -create_new_node(Host, Node, Owner, ServerHost, Access) -> - case Node of - [] -> - {LOU, LOS, _} = jlib:jid_tolower(Owner), - HomeNode = ["home", LOS, LOU], - create_new_node(Host, HomeNode, Owner, ServerHost, Access), - NewNode = ["home", LOS, LOU, randoms:get_string()], - create_new_node(Host, NewNode, Owner, ServerHost, Access); - _ -> - LOwner = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Parent = lists:sublist(Node, length(Node) - 1), - F = fun() -> - ParentExists = (Parent == []) orelse - case mnesia:read({pubsub_node, {Host, Parent}}) of - [_] -> - true; - [] -> - false - end, - case ParentExists of - false -> - {error, ?ERR_CONFLICT}; - _ -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [_] -> - {error, ?ERR_CONFLICT}; - [] -> - Entities = - ?DICT:store( - LOwner, - #entity{affiliation = owner, - subscription = none}, - ?DICT:new()), - mnesia:write( - #pubsub_node{host_node = {Host, Node}, - host_parent = {Host, Parent}, - info = #nodeinfo{ - entities = Entities}}), - ok - end - end - end, - case check_create_permission(Host, Node, Owner, ServerHost, Access) of - true -> - case mnesia:transaction(F) of - {atomic, ok} -> - Lang = "", - broadcast_publish_item( - Host, ["pubsub", "nodes"], node_to_string(Node), - [{xmlelement, "x", - [{"xmlns", ?NS_XDATA}, - {"type", "result"}], - [?XFIELD("hidden", "", "FORM_TYPE", - ?NS_PUBSUB_NMI), - ?XFIELD("jid-single", "Node Creator", - "creator", - jlib:jid_to_string(LOwner))]}]), - {result, - [{xmlelement, "pubsub", - [{"xmlns", ?NS_PUBSUB}], - [{xmlelement, "create", - [{"node", node_to_string(Node)}], []}]}]}; - {atomic, {error, _} = Error} -> - Error; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - _ -> - {error, ?ERR_NOT_ALLOWED} - end - end. - - -publish_item(Host, JID, Node, ItemID, Payload) -> - ejabberd_hooks:run(pubsub_publish_item, Host, - [JID, ?MYJID, Node, ItemID, Payload]), - Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - Affiliation = get_affiliation(Info, Publisher), - Subscription = get_subscription(Info, Publisher), - MaxSize = get_node_option(Info, max_payload_size), - Model = get_node_option(Info, publish_model), - Size = size(term_to_binary(Payload)), - if - ((Model == open) or - ((Model == publishers) and - ((Affiliation == owner) or - (Affiliation == publisher))) or - ((Model == subscribers) and - (Subscription == subscribed))) and - (Size =< MaxSize) -> - NewInfo = - insert_item(Info, ItemID, - Publisher, Payload), - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, []}; - true -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res}} -> - broadcast_publish_item(Host, Node, ItemID, Payload), - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - -delete_item(Host, JID, Node, ItemID) -> - Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - case check_item_publisher(Info, ItemID, Publisher) - orelse - (get_affiliation(Info, Publisher) == owner) of - true -> - NewInfo = - remove_item(Info, ItemID), - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, []}; - _ -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res}} -> - broadcast_retract_item(Host, Node, ItemID), - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - -subscribe_node(Host, From, JID, Node) -> - Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)), - SubscriberJID = - case jlib:string_to_jid(JID) of - error -> - {"", "", ""}; - J -> - J - end, - Subscriber = jlib:jid_tolower(SubscriberJID), - SubscriberWithoutResource = jlib:jid_remove_resource(Subscriber), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - Affiliation = get_affiliation(Info, Subscriber), - AllowSubscriptions = get_node_option(Info, subscribe), - if - AllowSubscriptions and - (Affiliation /= outcast) -> - NewInfo = add_subscriber(Info, Subscriber), - mnesia:write(N#pubsub_node{info = NewInfo}), - {result, [], Info}; - true -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - if - Sender == SubscriberWithoutResource -> - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res, Info}} -> - case get_node_option(Info, send_item_subscribe) of - true -> - ItemsEls = - lists:map( - fun(#item{id = ItemID, - payload = Payload}) -> - ItemAttrs = case ItemID of - "" -> []; - _ -> [{"id", ItemID}] - end, - {xmlelement, "item", - ItemAttrs, Payload} - end, Info#nodeinfo.items), - Stanza = - {xmlelement, "message", - [], - [{xmlelement, "x", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - ItemsEls}]}]}, - ejabberd_router:route( - ?MYJID, jlib:make_jid(Subscriber), Stanza); - false -> - ok - end, - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - true -> - {error, ?ERR_NOT_ALLOWED} - end. - - -unsubscribe_node(Host, From, JID, Node) -> - Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)), - SubscriberJID = - case jlib:string_to_jid(JID) of - error -> - {"", "", ""}; - J -> - J - end, - Subscriber = jlib:jid_tolower(SubscriberJID), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - Subscription = get_subscription(Info, Subscriber), - if - Subscription /= none -> - NewInfo = - remove_subscriber(Info, Subscriber), - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, []}; - true -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - if - Sender == Subscriber -> - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res}} -> - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - true -> - {error, ?ERR_NOT_ALLOWED} - end. - - -get_items(Host, JID, Node, SMaxItems) -> - MaxItems = - if - SMaxItems == "" -> - ?MAXITEMS; - true -> - case catch list_to_integer(SMaxItems) of - {'EXIT', _} -> - {error, ?ERR_BAD_REQUEST}; - Val -> - Val - end - end, - case MaxItems of - {error, _} = Error -> - Error; - _ -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - Items = lists:sublist(Info#nodeinfo.items, MaxItems), - ItemsEls = - lists:map( - fun(#item{id = ItemID, - payload = Payload}) -> - ItemAttrs = case ItemID of - "" -> []; - _ -> [{"id", ItemID}] - end, - {xmlelement, "item", ItemAttrs, Payload} - end, Items), - {result, [{xmlelement, "pubsub", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - ItemsEls}]}]}; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end. - - -delete_node(Host, JID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info}] -> - case get_affiliation(Info, Owner) of - owner -> - % TODO: don't iterate over entire table - Removed = - mnesia:foldl( - fun(#pubsub_node{host_node = {_, N}, - info = NInfo}, Acc) -> - case lists:prefix(Node, N) of - true -> - [{N, NInfo} | Acc]; - _ -> - Acc - end - end, [], pubsub_node), - lists:foreach( - fun({N, _}) -> - mnesia:delete({pubsub_node, {Host, N}}) - end, Removed), - {removed, Removed}; - _ -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {removed, Removed}} -> - broadcast_removed_node(Host, Removed), - Lang = "", - broadcast_retract_item( - Host, ["pubsub", "nodes"], node_to_string(Node)), - {result, []}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - -purge_node(Host, JID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - case get_affiliation(Info, Owner) of - owner -> - NewInfo = Info#nodeinfo{items = []}, - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, Info#nodeinfo.items, []}; - _ -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Items, Res}} -> - lists:foreach( - fun(#item{id = ItemID}) -> - broadcast_retract_item(Host, Node, ItemID) - end, Items), - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - -get_entities(Host, OJID, Node) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - case get_affiliation(Info, Owner) of - owner -> - Entities = Info#nodeinfo.entities, - EntitiesEls = - ?DICT:fold( - fun(JID, - #entity{affiliation = Affiliation, - subscription = Subscription}, - Acc) -> - [{xmlelement, "entity", - [{"jid", jlib:jid_to_string(JID)}, - {"affiliation", - affiliation_to_string(Affiliation)}, - {"subscription", - subscription_to_string(Subscription)}], - []} | Acc] - end, [], Entities), - {result, [{xmlelement, "pubsub", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "entities", - [{"node", node_to_string(Node)}], - EntitiesEls}]}]}; - _ -> - {error, ?ERR_NOT_ALLOWED} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - - -set_entities(Host, OJID, Node, EntitiesEls) -> - Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)), - Entities = - lists:foldl( - fun(El, Acc) -> - case Acc of - error -> - error; - _ -> - case El of - {xmlelement, "entity", Attrs, _} -> - JID = jlib:string_to_jid( - xml:get_attr_s("jid", Attrs)), - Affiliation = - case xml:get_attr_s("affiliation", - Attrs) of - "owner" -> owner; - "publisher" -> publisher; - "outcast" -> outcast; - "none" -> none; - _ -> false - end, - Subscription = - case xml:get_attr_s("subscription", - Attrs) of - "subscribed" -> subscribed; - "pending" -> pending; - "unconfigured" -> unconfigured; - "none" -> none; - _ -> false - end, - if - (JID == error) or - (Affiliation == false) or - (Subscription == false) -> - error; - true -> - [{jlib:jid_tolower(JID), - #entity{ - affiliation = Affiliation, - subscription = Subscription}} | - Acc] - end - end - end - end, [], EntitiesEls), - case Entities of - error -> - {error, ?ERR_BAD_REQUEST}; - _ -> - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - case get_affiliation(Info, Owner) of - owner -> - NewInfo = - set_info_entities(Info, Entities), - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, []}; - _ -> - {error, ?ERR_NOT_ALLOWED} - end; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, _}} -> - {result, []}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end - end. - - -get_affiliations(Host, JID) -> - LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - case catch mnesia:dirty_select( - pubsub_node, - [{#pubsub_node{_ = '_'}, - [], - ['$_']}]) of - {'EXIT', _} -> - {error, ?ERR_INTERNAL_SERVER_ERROR}; - Nodes -> - Entities = - lists:flatmap( - fun(#pubsub_node{host_node = {H, Node}, info = Info}) - when H == Host -> - Affiliation = get_affiliation(Info, LJID), - Subscription = get_subscription(Info, LJID), - if - (Affiliation /= none) or - (Subscription /= none) -> - [{xmlelement, "entity", - [{"node", node_to_string(Node)}, - {"jid", jlib:jid_to_string(JID)}, - {"affiliation", - affiliation_to_string(Affiliation)}, - {"subscription", - subscription_to_string(Subscription)}], - []}]; - true -> - [] - end; - (_) -> - [] - end, Nodes), - {result, [{xmlelement, "pubsub", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "affiliations", [], - Entities}]}]} - end. - - - - -get_affiliation(#nodeinfo{entities = Entities}, JID) -> - LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - case ?DICT:find(LJID, Entities) of - {ok, #entity{affiliation = Affiliation}} -> - Affiliation; - _ -> - none - end. - -get_subscription(#nodeinfo{entities = Entities}, JID) -> - LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)), - case ?DICT:find(LJID, Entities) of - {ok, #entity{subscription = Subscription}} -> - Subscription; - _ -> - none - end. - -affiliation_to_string(Affiliation) -> - case Affiliation of - owner -> "owner"; - publisher -> "publisher"; - outcast -> "outcast"; - _ -> "none" - end. - -subscription_to_string(Subscription) -> - case Subscription of - subscribed -> "subscribed"; - pending -> "pending"; - unconfigured -> "unconfigured"; - _ -> "none" - end. - - -check_create_permission(Host, Node, Owner, ServerHost, Access) -> - #jid{luser = User, lserver = Server, lresource = Resource} = Owner, - case acl:match_rule(ServerHost, Access, {User, Server, Resource}) of - allow -> - if Server == Host -> - true; - true -> - case Node of - ["home", Server, User | _] -> - true; - _ -> - false - end - end; - _ -> - case Owner of - ?MYJID -> - true; - _ -> - false - end - end. - -insert_item(Info, ItemID, Publisher, Payload) -> - Items = Info#nodeinfo.items, - Items1 = lists:filter(fun(I) -> - I#item.id /= ItemID - end, Items), - Items2 = [#item{id = ItemID, publisher = Publisher, payload = Payload} | - Items1], - Items3 = lists:sublist(Items2, get_max_items(Info)), - Info#nodeinfo{items = Items3}. - -remove_item(Info, ItemID) -> - Items = Info#nodeinfo.items, - Items1 = lists:filter(fun(I) -> - I#item.id /= ItemID - end, Items), - Info#nodeinfo{items = Items1}. - -check_item_publisher(Info, ItemID, Publisher) -> - Items = Info#nodeinfo.items, - case lists:keysearch(ItemID, #item.id, Items) of - {value, #item{publisher = Publisher}} -> - true; - _ -> - false - end. - -add_subscriber(Info, Subscriber) -> - Entities = Info#nodeinfo.entities, - case ?DICT:find(Subscriber, Entities) of - {ok, Entity} -> - Info#nodeinfo{ - entities = ?DICT:store(Subscriber, - Entity#entity{subscription = subscribed}, - Entities)}; - _ -> - Info#nodeinfo{ - entities = ?DICT:store(Subscriber, - #entity{subscription = subscribed}, - Entities)} - end. - -remove_subscriber(Info, Subscriber) -> - Entities = Info#nodeinfo.entities, - case ?DICT:find(Subscriber, Entities) of - {ok, #entity{affiliation = none}} -> - Info#nodeinfo{ - entities = ?DICT:erase(Subscriber, Entities)}; - {ok, Entity} -> - Info#nodeinfo{ - entities = ?DICT:store(Subscriber, - Entity#entity{subscription = none}, - Entities)}; - _ -> - Info - end. - - -set_info_entities(Info, Entities) -> - NewEntities = - lists:foldl( - fun({JID, Ent}, Es) -> - case Ent of - #entity{affiliation = none, subscription = none} -> - ?DICT:erase(JID, Es); - _ -> - ?DICT:store(JID, Ent, Es) - end - end, Info#nodeinfo.entities, Entities), - Info#nodeinfo{entities = NewEntities}. - - - -broadcast_publish_item(Host, Node, ItemID, Payload) -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> - Present = case get_node_option( - Info, presence_based_delivery) of - true -> - case ets:lookup( - gen_mod:get_module_proc(Host, pubsub_presence), - {element(1, JID), - element(2, JID)}) of - [_] -> - true; - [] -> - false - end; - false -> - true - end, - if - (Subscription /= none) and - (Subscription /= pending) and - Present -> - ItemAttrs = case ItemID of - "" -> []; - _ -> [{"id", ItemID}] - end, - Content = case get_node_option( - Info, deliver_payloads) of - true -> - Payload; - false -> - [] - end, - Stanza = - {xmlelement, "message", [], - [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - [{xmlelement, "item", - ItemAttrs, - Content}]}]}]}, - ejabberd_router:route( - ?MYJID, jlib:make_jid(JID), Stanza); - true -> - ok - end - end, ok, Info#nodeinfo.entities); - _ -> - false - end. - - -broadcast_retract_item(Host, Node, ItemID) -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - case get_node_option(Info, notify_retract) of - true -> - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> - if - (Subscription /= none) and - (Subscription /= pending) -> - ItemAttrs = case ItemID of - "" -> []; - _ -> [{"id", ItemID}] - end, - Stanza = - {xmlelement, "message", [], - [{xmlelement, "x", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - [{xmlelement, "retract", - ItemAttrs, []}]}]}]}, - ejabberd_router:route( - ?MYJID, jlib:make_jid(JID), Stanza); - true -> - ok - end - end, ok, Info#nodeinfo.entities); - false -> - ok - end; - _ -> - false - end. - - -broadcast_removed_node(Host, Removed) -> - lists:foreach( - fun({Node, Info}) -> - case get_node_option(Info, notify_delete) of - true -> - Entities = Info#nodeinfo.entities, - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> - if - (Subscription /= none) and - (Subscription /= pending) -> - Stanza = - {xmlelement, "message", [], - [{xmlelement, "x", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "delete", - [{"node", node_to_string(Node)}], - []}]}]}, - ejabberd_router:route( - ?MYJID, jlib:make_jid(JID), Stanza); - true -> - ok - end - end, ok, Entities); - false -> - ok - end - end, Removed). - - -broadcast_config_notification(Host, Node, Lang) -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - case get_node_option(Info, notify_config) of - true -> - ?DICT:fold( - fun(JID, #entity{subscription = Subscription}, _) -> - Present = case get_node_option( - Info, presence_based_delivery) of - true -> - case ets:lookup( - gen_mod:get_module_proc(Host, pubsub_presence), - {element(1, JID), - element(2, JID)}) of - [_] -> - true; - [] -> - false - end; - false -> - true - end, - if - (Subscription /= none) and - (Subscription /= pending) and - Present -> - Fields = get_node_config_xfields( - Node, Info, Lang), - Content = case get_node_option( - Info, deliver_payloads) of - true -> - [{xmlelement, "x", - [{"xmlns", ?NS_XDATA}, - {"type", "form"}], - Fields}]; - false -> - [] - end, - Stanza = - {xmlelement, "message", [], - [{xmlelement, "x", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", - [{"node", node_to_string(Node)}], - [{xmlelement, "item", - [{"id", "configuration"}], - Content}]}]}]}, - ejabberd_router:route( - ?MYJID, jlib:make_jid(JID), Stanza); - true -> - ok - end - end, ok, Info#nodeinfo.entities); - false -> - ok - end; - _ -> - false - end. - - - -iq_pubsub_owner(Host, From, Type, Lang, SubEl) -> - {xmlelement, _, _, SubEls} = SubEl, - case xml:remove_cdata(SubEls) of - [{xmlelement, Name, Attrs, Els}] -> - SNode = xml:get_attr_s("node", Attrs), - Node = string:tokens(SNode, "/"), - case {Type, Name} of - {get, "configure"} -> - get_node_config(Host, From, Node, Lang); - {set, "configure"} -> - set_node_config(Host, From, Node, Els, Lang); - _ -> - {error, ?ERR_FEATURE_NOT_IMPLEMENTED} - end; - _ -> - {error, ?ERR_BAD_REQUEST} - end. - -get_node_config(Host, From, Node, Lang) -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info}] -> - case get_affiliation(Info, From) of - owner -> - Fields = get_node_config_xfields(Node, Info, Lang), - {result, [{xmlelement, "pubsub", - [{"xmlns", ?NS_PUBSUB_OWNER}], - [{xmlelement, "configure", - [{"node", node_to_string(Node)}], - [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, - {"type", "form"}], - Fields}]}]}]}; - _ -> - {error, ?ERR_NOT_AUTHORIZED} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - -% TODO: move to jlib.hrl --define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config"). - --define(BOOL_CONFIG_FIELD(Label, Var), - ?BOOLXFIELD(Label, "pubsub#" ++ atom_to_list(Var), - get_node_option(Info, Var))). - --define(STRING_CONFIG_FIELD(Label, Var), - ?STRINGXFIELD(Label, "pubsub#" ++ atom_to_list(Var), - get_node_option(Info, Var))). - --define(INTEGER_CONFIG_FIELD(Label, Var), - ?STRINGXFIELD(Label, "pubsub#" ++ atom_to_list(Var), - integer_to_list(get_node_option(Info, Var)))). - --define(JLIST_CONFIG_FIELD(Label, Var, Opts), - ?LISTXFIELD(Label, "pubsub#" ++ atom_to_list(Var), - jlib:jid_to_string(get_node_option(Info, Var)), - [jlib:jid_to_string(O) || O <- Opts])). - --define(ALIST_CONFIG_FIELD(Label, Var, Opts), - ?LISTXFIELD(Label, "pubsub#" ++ atom_to_list(Var), - atom_to_list(get_node_option(Info, Var)), - [atom_to_list(O) || O <- Opts])). - - --define(DEFAULT_OPTIONS, - [{deliver_payloads, true}, - {notify_config, false}, - {notify_delete, false}, - {notify_retract, true}, - {persist_items, true}, - {max_items, ?MAXITEMS div 2}, - {subscribe, true}, - {subscription_model, open}, - {publish_model, publishers}, - {max_payload_size, ?MAX_PAYLOAD_SIZE}, - {send_item_subscribe, false}, - {presence_based_delivery, false}]). - -get_node_option(Info, current_approver) -> - Default = hd(get_owners_jids(Info)), - Options = Info#nodeinfo.options, - element( - 2, element(2, lists:keysearch( - current_approver, 1, - Options ++ [{current_approver, Default}]))); -get_node_option(#nodeinfo{options = Options}, Var) -> - element( - 2, element(2, lists:keysearch(Var, 1, Options ++ ?DEFAULT_OPTIONS))). - -get_max_items(Info) -> - case get_node_option(Info, persist_items) of - true -> - get_node_option(Info, max_items); - false -> - 0 - end. - -get_owners_jids(Info) -> - Entities = Info#nodeinfo.entities, - Owners = - ?DICT:fold( - fun(JID, - #entity{affiliation = Affiliation, - subscription = Subscription}, - Acc) -> - case Affiliation of - owner -> - [JID | Acc]; - _ -> - Acc - end - end, [], Entities), - lists:sort(Owners). - - -get_node_config_xfields(Node, Info, Lang) -> - [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG), - ?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads), - ?BOOL_CONFIG_FIELD("Notify subscribers when the node configuration changes", notify_config), - ?BOOL_CONFIG_FIELD("Notify subscribers when the node is deleted", notify_delete), - ?BOOL_CONFIG_FIELD("Notify subscribers when items are removed from the node", notify_retract), - ?BOOL_CONFIG_FIELD("Persist items to storage", persist_items), - ?INTEGER_CONFIG_FIELD("Max # of items to persist", max_items), - ?BOOL_CONFIG_FIELD("Whether to allow subscriptions", subscribe), - ?ALIST_CONFIG_FIELD("Specify the subscriber model", subscription_model, - [open]), - ?ALIST_CONFIG_FIELD("Specify the publisher model", publish_model, - [publishers, subscribers, open]), - ?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size), - ?BOOL_CONFIG_FIELD("Send items to new subscribers", send_item_subscribe), - ?BOOL_CONFIG_FIELD("Only deliver notifications to available users", presence_based_delivery), - ?JLIST_CONFIG_FIELD("Specify the current subscription approver", current_approver, - get_owners_jids(Info)) - ]. - - -set_node_config(Host, From, Node, Els, Lang) -> - case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of - [#pubsub_node{info = Info} = N] -> - case get_affiliation(Info, From) of - owner -> - case xml:remove_cdata(Els) of - [{xmlelement, "x", _Attrs1, _Els1} = XEl] -> - case {xml:get_tag_attr_s("xmlns", XEl), - xml:get_tag_attr_s("type", XEl)} of - {?NS_XDATA, "cancel"} -> - {result, []}; - {?NS_XDATA, "submit"} -> - CurOpts = Info#nodeinfo.options, - set_node_config1( - Host, From, Node, XEl, CurOpts, Lang); - _ -> - {error, ?ERR_BAD_REQUEST} - end; - _ -> - {error, ?ERR_BAD_REQUEST} - end; - _ -> - {error, ?ERR_NOT_AUTHORIZED} - end; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} - end. - - -set_node_config1(Host, From, Node, XEl, CurOpts, Lang) -> - XData = jlib:parse_xdata_submit(XEl), - case XData of - invalid -> - {error, ?ERR_BAD_REQUEST}; - _ -> - case set_xoption(XData, CurOpts) of - NewOpts when is_list(NewOpts) -> - change_node_opts(Host, NewOpts, Node, Lang); - Err -> - Err - end - end. - -add_opt(Key, Value, Opts) -> - Opts1 = lists:keydelete(Key, 1, Opts), - [{Key, Value} | Opts1]. - - --define(SET_BOOL_XOPT(Opt, Val), - case Val of - "0" -> set_xoption(Opts, add_opt(Opt, false, NewOpts)); - "1" -> set_xoption(Opts, add_opt(Opt, true, NewOpts)); - _ -> {error, ?ERR_BAD_REQUEST} - end). - --define(SET_STRING_XOPT(Opt, Val), - set_xoption(Opts, add_opt(Opt, Val, NewOpts))). - --define(SET_INTEGER_XOPT(Opt, Val, Min, Max), - case catch list_to_integer(Val) of - IVal when is_integer(IVal), - IVal >= Min, - IVal =< Max -> - set_xoption(Opts, add_opt(Opt, IVal, NewOpts)); - _ -> - {error, ?ERR_BAD_REQUEST} - end). - --define(SET_ALIST_XOPT(Opt, Val, Vals), - case lists:member(Val, [atom_to_list(V) || V <- Vals]) of - true -> - set_xoption(Opts, add_opt(Opt, list_to_atom(Val), NewOpts)); - false -> - {error, ?ERR_BAD_REQUEST} - end). - - -set_xoption([], NewOpts) -> - NewOpts; -set_xoption([{"FORM_TYPE", _} | Opts], NewOpts) -> - set_xoption(Opts, NewOpts); -set_xoption([{"pubsub#deliver_payloads", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(deliver_payloads, Val); -set_xoption([{"pubsub#notify_config", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(notify_config, Val); -set_xoption([{"pubsub#notify_delete", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(notify_delete, Val); -set_xoption([{"pubsub#notify_retract", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(notify_retract, Val); -set_xoption([{"pubsub#persist_items", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(persist_items, Val); -set_xoption([{"pubsub#max_items", [Val]} | Opts], NewOpts) -> - ?SET_INTEGER_XOPT(max_items, Val, 0, ?MAXITEMS); -set_xoption([{"pubsub#subscribe", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(subscribe, Val); -set_xoption([{"pubsub#subscription_model", [Val]} | Opts], NewOpts) -> - ?SET_ALIST_XOPT(subscription_model, Val, [open]); -set_xoption([{"pubsub#publish_model", [Val]} | Opts], NewOpts) -> - ?SET_ALIST_XOPT(publish_model, Val, [publishers, subscribers, open]); -set_xoption([{"pubsub#max_payload_size", [Val]} | Opts], NewOpts) -> - ?SET_INTEGER_XOPT(max_payload_size, Val, 0, ?MAX_PAYLOAD_SIZE); -set_xoption([{"pubsub#send_item_subscribe", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(send_item_subscribe, Val); -set_xoption([{"pubsub#presence_based_delivery", [Val]} | Opts], NewOpts) -> - ?SET_BOOL_XOPT(presence_based_delivery, Val); -set_xoption([{"pubsub#current_approver", _} | Opts], NewOpts) -> - % TODO - set_xoption(Opts, NewOpts); -%set_xoption([{"title", [Val]} | Opts], NewOpts) -> -% ?SET_STRING_XOPT(title, Val); -set_xoption([_ | _Opts], _NewOpts) -> - {error, ?ERR_BAD_REQUEST}. - - -change_node_opts(Host, NewOpts, Node, Lang) -> - F = fun() -> - case mnesia:read({pubsub_node, {Host, Node}}) of - [#pubsub_node{info = Info} = N] -> - NewInfo = Info#nodeinfo{options = NewOpts}, - mnesia:write( - N#pubsub_node{info = NewInfo}), - {result, []}; - [] -> - {error, ?ERR_ITEM_NOT_FOUND} - end - end, - case mnesia:transaction(F) of - {atomic, {error, _} = Error} -> - Error; - {atomic, {result, Res}} -> - broadcast_config_notification(Host, Node, Lang), - {result, Res}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} - end. - - - - - -find_my_host(LServer) -> - Parts = string:tokens(LServer, "."), - find_my_host(Parts, ?MYHOSTS). - -find_my_host([], _Hosts) -> - ?MYNAME; -find_my_host([_ | Tail] = Parts, Hosts) -> - Domain = parts_to_string(Parts), - case lists:member(Domain, Hosts) of - true -> - Domain; - false -> - find_my_host(Tail, Hosts) - end. - -parts_to_string(Parts) -> - string:strip(lists:flatten(lists:map(fun(S) -> [S, $.] end, Parts)), - right, $.). - - - -update_table(Host) -> - Fields = record_info(fields, pubsub_node), - case mnesia:table_info(pubsub_node, attributes) of - Fields -> - ok; - [node, parent, info] -> - ?INFO_MSG("Converting pubsub_node table from " - "{node, parent, info} format", []), - {atomic, ok} = mnesia:create_table( - mod_pubsub_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, pubsub_node}, - {attributes, record_info(fields, pubsub_node)}]), - mnesia:del_table_index(pubsub_node, parent), - mnesia:transform_table(pubsub_node, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_pubsub_tmp_table), - mnesia:foldl( - fun(#pubsub_node{host_node = N, - host_parent = P} = R, _) -> - mnesia:dirty_write( - mod_pubsub_tmp_table, - R#pubsub_node{host_node = {Host, N}, - host_parent = {Host, P}}) - end, ok, pubsub_node) - end, - mnesia:transaction(F1), - mnesia:clear_table(pubsub_node), - F2 = fun() -> - mnesia:write_lock_table(pubsub_node), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_pubsub_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_pubsub_tmp_table); - _ -> - ?INFO_MSG("Recreating pubsub_node table", []), - mnesia:transform_table(pubsub_node, ignore, Fields) - end. - - - - |