Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/processone/ejabberd.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/pubsub_subscription.erl')
-rw-r--r--src/pubsub_subscription.erl463
1 files changed, 463 insertions, 0 deletions
diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl
new file mode 100644
index 000000000..bb09cdd60
--- /dev/null
+++ b/src/pubsub_subscription.erl
@@ -0,0 +1,463 @@
+%%% ====================================================================
+%%% ``The contents of this file are subject to the Erlang Public License,
+%%% Version 1.1, (the "License"); you may not use this file except in
+%%% compliance with the License. You should have received a copy of the
+%%% Erlang Public License along with this software. If not, it can be
+%%% retrieved via the world wide web at http://www.erlang.org/.
+%%%
+%%% Software distributed under the License is distributed on an "AS IS"
+%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%%% the License for the specific language governing rights and limitations
+%%% under the License.
+%%%
+%%% The Initial Developer of the Original Code is ProcessOne.
+%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
+%%% All Rights Reserved.''
+%%% This software is copyright 2006-2013, ProcessOne.
+%%%
+%%% @author Brian Cully <bjc@kublai.com>
+%%% @version {@vsn}, {@date} {@time}
+%%% @end
+%%% ====================================================================
+
+-module(pubsub_subscription).
+
+-author("bjc@kublai.com").
+
+%% API
+-export([init/0, subscribe_node/3, unsubscribe_node/3,
+ get_subscription/3, set_subscription/4,
+ get_options_xform/2, parse_options_xform/1]).
+
+% Internal function also exported for use in transactional bloc from pubsub plugins
+-export([add_subscription/3, delete_subscription/3,
+ read_subscription/3, write_subscription/4]).
+
+-include("pubsub.hrl").
+
+-include("jlib.hrl").
+
+-define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
+
+-define(PUBSUB_DIGEST, <<"pubsub#digest">>).
+
+-define(PUBSUB_DIGEST_FREQUENCY,
+ <<"pubsub#digest_frequency">>).
+
+-define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
+
+-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
+
+-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
+
+-define(PUBSUB_SUBSCRIPTION_TYPE,
+ <<"pubsub#subscription_type">>).
+
+-define(PUBSUB_SUBSCRIPTION_DEPTH,
+ <<"pubsub#subscription_depth">>).
+
+-define(DELIVER_LABEL,
+ <<"Whether an entity wants to receive or "
+ "disable notifications">>).
+
+-define(DIGEST_LABEL,
+ <<"Whether an entity wants to receive digests "
+ "(aggregations) of notifications or all "
+ "notifications individually">>).
+
+-define(DIGEST_FREQUENCY_LABEL,
+ <<"The minimum number of milliseconds between "
+ "sending any two notification digests">>).
+
+-define(EXPIRE_LABEL,
+ <<"The DateTime at which a leased subscription "
+ "will end or has ended">>).
+
+-define(INCLUDE_BODY_LABEL,
+ <<"Whether an entity wants to receive an "
+ "XMPP message body in addition to the "
+ "payload format">>).
+
+-define(SHOW_VALUES_LABEL,
+ <<"The presence states for which an entity "
+ "wants to receive notifications">>).
+
+-define(SUBSCRIPTION_TYPE_LABEL,
+ <<"Type of notification to receive">>).
+
+-define(SUBSCRIPTION_DEPTH_LABEL,
+ <<"Depth from subscription for which to "
+ "receive notifications">>).
+
+-define(SHOW_VALUE_AWAY_LABEL,
+ <<"XMPP Show Value of Away">>).
+
+-define(SHOW_VALUE_CHAT_LABEL,
+ <<"XMPP Show Value of Chat">>).
+
+-define(SHOW_VALUE_DND_LABEL,
+ <<"XMPP Show Value of DND (Do Not Disturb)">>).
+
+-define(SHOW_VALUE_ONLINE_LABEL,
+ <<"Mere Availability in XMPP (No Show Value)">>).
+
+-define(SHOW_VALUE_XA_LABEL,
+ <<"XMPP Show Value of XA (Extended Away)">>).
+
+-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL,
+ <<"Receive notification of new items only">>).
+
+-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL,
+ <<"Receive notification of new nodes only">>).
+
+-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL,
+ <<"Receive notification from direct child "
+ "nodes only">>).
+
+-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL,
+ <<"Receive notification from all descendent "
+ "nodes">>).
+
+%%====================================================================
+%% API
+%%====================================================================
+init() -> ok = create_table().
+
+subscribe_node(JID, NodeID, Options) ->
+ case catch mnesia:sync_dirty(fun add_subscription/3,
+ [JID, NodeID, Options])
+ of
+ {'EXIT', {aborted, Error}} -> Error;
+ {error, Error} -> {error, Error};
+ Result -> {result, Result}
+ end.
+
+unsubscribe_node(JID, NodeID, SubID) ->
+ case catch mnesia:sync_dirty(fun delete_subscription/3,
+ [JID, NodeID, SubID])
+ of
+ {'EXIT', {aborted, Error}} -> Error;
+ {error, Error} -> {error, Error};
+ Result -> {result, Result}
+ end.
+
+get_subscription(JID, NodeID, SubID) ->
+ case catch mnesia:sync_dirty(fun read_subscription/3,
+ [JID, NodeID, SubID])
+ of
+ {'EXIT', {aborted, Error}} -> Error;
+ {error, Error} -> {error, Error};
+ Result -> {result, Result}
+ end.
+
+set_subscription(JID, NodeID, SubID, Options) ->
+ case catch mnesia:sync_dirty(fun write_subscription/4,
+ [JID, NodeID, SubID, Options])
+ of
+ {'EXIT', {aborted, Error}} -> Error;
+ {error, Error} -> {error, Error};
+ Result -> {result, Result}
+ end.
+
+
+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}}.
+
+parse_options_xform(XFields) ->
+ case xml: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.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+create_table() ->
+ case mnesia:create_table(pubsub_subscription,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, pubsub_subscription)},
+ {type, set}])
+ of
+ {atomic, ok} -> ok;
+ {aborted, {already_exists, _}} -> ok;
+ Other -> Other
+ end.
+
+-spec(add_subscription/3 ::
+(
+ _JID :: ljid(),
+ _NodeID :: mod_pubsub:nodeIdx(),
+ Options :: [] | mod_pubsub:subOptions())
+ -> SubId :: mod_pubsub:subId()
+).
+
+add_subscription(_JID, _NodeID, []) -> make_subid();
+add_subscription(_JID, _NodeID, Options) ->
+ SubID = make_subid(),
+ mnesia:write(#pubsub_subscription{subid = SubID,
+ options = Options}),
+ SubID.
+
+-spec(delete_subscription/3 ::
+(
+ _JID :: _,
+ _NodeID :: _,
+ SubId :: mod_pubsub:subId())
+ -> ok
+).
+
+delete_subscription(_JID, _NodeID, SubID) ->
+ mnesia:delete({pubsub_subscription, SubID}).
+
+-spec(read_subscription/3 ::
+(
+ _JID :: ljid(),
+ _NodeID :: _,
+ SubID :: mod_pubsub:subId())
+ -> mod_pubsub:pubsubSubscription()
+ | {error, notfound}
+).
+
+read_subscription(_JID, _NodeID, SubID) ->
+ case mnesia:read({pubsub_subscription, SubID}) of
+ [Sub] -> Sub;
+ _ -> {error, notfound}
+ end.
+
+-spec(write_subscription/4 ::
+(
+ _JID :: ljid(),
+ _NodeID :: _,
+ SubID :: mod_pubsub:subId(),
+ Options :: mod_pubsub:subOptions())
+ -> ok
+).
+
+write_subscription(_JID, _NodeID, SubID, Options) ->
+ mnesia:write(#pubsub_subscription{subid = SubID,
+ options = Options}).
+
+-spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()).
+make_subid() ->
+ {T1, T2, T3} = now(),
+ iolist_to_binary(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
+
+%%
+%% Subscription XForm processing.
+%%
+
+%% Return processed options, with types converted and so forth, using
+%% Opts as defaults.
+set_xoption([], Opts) -> Opts;
+set_xoption([{Var, Value} | T], Opts) ->
+ NewOpts = case var_xfield(Var) of
+ {error, _} -> Opts;
+ Key ->
+ Val = val_xfield(Key, Value),
+ lists:keystore(Key, 1, Opts, {Key, Val})
+ end,
+ set_xoption(T, NewOpts).
+
+%% Return the options list's key for an XForm var.
+%% Convert Values for option list's Key.
+var_xfield(?PUBSUB_DELIVER) -> deliver;
+var_xfield(?PUBSUB_DIGEST) -> digest;
+var_xfield(?PUBSUB_DIGEST_FREQUENCY) ->
+ digest_frequency;
+var_xfield(?PUBSUB_EXPIRE) -> expire;
+var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
+var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
+var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) ->
+ subscription_type;
+var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
+ subscription_depth;
+var_xfield(_) -> {error, badarg}.
+
+val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
+%val_xfield(digest, [Val]) -> xopt_to_bool(Val);
+%val_xfield(digest_frequency, [Val]) ->
+% jlib:binary_to_integer(Val);
+%val_xfield(expire, [Val]) ->
+% jlib:datetime_string_to_timestamp(Val);
+%val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
+val_xfield(show_values, Vals) -> Vals;
+val_xfield(subscription_type, [<<"items">>]) -> items;
+val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
+val_xfield(subscription_depth, [<<"all">>]) -> all;
+val_xfield(subscription_depth, [Depth]) ->
+ case catch jlib:binary_to_integer(Depth) of
+ N when is_integer(N) -> N;
+ _ -> {error, ?ERR_NOT_ACCEPTABLE}
+ end.
+
+%% Convert XForm booleans to Erlang booleans.
+xopt_to_bool(<<"0">>) -> false;
+xopt_to_bool(<<"1">>) -> true;
+xopt_to_bool(<<"false">>) -> false;
+xopt_to_bool(<<"true">>) -> true;
+xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
+
+-spec(get_option_xfield/3 ::
+(
+ Lang :: binary(),
+ Key :: atom(),
+ Options :: mod_pubsub:subOptions())
+ -> xmlel()
+).
+
+%% Return a field for an XForm for Key, with data filled in, if
+%% applicable, from Options.
+get_option_xfield(Lang, Key, Options) ->
+ Var = xfield_var(Key),
+ 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}.
+
+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}]}.
+
+-spec(xfield_var/1 ::
+(
+ Var :: 'deliver'
+% | 'digest'
+% | 'digest_frequency'
+% | 'expire'
+% | 'include_body'
+ | 'show_values'
+ | 'subscription_type'
+ | 'subscription_depth')
+ -> binary()
+).
+
+xfield_var(deliver) -> ?PUBSUB_DELIVER;
+%xfield_var(digest) -> ?PUBSUB_DIGEST;
+%xfield_var(digest_frequency) ->
+% ?PUBSUB_DIGEST_FREQUENCY;
+%xfield_var(expire) -> ?PUBSUB_EXPIRE;
+%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
+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(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}]};
+xfield_type(subscription_type) ->
+ {<<"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}]}.
+
+%% Return the XForm variable label for a subscription option key.
+xfield_label(deliver) -> ?DELIVER_LABEL;
+%xfield_label(digest) -> ?DIGEST_LABEL;
+%xfield_label(digest_frequency) ->
+% ?DIGEST_FREQUENCY_LABEL;
+%xfield_label(expire) -> ?EXPIRE_LABEL;
+%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
+xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
+%% Return the XForm value for a subscription option key.
+%% Convert erlang booleans to XForms.
+xfield_label(subscription_type) ->
+ ?SUBSCRIPTION_TYPE_LABEL;
+xfield_label(subscription_depth) ->
+ ?SUBSCRIPTION_DEPTH_LABEL.
+
+-spec(xfield_val/2 ::
+(
+ Field :: 'deliver'
+% | 'digest'
+% | 'digest_frequency'
+% | 'expire'
+% | 'include_body'
+ | 'show_values'
+ | 'subscription_type'
+ | 'subscription_depth',
+ Val :: boolean()
+ | binary()
+ | integer()
+ | [binary()])
+% | erlang:timestamp())
+ -> [binary()]
+).
+
+xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
+%xfield_val(digest, Val) -> [bool_to_xopt(Val)];
+%xfield_val(digest_frequency, Val) ->
+% [iolist_to_binary(integer_to_list(Val))];
+%xfield_val(expire, Val) ->
+% [jlib:now_to_utc_string(Val)];
+%%xfield_val(include_body, Val) -> [bool_to_xopt(Val)];
+xfield_val(show_values, Val) -> Val;
+xfield_val(subscription_type, items) -> [<<"items">>];
+xfield_val(subscription_type, nodes) -> [<<"nodes">>];
+xfield_val(subscription_depth, all) -> [<<"all">>];
+xfield_val(subscription_depth, N) ->
+ [iolist_to_binary(integer_to_list(N))].
+
+
+bool_to_xopt(true) -> <<"true">>;
+bool_to_xopt(false) -> <<"false">>.