diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index 7eabdab8f..d2acf316c 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -23,27 +23,27 @@ 'presence-subscription-required' | 'subid-required' | 'too-many-subscriptions' | 'unsupported' | 'unsupported-access-model'. --type ps_error_feature() :: 'access-authorize' | 'access-open' | - 'access-presence' | 'access-roster' | - 'access-whitelist' | 'auto-create' | - 'auto-subscribe' | 'collections' | 'config-node' | - 'create-and-configure' | 'create-nodes' | - 'delete-items' | 'delete-nodes' | - 'filtered-notifications' | 'get-pending' | - 'instant-nodes' | 'item-ids' | 'last-published' | - 'leased-subscription' | 'manage-subscriptions' | - 'member-affiliation' | 'meta-data' | - 'modify-affiliations' | 'multi-collection' | - 'multi-subscribe' | 'outcast-affiliation' | - 'persistent-items' | 'presence-notifications' | - 'presence-subscribe' | 'publish' | - 'publish-options' | 'publish-only-affiliation' | - 'publisher-affiliation' | 'purge-nodes' | - 'retract-items' | 'retrieve-affiliations' | - 'retrieve-default' | 'retrieve-items' | - 'retrieve-subscriptions' | 'subscribe' | - 'subscription-options' | 'subscription-notifications'. --record(ps_error, {type :: ps_error_type(), feature :: ps_error_feature()}). +-type ps_feature() :: 'access-authorize' | 'access-open' | + 'access-presence' | 'access-roster' | + 'access-whitelist' | 'auto-create' | + 'auto-subscribe' | 'collections' | 'config-node' | + 'create-and-configure' | 'create-nodes' | + 'delete-items' | 'delete-nodes' | + 'filtered-notifications' | 'get-pending' | + 'instant-nodes' | 'item-ids' | 'last-published' | + 'leased-subscription' | 'manage-subscriptions' | + 'member-affiliation' | 'meta-data' | + 'modify-affiliations' | 'multi-collection' | + 'multi-subscribe' | 'outcast-affiliation' | + 'persistent-items' | 'presence-notifications' | + 'presence-subscribe' | 'publish' | + 'publish-options' | 'publish-only-affiliation' | + 'publisher-affiliation' | 'purge-nodes' | + 'retract-items' | 'retrieve-affiliations' | + 'retrieve-default' | 'retrieve-items' | + 'retrieve-subscriptions' | 'subscribe' | + 'subscription-options' | 'subscription-notifications'. +-record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}). -type ps_error() :: #ps_error{}. -record(chatstate, {type :: active | composing | gone | inactive | paused}). diff --git a/src/adhoc.erl b/src/adhoc.erl deleted file mode 100644 index 6970584f9..000000000 --- a/src/adhoc.erl +++ /dev/null @@ -1,149 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : adhoc.erl -%%% Author : Magnus Henoch -%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050) -%%% Created : 31 Oct 2005 by Magnus Henoch -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License along -%%% with this program; if not, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(adhoc). - --author('henoch@dtek.chalmers.se'). - --export([ - parse_request/1, - produce_response/2, - produce_response/1 -]). - --include("ejabberd.hrl"). --include("logger.hrl"). --include("jlib.hrl"). --include("adhoc.hrl"). - -%% Parse an ad-hoc request. Return either an adhoc_request record or -%% an {error, ErrorType} tuple. -%% --spec parse_request(IQ :: iq_request()) -> adhoc_response() | {error, _}. - -parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) -> - ?DEBUG("entering parse_request...", []), - Node = fxml:get_tag_attr_s(<<"node">>, SubEl), - SessionID = fxml:get_tag_attr_s(<<"sessionid">>, SubEl), - Action = fxml:get_tag_attr_s(<<"action">>, SubEl), - XData = find_xdata_el(SubEl), - #xmlel{children = AllEls} = SubEl, - Others = case XData of - false -> AllEls; - _ -> lists:delete(XData, AllEls) - end, - #adhoc_request{ - lang = Lang, - node = Node, - sessionid = SessionID, - action = Action, - xdata = XData, - others = Others - }; -parse_request(#iq{lang = Lang}) -> - Text = <<"Failed to parse ad-hoc command request">>, - {error, ?ERRT_BAD_REQUEST(Lang, Text)}. - -%% Borrowed from mod_vcard.erl -find_xdata_el(#xmlel{children = SubEls}) -> - find_xdata_el1(SubEls). - -find_xdata_el1([]) -> false; -find_xdata_el1([El | Els]) when is_record(El, xmlel) -> - case fxml:get_tag_attr_s(<<"xmlns">>, El) of - ?NS_XDATA -> El; - _ -> find_xdata_el1(Els) - end; -find_xdata_el1([_ | Els]) -> find_xdata_el1(Els). - -%% Produce a node to use as response from an adhoc_response -%% record, filling in values for language, node and session id from -%% the request. -%% --spec produce_response(Adhoc_Request :: adhoc_request(), - Adhoc_Response :: adhoc_response()) -> - Xmlel::xmlel(). - -%% Produce a node to use as response from an adhoc_response -%% record. -produce_response(#adhoc_request{lang = Lang, node = Node, sessionid = SessionID}, - Adhoc_Response) -> - produce_response(Adhoc_Response#adhoc_response{ - lang = Lang, node = Node, sessionid = SessionID - }). - -%% --spec produce_response(Adhoc_Response::adhoc_response()) -> Xmlel::xmlel(). - -produce_response( - #adhoc_response{ - %lang = _Lang, - node = Node, - sessionid = ProvidedSessionID, - status = Status, - defaultaction = DefaultAction, - actions = Actions, - notes = Notes, - elements = Elements - }) -> - SessionID = if is_binary(ProvidedSessionID), - ProvidedSessionID /= <<"">> -> ProvidedSessionID; - true -> jlib:now_to_utc_string(p1_time_compat:timestamp()) - end, - case Actions of - [] -> - ActionsEls = []; - _ -> - case DefaultAction of - <<"">> -> ActionsElAttrs = []; - _ -> ActionsElAttrs = [{<<"execute">>, DefaultAction}] - end, - ActionsEls = [ - #xmlel{ - name = <<"actions">>, - attrs = ActionsElAttrs, - children = [ - #xmlel{name = Action, attrs = [], children = []} - || Action <- Actions] - } - ] - end, - NotesEls = lists:map(fun({Type, Text}) -> - #xmlel{ - name = <<"note">>, - attrs = [{<<"type">>, Type}], - children = [{xmlcdata, Text}] - } - end, Notes), - #xmlel{ - name = <<"command">>, - attrs = [ - {<<"xmlns">>, ?NS_COMMANDS}, - {<<"sessionid">>, SessionID}, - {<<"node">>, Node}, - {<<"status">>, iolist_to_binary(atom_to_list(Status))} - ], - children = ActionsEls ++ NotesEls ++ Elements - }. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index c84fb5fe8..a7a0e2d88 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -41,8 +41,6 @@ -include("ejabberd.hrl"). -include("logger.hrl"). -%%-include("adhoc.hrl"). -%%-include("jlib.hrl"). -include("xmpp.hrl"). -include("pubsub.hrl"). @@ -1199,11 +1197,14 @@ iq_get_vcard(Lang) -> iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, sub_els = [SubEl]}) -> case {IQType, SubEl} of - {set, #pubsub{create = Node, configure = {_, XData}, + {set, #pubsub{create = Node, configure = Configure, _ = undefined}} when is_binary(Node) -> ServerHost = serverhost(Host), Plugins = config(ServerHost, plugins), - Config = get_xdata_fields(XData), + Config = case Configure of + {_, XData} -> get_xdata_fields(XData); + undefined -> [] + end, Type = hd(Plugins), create_node(Host, ServerHost, Node, From, Type, Access, Config); {set, #pubsub{publish = #ps_publish{node = Node, items = Items}, @@ -1223,7 +1224,12 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, _ = undefined}} -> case Items of [#ps_item{id = ItemId}] -> - delete_item(Host, Node, From, ItemId, Notify); + if ItemId /= <<>> -> + delete_item(Host, Node, From, ItemId, Notify); + true -> + {error, extended_error(xmpp:err_bad_request(), + err_item_required())} + end; [] -> {error, extended_error(xmpp:err_bad_request(), err_item_required())}; _ -> @@ -1259,11 +1265,14 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, jid = JID, xdata = XData}, _ = undefined}} -> set_options(Host, Node, JID, SubId, get_xdata_fields(XData)); + {set, #pubsub{}} -> + {error, xmpp:err_bad_request()}; _ -> {error, xmpp:err_feature_not_implemented()} end. --spec iq_pubsub_owner(binary() | ljid(), iq()) -> {result, pubsub()} | {error, error()}. +-spec iq_pubsub_owner(binary() | ljid(), iq()) -> {result, pubsub_owner() | undefined} | + {error, error()}. iq_pubsub_owner(Host, #iq{type = IQType, from = From, lang = Lang, sub_els = [SubEl]}) -> case {IQType, SubEl} of @@ -1275,7 +1284,7 @@ iq_pubsub_owner(Host, #iq{type = IQType, from = From, undefined -> {error, xmpp:err_bad_request(<<"No data form found">>, Lang)}; #xdata{type = cancel} -> - {result, #pubsub{}}; + {result, #pubsub_owner{}}; #xdata{type = submit} -> Config = get_xdata_fields(XData), set_configure(Host, Node, From, Config, Lang); @@ -1684,7 +1693,7 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> %%
  • The node is the root collection node, which cannot be deleted.
  • %%
  • The specified node does not exist.
  • %% --spec delete_node(host(), binary(), jid()) -> {result, pubsub()} | {error, error()}. +-spec delete_node(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, error()}. delete_node(_Host, <<>>, _Owner) -> {error, xmpp:err_not_allowed(<<"No node specified">>, ?MYLANG)}; delete_node(Host, Node, Owner) -> @@ -1701,7 +1710,7 @@ delete_node(Host, Node, Owner) -> {error, xmpp:err_forbidden(<<"Owner privileges required">>, ?MYLANG)} end end, - Reply = [], + Reply = undefined, ServerHost = serverhost(Host), case transaction(Host, Node, Action, transaction) of {result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} -> @@ -2566,8 +2575,8 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) -> Error end. --spec get_subscriptions(binary(), binary(), jid()) -> {result, pubsub()} | - {error, error()}. +-spec get_subscriptions(host(), binary(), jid()) -> {result, pubsub_owner()} | + {error, error()}. get_subscriptions(Host, Node, JID) -> Action = fun (#pubsub_node{type = Type, id = Nidx}) -> Features = plugin_features(Host, Type), @@ -2595,7 +2604,7 @@ get_subscriptions(Host, Node, JID) -> ({AJID, Sub, SubId}) -> [#ps_subscription{jid = AJID, type = Sub, subid = SubId}] end, Subs), - {result, #pubsub{subscriptions = {Node, Entities}}}; + {result, #pubsub_owner{subscriptions = {Node, Entities}}}; Error -> Error end. @@ -2623,6 +2632,8 @@ get_subscriptions_for_send_last(Host, PType, sql, JID, LJID, BJID) -> get_subscriptions_for_send_last(_Host, _PType, _, _JID, _LJID, _BJID) -> []. +-spec set_subscriptions(host(), binary(), jid(), [ps_subscription()]) -> + {result, undefined} | {error, error()}. set_subscriptions(Host, Node, From, Entities) -> Owner = jid:tolower(jid:remove_resource(From)), Notify = fun(#ps_subscription{jid = JID, type = Sub}) -> @@ -3152,7 +3163,7 @@ user_resource(_, _, Resource) -> %%%%%%% Configuration handling -spec get_configure(host(), binary(), binary(), jid(), - binary()) -> {error, error()} | {result, pubsub()}. + binary()) -> {error, error()} | {result, pubsub_owner()}. get_configure(Host, ServerHost, Node, From, Lang) -> Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) -> case node_call(Host, Type, get_affiliation, [Nidx, From]) of @@ -3171,7 +3182,7 @@ get_configure(Host, ServerHost, Node, From, Lang) -> Other -> Other end. --spec get_default(host(), binary(), jid(), binary()) -> {result, pubsub()}. +-spec get_default(host(), binary(), jid(), binary()) -> {result, pubsub_owner()}. get_default(Host, Node, _From, Lang) -> Type = select_type(Host, Host, Node), Options = node_options(Host, Type), @@ -3371,6 +3382,8 @@ get_configure_xfields(_Type, Options, Lang, Groups) -> %% -spec set_configure(host(), binary(), jid(), [{binary(), [binary()]}], binary()) -> {result, undefined} | {error, error()}. +set_configure(_Host, <<>>, _From, _Config, _Lang) -> + {error, extended_error(xmpp:err_bad_request(), err_nodeid_required())}; set_configure(Host, Node, From, Config, Lang) -> Action = fun(#pubsub_node{options = Options, type = Type, id = Nidx} = N) -> @@ -3664,26 +3677,28 @@ select_type(ServerHost, Host, Node, Type) -> select_type(ServerHost, Host, Node) -> select_type(ServerHost, Host, Node, hd(plugins(Host))). +-spec feature(binary()) -> binary(). feature(<<"rsm">>) -> ?NS_RSM; feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>. +-spec features() -> [binary()]. features() -> [% see plugin "access-authorize", % OPTIONAL - <<"access-open">>, % OPTIONAL this relates to access_model option in node_hometree - <<"access-presence">>, % OPTIONAL this relates to access_model option in node_pep - <<"access-whitelist">>, % OPTIONAL - <<"collections">>, % RECOMMENDED - <<"config-node">>, % RECOMMENDED - <<"create-and-configure">>, % RECOMMENDED - <<"item-ids">>, % RECOMMENDED - <<"last-published">>, % RECOMMENDED - <<"member-affiliation">>, % RECOMMENDED - <<"presence-notifications">>, % OPTIONAL - <<"presence-subscribe">>, % RECOMMENDED - <<"publisher-affiliation">>, % RECOMMENDED - <<"publish-only-affiliation">>, % OPTIONAL - <<"retrieve-default">>, - <<"shim">>]. % RECOMMENDED + <<"access-open">>, % OPTIONAL this relates to access_model option in node_hometree + <<"access-presence">>, % OPTIONAL this relates to access_model option in node_pep + <<"access-whitelist">>, % OPTIONAL + <<"collections">>, % RECOMMENDED + <<"config-node">>, % RECOMMENDED + <<"create-and-configure">>, % RECOMMENDED + <<"item-ids">>, % RECOMMENDED + <<"last-published">>, % RECOMMENDED + <<"member-affiliation">>, % RECOMMENDED + <<"presence-notifications">>, % OPTIONAL + <<"presence-subscribe">>, % RECOMMENDED + <<"publisher-affiliation">>, % RECOMMENDED + <<"publish-only-affiliation">>, % OPTIONAL + <<"retrieve-default">>, + <<"shim">>]. % RECOMMENDED % see plugin "retrieve-items", % RECOMMENDED % see plugin "retrieve-subscriptions", % RECOMMENDED @@ -3920,7 +3935,7 @@ err_subid_required() -> err_too_many_subscriptions() -> #ps_error{type = 'too-many-subscriptions'}. --spec err_unsupported(ps_error_feature()) -> ps_error(). +-spec err_unsupported(ps_feature()) -> ps_error(). err_unsupported(Feature) -> #ps_error{type = 'unsupported', feature = Feature}. diff --git a/src/node_dag.erl b/src/node_dag.erl index afb610ca7..45f8ade63 100644 --- a/src/node_dag.erl +++ b/src/node_dag.erl @@ -28,7 +28,7 @@ -author('bjc@kublai.com'). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, @@ -78,8 +78,9 @@ publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> case find_opt(node_type, Options) of collection -> Txt = <<"Publishing items to collection node is not allowed">>, - {error, - ?ERR_EXTENDED(?ERRT_NOT_ALLOWED(?MYLANG, Txt), <<"publish">>)}; + {error, mod_pubsub:extended_error( + xmpp:err_not_allowed(Txt, ?MYLANG), + mod_pubsub:err_unsupported('publish'))}; _ -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) diff --git a/src/node_dispatch.erl b/src/node_dispatch.erl index b3af69cd1..0a72b18d5 100644 --- a/src/node_dispatch.erl +++ b/src/node_dispatch.erl @@ -34,7 +34,7 @@ -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, @@ -94,10 +94,12 @@ delete_node(Nodes) -> subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription, _RosterGroup, _Options) -> - {error, ?ERR_FORBIDDEN}. + {error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(), + mod_pubsub:err_unsupported('subscribe'))}. unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) -> - {error, ?ERR_FORBIDDEN}. + {error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(), + mod_pubsub:err_unsupported('subscribe'))}. publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, PubOpts) -> @@ -118,10 +120,12 @@ remove_extra_items(_Nidx, _MaxItems, ItemIds) -> {result, {ItemIds, []}}. delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) -> - {error, ?ERR_ITEM_NOT_FOUND}. + {error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(), + mod_pubsub:err_unsupported('delete-items'))}. purge_node(_Nidx, _Owner) -> - {error, ?ERR_FORBIDDEN}. + {error, mod_pubsub:extended_error(xmpp:err_feature_not_implemented(), + mod_pubsub:err_unsupported('purge-nodes'))}. get_entity_affiliations(_Host, _Owner) -> {result, []}. diff --git a/src/node_flat.erl b/src/node_flat.erl index 2fb24ee69..2ec9afe54 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -34,7 +34,7 @@ -author('christophe.romain@process-one.net'). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, @@ -107,8 +107,8 @@ features() -> <<"retrieve-items">>, <<"retrieve-subscriptions">>, <<"subscribe">>, + %%<<"subscription-options">>, <<"subscription-notifications">>]. -%%<<"subscription-options">> %% @doc Checks if the current user has the permission to create the requested node %%

    In flat node, any unused node name is allowed. The access parameter is also @@ -196,27 +196,27 @@ subscribe_node(Nidx, Sender, Subscriber, AccessModel, Owner = Affiliation == owner, if not Authorized -> {error, - ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)}; + mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_invalid_jid())}; (Affiliation == outcast) or (Affiliation == publish_only) -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; PendingSubscription -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"pending-subscription">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_pending_subscription())}; (AccessModel == presence) and (not PresenceSubscription) and (not Owner) -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and (not RosterGroup) and (not Owner) -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and (not Whitelisted) and (not Owner) -> {error, - ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)}; + mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; %%ForbiddenAnonymous -> %% % Requesting entity is anonymous - %% {error, ?ERR_FORBIDDEN}; + %% {error, xmpp:err_forbidden()}; true -> %%SubId = pubsub_subscription:add_subscription(Subscriber, Nidx, Options), {NewSub, SubId} = case Subscriptions of @@ -265,17 +265,17 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> if %% Requesting entity is prohibited from unsubscribing entity not Authorized -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; %% Entity did not specify SubId %%SubId == "", ?? -> - %% {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; + %% {error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %% Invalid subscription identifier %%InvalidSubId -> - %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + %% {error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; %% Requesting entity is not a subscriber Subscriptions == [] -> {error, - ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)}; + mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())}; %% Subid supplied, so use that. SubIdExists -> Sub = first_in_list(fun @@ -289,7 +289,7 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> {result, default}; false -> {error, - ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)} + mod_pubsub:extended_error(xmpp:err_unexpected_request(), mod_pubsub:err_not_subscribed())} end; %% Asking to remove all subscriptions to the given node SubId == all -> @@ -302,7 +302,7 @@ unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> %% No subid and more than one possible subscription match. true -> {error, - ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)} + mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())} end. delete_subscriptions(SubKey, Nidx, Subscriptions, SubState) -> @@ -366,7 +366,7 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, or (Affiliation == publisher) or (Affiliation == publish_only)) or (Subscribed == true)) -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; true -> if MaxItems > 0 -> Now = p1_time_compat:timestamp(), @@ -425,7 +425,7 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) -> _ -> false end, if not Allowed -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; true -> case lists:member(ItemId, Items) of true -> @@ -450,9 +450,9 @@ delete_item(Nidx, Publisher, PublishModel, ItemId) -> (_, Res) -> Res end, - {error, ?ERR_ITEM_NOT_FOUND}, States); + {error, xmpp:err_item_not_found()}, States); _ -> - {error, ?ERR_ITEM_NOT_FOUND} + {error, xmpp:err_item_not_found()} end end end. @@ -474,7 +474,7 @@ purge_node(Nidx, Owner) -> States), {result, {default, broadcast}}; _ -> - {error, ?ERR_FORBIDDEN} + {error, xmpp:err_forbidden()} end. %% @doc

    Return the current affiliations for the given user

    @@ -581,7 +581,7 @@ set_subscriptions(Nidx, Owner, Subscription, SubId) -> case Subscription of none -> {error, - ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"not-subscribed">>)}; + mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_not_subscribed())}; _ -> new_subscription(Nidx, Owner, Subscription, SubState) end; @@ -592,7 +592,7 @@ set_subscriptions(Nidx, Owner, Subscription, SubId) -> end; {<<>>, [_ | _]} -> {error, - ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}; + mod_pubsub:extended_error((xmpp:err_bad_request()), mod_pubsub:err_subid_required())}; _ -> case Subscription of none -> unsub_with_subid(Nidx, SubId, SubState); @@ -721,23 +721,23 @@ get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM can_fetch_item(Affiliation, FullSubscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID - %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; + %{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID - %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + %{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, - ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)}; + mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; @@ -750,7 +750,7 @@ get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM get_item(Nidx, ItemId) -> case mnesia:read({pubsub_item, {ItemId, Nidx}}) of [Item] when is_record(Item, pubsub_item) -> {result, Item}; - _ -> {error, ?ERR_ITEM_NOT_FOUND} + _ -> {error, xmpp:err_item_not_found()} end. get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> @@ -762,23 +762,23 @@ get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _Sub Whitelisted = can_fetch_item(Affiliation, Subscriptions), if %%SubId == "", ?? -> %% Entity has multiple subscriptions to the node but does not specify a subscription ID - %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; + %{error, mod_pubsub:extended_error(xmpp:err_bad_request(), "subid-required")}; %%InvalidSubId -> %% Entity is subscribed but specifies an invalid subscription ID - %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + %{error, mod_pubsub:extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; (Affiliation == outcast) or (Affiliation == publish_only) -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; (AccessModel == presence) and not PresenceSubscription -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_presence_subscription_required())}; (AccessModel == roster) and not RosterGroup -> {error, - ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)}; + mod_pubsub:extended_error((xmpp:err_not_authorized()), mod_pubsub:err_not_in_roster_group())}; (AccessModel == whitelist) and not Whitelisted -> {error, - ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)}; + mod_pubsub:extended_error((xmpp:err_not_allowed()), mod_pubsub:err_closed_node())}; (AccessModel == authorize) and not Whitelisted -> - {error, ?ERR_FORBIDDEN}; + {error, xmpp:err_forbidden()}; %%MustPay -> %% % Payment is required for a subscription %% {error, ?ERR_PAYMENT_REQUIRED}; diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 2a468c69a..025f6caa8 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -677,7 +677,7 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex, "where exists ( select count(*) as count1 " "from pubsub_item where nodeid='">>, SNidx, <<"' and modification > pi.modification having count1 = ">>, - IncIndex, <<" );">>]) of + integer_to_binary(IncIndex), <<" );">>]) of {selected, [_], [[O]]} -> [<<"modification">>, <<"'", O/binary, "'">>]; _ -> @@ -699,7 +699,7 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex, end, Query = fun(mssql, _) -> ejabberd_sql:sql_query_t( - [<<"select top ">>, Max, + [<<"select top ">>, integer_to_binary(Max), <<" itemid, publisher, creation, modification, payload " "from pubsub_item where nodeid='">>, SNidx, <<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>, @@ -709,7 +709,8 @@ get_items(Nidx, _From, #rsm_set{max = Max, index = IncIndex, [<<"select itemid, publisher, creation, modification, payload " "from pubsub_item where nodeid='">>, SNidx, <<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>, - AttrName, <<" ">>, Order, <<" limit ">>, Max, <<" ;">>]) + AttrName, <<" ">>, Order, <<" limit ">>, + integer_to_binary(Max), <<" ;">>]) end, case ejabberd_sql:sql_query_t(Query) of {selected, [<<"itemid">>, <<"publisher">>, <<"creation">>, diff --git a/src/nodetree_dag.erl b/src/nodetree_dag.erl index 387d98413..f17f2846d 100644 --- a/src/nodetree_dag.erl +++ b/src/nodetree_dag.erl @@ -30,7 +30,7 @@ -include_lib("stdlib/include/qlc.hrl"). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, @@ -69,13 +69,13 @@ create_node(Key, Node, Type, Owner, Options, Parents) -> Other -> Other end; _ -> - {error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)} + {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)} end. delete_node(Key, Node) -> case find_node(Key, Node) of false -> - {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}; + {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; Record -> lists:foreach(fun (#pubsub_node{options = Opts} = Child) -> NewOpts = remove_config_parent(Node, Opts), @@ -99,7 +99,7 @@ get_node(Host, Node, _From) -> get_node(Host, Node) -> case find_node(Host, Node) of - false -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}; + false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; Record -> Record end. @@ -115,7 +115,7 @@ get_nodes(Key) -> get_parentnodes(Host, Node, _From) -> case find_node(Host, Node) of false -> - {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}; + {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; #pubsub_node{parents = Parents} -> Q = qlc:q([N || #pubsub_node{nodeid = {NHost, NNode}} = N @@ -139,7 +139,7 @@ get_subnodes(Host, <<>>) -> get_subnodes_helper(Host, <<>>); get_subnodes(Host, Node) -> case find_node(Host, Node) of - false -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}; + false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; _ -> get_subnodes_helper(Host, Node) end. @@ -226,13 +226,13 @@ validate_parentage(Key, Owners, [<<>> | T]) -> validate_parentage(Key, Owners, [ParentID | T]) -> case find_node(Key, ParentID) of false -> - {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)}; + {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)}; #pubsub_node{owners = POwners, options = POptions} -> NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions), MutualOwners = [O || O <- Owners, PO <- POwners, O == PO], case {MutualOwners, NodeType} of - {[], _} -> {error, ?ERR_FORBIDDEN}; + {[], _} -> {error, xmpp:err_forbidden()}; {_, collection} -> validate_parentage(Key, Owners, T); - {_, _} -> {error, ?ERR_NOT_ALLOWED} + {_, _} -> {error, xmpp:err_not_allowed()} end end. diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index 69b50ff9f..81972ca3c 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -40,7 +40,7 @@ -include_lib("stdlib/include/qlc.hrl"). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, @@ -76,13 +76,13 @@ get_node(Host, Node, _From) -> get_node(Host, Node) -> case mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> Record; - _ -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)} + _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_node(Nidx) -> case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of [Record] when is_record(Record, pubsub_node) -> Record; - _ -> {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)} + _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_nodes(Host, _From) -> @@ -180,10 +180,10 @@ create_node(Host, Node, Type, Owner, Options, Parents) -> options = Options}), {ok, Nidx}; false -> - {error, ?ERR_FORBIDDEN} + {error, xmpp:err_forbidden()} end; _ -> - {error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)} + {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)} end. delete_node(Host, Node) -> diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index edfdbc1d5..c292c7755 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -40,7 +40,7 @@ -compile([{parse_transform, ejabberd_sql_pt}]). -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -include("ejabberd_sql_pt.hrl"). -export([init/3, terminate/2, options/0, set_node/1, @@ -97,7 +97,7 @@ set_node(Record) when is_record(Record, pubsub_node) -> case Nidx of none -> Txt = <<"Node index not found">>, - {error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, Txt)}; + {error, xmpp:err_internal_server_error(Txt, ?MYLANG)}; _ -> lists:foreach(fun ({Key, Value}) -> SKey = iolist_to_binary(atom_to_list(Key)), @@ -125,9 +125,9 @@ get_node(Host, Node) -> {selected, [RItem]} -> raw_to_node(Host, RItem); {'EXIT', _Reason} -> - {error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)}; + {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; _ -> - {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)} + {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_node(Nidx) -> @@ -139,9 +139,9 @@ get_node(Nidx) -> {selected, [{Host, Node, Parent, Type}]} -> raw_to_node(Host, {Node, Parent, Type, Nidx}); {'EXIT', _Reason} -> - {error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)}; + {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; _ -> - {error, ?ERRT_ITEM_NOT_FOUND(?MYLANG, <<"Node not found">>)} + {error, xmpp:err_item_not_found(<<"Node not found">>, ?MYLANG)} end. get_nodes(Host, _From) -> @@ -249,12 +249,12 @@ create_node(Host, Node, Type, Owner, Options, Parents) -> Other -> Other end; false -> - {error, ?ERR_FORBIDDEN} + {error, xmpp:err_forbidden()} end; {result, _} -> - {error, ?ERRT_CONFLICT(?MYLANG, <<"Node already exists">>)}; + {error, xmpp:err_conflict(<<"Node already exists">>, ?MYLANG)}; {error, db_fail} -> - {error, ?ERRT_INTERNAL_SERVER_ERROR(?MYLANG, <<"Database failure">>)} + {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)} end. delete_node(Host, Node) -> diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl index b910a5e7d..a6f8888a9 100644 --- a/src/pubsub_db_sql.erl +++ b/src/pubsub_db_sql.erl @@ -75,27 +75,27 @@ add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) -> Opts), ok. -subscription_opt_from_sql({<<"DELIVER">>, Value}) -> +subscription_opt_from_sql([<<"DELIVER">>, Value]) -> {deliver, sql_to_boolean(Value)}; -subscription_opt_from_sql({<<"DIGEST">>, Value}) -> +subscription_opt_from_sql([<<"DIGEST">>, Value]) -> {digest, sql_to_boolean(Value)}; -subscription_opt_from_sql({<<"DIGEST_FREQUENCY">>, Value}) -> +subscription_opt_from_sql([<<"DIGEST_FREQUENCY">>, Value]) -> {digest_frequency, sql_to_integer(Value)}; -subscription_opt_from_sql({<<"EXPIRE">>, Value}) -> +subscription_opt_from_sql([<<"EXPIRE">>, Value]) -> {expire, sql_to_timestamp(Value)}; -subscription_opt_from_sql({<<"INCLUDE_BODY">>, Value}) -> +subscription_opt_from_sql([<<"INCLUDE_BODY">>, Value]) -> {include_body, sql_to_boolean(Value)}; %%TODO: might be > than 1 show_values value??. %% need to use compact all in only 1 opt. -subscription_opt_from_sql({<<"SHOW_VALUES">>, Value}) -> +subscription_opt_from_sql([<<"SHOW_VALUES">>, Value]) -> {show_values, Value}; -subscription_opt_from_sql({<<"SUBSCRIPTION_TYPE">>, Value}) -> +subscription_opt_from_sql([<<"SUBSCRIPTION_TYPE">>, Value]) -> {subscription_type, case Value of <<"items">> -> items; <<"nodes">> -> nodes end}; -subscription_opt_from_sql({<<"SUBSCRIPTION_DEPTH">>, Value}) -> +subscription_opt_from_sql([<<"SUBSCRIPTION_DEPTH">>, Value]) -> {subscription_depth, case Value of <<"all">> -> all; diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl index 3ab502184..33c884afb 100644 --- a/src/pubsub_subscription.erl +++ b/src/pubsub_subscription.erl @@ -39,7 +39,7 @@ -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>). @@ -112,30 +112,15 @@ get_options_xform(Lang, Options) -> Keys = [deliver, show_values, subscription_type, subscription_depth], XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys], {result, - #xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, ?NS_XDATA}], - children = - [#xmlel{name = <<"field">>, - attrs = - [{<<"var">>, <<"FORM_TYPE">>}, - {<<"type">>, <<"hidden">>}], - children = - [#xmlel{name = <<"value">>, attrs = [], - children = - [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}] - ++ XFields}}. + #xdata{type = form, + fields = [#xdata_field{type = hidden, + var = <<"FORM_TYPE">>, + values = [?NS_PUBSUB_SUB_OPTIONS]}| + XFields]}}. parse_options_xform(XFields) -> - case fxml:remove_cdata(XFields) of - [#xmlel{name = <<"x">>} = XEl] -> - case jlib:parse_xdata_submit(XEl) of - XData when is_list(XData) -> - Opts = set_xoption(XData, []), - {result, Opts}; - Other -> Other - end; - _ -> {result, []} - end. + Opts = set_xoption(XFields, []), + {result, Opts}. %%==================================================================== %% Internal functions @@ -223,9 +208,17 @@ val_xfield(digest_frequency = Opt, [Val]) -> _ -> Txt = <<"Value of '~s' should be integer">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)} + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)} + end; +val_xfield(expire = Opt, [Val]) -> + case jlib:datetime_string_to_timestamp(Val) of + undefined -> + Txt = <<"Value of '~s' should be datetime string">>, + ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}; + Timestamp -> + Timestamp end; -val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val); val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(show_values, Vals) -> Vals; val_xfield(subscription_type, [<<"items">>]) -> items; @@ -237,7 +230,7 @@ val_xfield(subscription_depth = Opt, [Depth]) -> _ -> Txt = <<"Value of '~s' should be integer">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)} + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)} end. %% Convert XForm booleans to Erlang booleans. @@ -248,10 +241,7 @@ xopt_to_bool(_, <<"true">>) -> true; xopt_to_bool(Option, _) -> Txt = <<"Value of '~s' should be boolean">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}. - --spec get_option_xfield(Lang :: binary(), Key :: atom(), - Options :: mod_pubsub:subOptions()) -> xmlel(). + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}. %% Return a field for an XForm for Key, with data filled in, if %% applicable, from Options. @@ -261,33 +251,22 @@ get_option_xfield(Lang, Key, Options) -> {Type, OptEls} = type_and_options(xfield_type(Key), Lang), Vals = case lists:keysearch(Key, 1, Options) of {value, {_, Val}} -> - [tr_xfield_values(Vals) - || Vals <- xfield_val(Key, Val)]; - false -> [] + [xfield_val(Key, Val)]; + false -> + [] end, - #xmlel{name = <<"field">>, - attrs = - [{<<"var">>, Var}, {<<"type">>, Type}, - {<<"label">>, translate:translate(Lang, Label)}], - children = OptEls ++ Vals}. + #xdata_field{type = Type, var = Var, + label = translate:translate(Lang, Label), + values = Vals, + options = OptEls}. type_and_options({Type, Options}, Lang) -> {Type, [tr_xfield_options(O, Lang) || O <- Options]}; type_and_options(Type, _Lang) -> {Type, []}. tr_xfield_options({Value, Label}, Lang) -> - #xmlel{name = <<"option">>, - attrs = - [{<<"label">>, translate:translate(Lang, Label)}], - children = - [#xmlel{name = <<"value">>, attrs = [], - children = [{xmlcdata, Value}]}]}. - -tr_xfield_values(Value) -> - %% Return the XForm variable name for a subscription option key. - %% Return the XForm variable type for a subscription option key. - #xmlel{name = <<"value">>, attrs = [], - children = [{xmlcdata, Value}]}. + #xdata_option{label = translate:translate(Lang, Label), + value = Value}. xfield_var(deliver) -> ?PUBSUB_DELIVER; %xfield_var(digest) -> ?PUBSUB_DIGEST; @@ -298,24 +277,24 @@ xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH. -xfield_type(deliver) -> <<"boolean">>; -%xfield_type(digest) -> <<"boolean">>; -%xfield_type(digest_frequency) -> <<"text-single">>; -%xfield_type(expire) -> <<"text-single">>; -%xfield_type(include_body) -> <<"boolean">>; +xfield_type(deliver) -> boolean; +%xfield_type(digest) -> boolean; +%xfield_type(digest_frequency) -> 'text-single'; +%xfield_type(expire) -> 'text-single'; +%xfield_type(include_body) -> boolean; xfield_type(show_values) -> - {<<"list-multi">>, + {'list-multi', [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, {<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; xfield_type(subscription_type) -> - {<<"list-single">>, + {'list-single', [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; xfield_type(subscription_depth) -> - {<<"list-single">>, + {'list-single', [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl index 6e598320c..7c0670957 100644 --- a/src/pubsub_subscription_sql.erl +++ b/src/pubsub_subscription_sql.erl @@ -35,7 +35,7 @@ -include("pubsub.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>). @@ -117,30 +117,15 @@ get_options_xform(Lang, Options) -> Keys = [deliver, show_values, subscription_type, subscription_depth], XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys], {result, - #xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, ?NS_XDATA}], - children = - [#xmlel{name = <<"field">>, - attrs = - [{<<"var">>, <<"FORM_TYPE">>}, - {<<"type">>, <<"hidden">>}], - children = - [#xmlel{name = <<"value">>, attrs = [], - children = - [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}] - ++ XFields}}. + #xdata{type = form, + fields = [#xdata_field{type = hidden, + var = <<"FORM_TYPE">>, + values = [?NS_PUBSUB_SUB_OPTIONS]}| + XFields]}}. parse_options_xform(XFields) -> - case fxml:remove_cdata(XFields) of - [#xmlel{name = <<"x">>} = XEl] -> - case jlib:parse_xdata_submit(XEl) of - XData when is_list(XData) -> - Opts = set_xoption(XData, []), - {result, Opts}; - Other -> Other - end; - _ -> {result, []} - end. + Opts = set_xoption(XFields, []), + {result, Opts}. %%==================================================================== %% Internal functions @@ -188,9 +173,17 @@ val_xfield(digest_frequency = Opt, [Val]) -> _ -> Txt = <<"Value of '~s' should be integer">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)} + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)} + end; +val_xfield(expire = Opt, [Val]) -> + case jlib:datetime_string_to_timestamp(Val) of + undefined -> + Txt = <<"Value of '~s' should be datetime string">>, + ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}; + Timestamp -> + Timestamp end; -val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val); val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val); val_xfield(show_values, Vals) -> Vals; val_xfield(subscription_type, [<<"items">>]) -> items; @@ -202,7 +195,7 @@ val_xfield(subscription_depth = Opt, [Depth]) -> _ -> Txt = <<"Value of '~s' should be integer">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Opt])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)} + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)} end. %% Convert XForm booleans to Erlang booleans. @@ -213,7 +206,7 @@ xopt_to_bool(_, <<"true">>) -> true; xopt_to_bool(Option, _) -> Txt = <<"Value of '~s' should be boolean">>, ErrTxt = iolist_to_binary(io_lib:format(Txt, [Option])), - {error, ?ERRT_NOT_ACCEPTABLE(?MYLANG, ErrTxt)}. + {error, xmpp:err_not_acceptable(ErrTxt, ?MYLANG)}. %% Return a field for an XForm for Key, with data filled in, if %% applicable, from Options. @@ -222,34 +215,23 @@ get_option_xfield(Lang, Key, Options) -> Label = xfield_label(Key), {Type, OptEls} = type_and_options(xfield_type(Key), Lang), Vals = case lists:keysearch(Key, 1, Options) of - {value, {_, Val}} -> - [tr_xfield_values(Vals) - || Vals <- xfield_val(Key, Val)]; - false -> [] - end, - #xmlel{name = <<"field">>, - attrs = - [{<<"var">>, Var}, {<<"type">>, Type}, - {<<"label">>, translate:translate(Lang, Label)}], - children = OptEls ++ Vals}. + {value, {_, Val}} -> + [xfield_val(Key, Val)]; + false -> + [] + end, + #xdata_field{type = Type, var = Var, + label = translate:translate(Lang, Label), + values = Vals, + options = OptEls}. type_and_options({Type, Options}, Lang) -> {Type, [tr_xfield_options(O, Lang) || O <- Options]}; type_and_options(Type, _Lang) -> {Type, []}. tr_xfield_options({Value, Label}, Lang) -> - #xmlel{name = <<"option">>, - attrs = - [{<<"label">>, translate:translate(Lang, Label)}], - children = - [#xmlel{name = <<"value">>, attrs = [], - children = [{xmlcdata, Value}]}]}. - -tr_xfield_values(Value) -> - %% Return the XForm variable name for a subscription option key. - %% Return the XForm variable type for a subscription option key. - #xmlel{name = <<"value">>, attrs = [], - children = [{xmlcdata, Value}]}. + #xdata_option{label = translate:translate(Lang, Label), + value = Value}. xfield_var(deliver) -> ?PUBSUB_DELIVER; %xfield_var(digest) -> ?PUBSUB_DIGEST; @@ -260,26 +242,26 @@ xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH. -xfield_type(deliver) -> <<"boolean">>; -%xfield_type(digest) -> <<"boolean">>; -%xfield_type(digest_frequency) -> <<"text-single">>; -%xfield_type(expire) -> <<"text-single">>; -%xfield_type(include_body) -> <<"boolean">>; +xfield_type(deliver) -> boolean; +%xfield_type(digest) -> boolean; +%xfield_type(digest_frequency) -> 'text-single'; +%xfield_type(expire) -> 'text-single'; +%xfield_type(include_body) -> boolean; xfield_type(show_values) -> - {<<"list-multi">>, - [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, - {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, - {<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, - {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, - {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; + {'list-multi', + [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, + {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, + {<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, + {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, + {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; xfield_type(subscription_type) -> - {<<"list-single">>, - [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, - {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; + {'list-single', + [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, + {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; xfield_type(subscription_depth) -> - {<<"list-single">>, - [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, - {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. + {'list-single', + [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, + {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. %% Return the XForm variable label for a subscription option key. xfield_label(deliver) -> ?DELIVER_LABEL; diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index 445d9a716..a11c8dd76 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -1961,27 +1961,27 @@ 'presence-subscription-required' | 'subid-required' | 'too-many-subscriptions' | 'unsupported' | 'unsupported-access-model'. --type ps_error_feature() :: 'access-authorize' | 'access-open' | - 'access-presence' | 'access-roster' | - 'access-whitelist' | 'auto-create' | - 'auto-subscribe' | 'collections' | 'config-node' | - 'create-and-configure' | 'create-nodes' | - 'delete-items' | 'delete-nodes' | - 'filtered-notifications' | 'get-pending' | - 'instant-nodes' | 'item-ids' | 'last-published' | - 'leased-subscription' | 'manage-subscriptions' | - 'member-affiliation' | 'meta-data' | - 'modify-affiliations' | 'multi-collection' | - 'multi-subscribe' | 'outcast-affiliation' | - 'persistent-items' | 'presence-notifications' | - 'presence-subscribe' | 'publish' | - 'publish-options' | 'publish-only-affiliation' | - 'publisher-affiliation' | 'purge-nodes' | - 'retract-items' | 'retrieve-affiliations' | - 'retrieve-default' | 'retrieve-items' | - 'retrieve-subscriptions' | 'subscribe' | - 'subscription-options' | 'subscription-notifications'. --record(ps_error, {type :: ps_error_type(), feature :: ps_error_feature()}). +-type ps_feature() :: 'access-authorize' | 'access-open' | + 'access-presence' | 'access-roster' | + 'access-whitelist' | 'auto-create' | + 'auto-subscribe' | 'collections' | 'config-node' | + 'create-and-configure' | 'create-nodes' | + 'delete-items' | 'delete-nodes' | + 'filtered-notifications' | 'get-pending' | + 'instant-nodes' | 'item-ids' | 'last-published' | + 'leased-subscription' | 'manage-subscriptions' | + 'member-affiliation' | 'meta-data' | + 'modify-affiliations' | 'multi-collection' | + 'multi-subscribe' | 'outcast-affiliation' | + 'persistent-items' | 'presence-notifications' | + 'presence-subscribe' | 'publish' | + 'publish-options' | 'publish-only-affiliation' | + 'publisher-affiliation' | 'purge-nodes' | + 'retract-items' | 'retrieve-affiliations' | + 'retrieve-default' | 'retrieve-items' | + 'retrieve-subscriptions' | 'subscribe' | + 'subscription-options' | 'subscription-notifications'. +-record(ps_error, {type :: ps_error_type(), feature :: ps_feature()}). -type ps_error() :: #ps_error{}. -xml(pubsub_error_closed_node,