aboutsummaryrefslogtreecommitdiff
path: root/ejabberd-1.1.2/src/mod_pubsub/mod_pubsub.erl
diff options
context:
space:
mode:
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.erl1698
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.
-
-
-
-