1 |
|
%%% ==================================================================== |
2 |
|
%%% ``The contents of this file are subject to the Erlang Public License, |
3 |
|
%%% Version 1.1, (the "License"); you may not use this file except in |
4 |
|
%%% compliance with the License. You should have received a copy of the |
5 |
|
%%% Erlang Public License along with this software. If not, it can be |
6 |
|
%%% retrieved via the world wide web at http://www.erlang.org/. |
7 |
|
%%% |
8 |
|
%%% |
9 |
|
%%% Software distributed under the License is distributed on an "AS IS" |
10 |
|
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See |
11 |
|
%%% the License for the specific language governing rights and limitations |
12 |
|
%%% under the License. |
13 |
|
%%% |
14 |
|
%%% |
15 |
|
%%% The Initial Developer of the Original Code is ProcessOne. |
16 |
|
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne |
17 |
|
%%% All Rights Reserved.'' |
18 |
|
%%% This software is copyright 2006-2015, ProcessOne. |
19 |
|
%%% |
20 |
|
%%% @copyright 2006-2015 ProcessOne |
21 |
|
%%% @author Christophe Romain <christophe.romain@process-one.net> |
22 |
|
%%% [http://www.process-one.net/] |
23 |
|
%%% @end |
24 |
|
%%% ==================================================================== |
25 |
|
|
26 |
|
-module(node_pep). |
27 |
|
-behaviour(gen_pubsub_node). |
28 |
|
-author('christophe.romain@process-one.net'). |
29 |
|
|
30 |
|
-include("mongoose.hrl"). |
31 |
|
-include("pubsub.hrl"). |
32 |
|
-include("jlib.hrl"). |
33 |
|
|
34 |
|
%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin. |
35 |
|
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p> |
36 |
|
|
37 |
|
-export([based_on/0, init/3, terminate/2, options/0, features/0, |
38 |
|
create_node_permission/6, delete_node/1, |
39 |
|
unsubscribe_node/4, node_to_path/1, |
40 |
|
get_entity_affiliations/2, get_entity_affiliations/3, |
41 |
|
get_entity_subscriptions/2, get_entity_subscriptions/4, |
42 |
|
should_delete_when_owner_removed/0, check_publish_options/2 |
43 |
|
]). |
44 |
|
|
45 |
|
-ignore_xref([get_entity_affiliations/3, get_entity_subscriptions/4, check_publish_options/2]). |
46 |
|
|
47 |
84 |
based_on() -> node_flat. |
48 |
|
|
49 |
|
init(Host, ServerHost, Opts) -> |
50 |
4 |
node_flat:init(Host, ServerHost, Opts), |
51 |
4 |
complain_if_modcaps_disabled(ServerHost), |
52 |
4 |
ok. |
53 |
|
|
54 |
|
terminate(Host, ServerHost) -> |
55 |
4 |
node_flat:terminate(Host, ServerHost), |
56 |
4 |
ok. |
57 |
|
|
58 |
|
options() -> |
59 |
20 |
[{deliver_payloads, true}, |
60 |
|
{notify_config, false}, |
61 |
|
{notify_delete, false}, |
62 |
|
{notify_retract, false}, |
63 |
|
{purge_offline, false}, |
64 |
|
{persist_items, true}, |
65 |
|
{max_items, 1}, |
66 |
|
{subscribe, true}, |
67 |
|
{access_model, presence}, |
68 |
|
{roster_groups_allowed, []}, |
69 |
|
{publish_model, publishers}, |
70 |
|
{notification_type, headline}, |
71 |
|
{max_payload_size, ?MAX_PAYLOAD_SIZE}, |
72 |
|
{send_last_published_item, on_sub_and_presence}, |
73 |
|
{deliver_notifications, true}, |
74 |
|
{presence_based_delivery, true}]. |
75 |
|
|
76 |
|
features() -> |
77 |
204 |
[<<"create-nodes">>, |
78 |
|
<<"auto-create">>, |
79 |
|
<<"auto-subscribe">>, |
80 |
|
<<"delete-nodes">>, |
81 |
|
<<"delete-items">>, |
82 |
|
<<"filtered-notifications">>, |
83 |
|
<<"modify-affiliations">>, |
84 |
|
<<"outcast-affiliation">>, |
85 |
|
<<"persistent-items">>, |
86 |
|
<<"publish">>, |
87 |
|
<<"publish-options">>, |
88 |
|
<<"purge-nodes">>, |
89 |
|
<<"retract-items">>, |
90 |
|
<<"retrieve-affiliations">>, |
91 |
|
<<"retrieve-items">>, |
92 |
|
<<"retrieve-subscriptions">>, |
93 |
|
<<"subscribe">>]. |
94 |
|
|
95 |
|
-spec check_publish_options(#{binary() => [binary()]} | invalid_form, #{binary() => [binary()]}) -> |
96 |
|
boolean(). |
97 |
|
check_publish_options(invalid_form, _) -> |
98 |
1 |
true; |
99 |
|
check_publish_options(PublishOptions, NodeOptions) -> |
100 |
22 |
F = fun(Key, Value) -> |
101 |
20 |
case string:split(Key, "#") of |
102 |
|
[<<"pubsub">>, Key2] -> |
103 |
19 |
compare_values(Value, maps:get(Key2, NodeOptions, null)); |
104 |
1 |
_ -> true |
105 |
|
end |
106 |
|
end, |
107 |
22 |
maps:size(maps:filter(F, PublishOptions)) =/= 0. |
108 |
|
|
109 |
|
-spec compare_values([binary()], [binary()] | null) -> boolean(). |
110 |
|
compare_values(_, null) -> |
111 |
1 |
true; |
112 |
|
compare_values(Value1, Value2) -> |
113 |
18 |
lists:sort(Value1) =/= lists:sort(Value2). |
114 |
|
|
115 |
|
create_node_permission(Host, _ServerHost, _Node, _ParentNode, |
116 |
|
#jid{ luser = <<>>, lserver = Host, lresource = <<>> }, _Access) -> |
117 |
:-( |
{result, true}; % pubsub service always allowed |
118 |
|
create_node_permission(Host, ServerHost, _Node, _ParentNode, |
119 |
|
#jid{ luser = User, lserver = Server } = Owner, Access) -> |
120 |
20 |
{ok, HostType} = mongoose_domain_api:get_domain_host_type(ServerHost), |
121 |
20 |
case acl:match_rule(HostType, ServerHost, Access, Owner) of |
122 |
|
allow -> |
123 |
20 |
case Host of |
124 |
20 |
{User, Server, _} -> {result, true}; |
125 |
:-( |
_ -> {result, false} |
126 |
|
end; |
127 |
|
_ -> |
128 |
:-( |
{result, false} |
129 |
|
end. |
130 |
|
|
131 |
|
delete_node(Nodes) -> |
132 |
4 |
{result, {_, _, Result}} = node_flat:delete_node(Nodes), |
133 |
4 |
{result, {[], Result}}. |
134 |
|
|
135 |
|
unsubscribe_node(Nidx, Sender, Subscriber, SubId) -> |
136 |
:-( |
case node_flat:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of |
137 |
:-( |
{error, Error} -> {error, Error}; |
138 |
:-( |
{result, _} -> {result, []} |
139 |
|
end. |
140 |
|
|
141 |
|
get_entity_affiliations(Host, #jid{ lserver = D } = Owner) -> |
142 |
:-( |
get_entity_affiliations(Host, D, jid:to_lower(Owner)); |
143 |
|
get_entity_affiliations(Host, {_, D, _} = Owner) -> |
144 |
55 |
get_entity_affiliations(Host, D, Owner). |
145 |
|
|
146 |
|
get_entity_affiliations(Host, D, Owner) -> |
147 |
55 |
{ok, States} = mod_pubsub_db_backend:get_states_by_bare(Owner), |
148 |
55 |
HT = mod_pubsub:host_to_host_type(Host), |
149 |
55 |
NodeTree = mod_pubsub:tree(HT), |
150 |
55 |
Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) -> |
151 |
23 |
case gen_pubsub_nodetree:get_node(NodeTree, N) of |
152 |
14 |
#pubsub_node{nodeid = {{_, D, _}, _}} = Node -> [{Node, A} | Acc]; |
153 |
9 |
_ -> Acc |
154 |
|
end |
155 |
|
end, |
156 |
|
[], States), |
157 |
55 |
{result, Reply}. |
158 |
|
|
159 |
|
get_entity_subscriptions(Host, #jid{ lserver = D, lresource = R } = Owner) -> |
160 |
58 |
get_entity_subscriptions(Host, D, R, Owner); |
161 |
|
get_entity_subscriptions(Host, {_, D, R} = Owner) -> |
162 |
:-( |
get_entity_subscriptions(Host, D, R, Owner). |
163 |
|
|
164 |
|
get_entity_subscriptions(Host, D, R, Owner) -> |
165 |
58 |
LOwner = jid:to_lower(Owner), |
166 |
58 |
States = case R of |
167 |
|
<<>> -> |
168 |
3 |
{ok, States0} = mod_pubsub_db_backend:get_states_by_lus(LOwner), |
169 |
3 |
States0; |
170 |
|
_ -> |
171 |
55 |
{ok, States0} = mod_pubsub_db_backend:get_states_by_bare_and_full(LOwner), |
172 |
55 |
States0 |
173 |
|
end, |
174 |
58 |
HT = mod_pubsub:host_to_host_type(Host), |
175 |
58 |
NodeTree = mod_pubsub:tree(HT), |
176 |
58 |
Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) -> |
177 |
1 |
case gen_pubsub_nodetree:get_node(NodeTree, N) of |
178 |
|
#pubsub_node{nodeid = {{_, D, _}, _}} = Node -> |
179 |
1 |
accumulate_entity_subscriptions(J, Node, Ss, Acc); |
180 |
|
_ -> |
181 |
:-( |
Acc |
182 |
|
end |
183 |
|
end, |
184 |
|
[], States), |
185 |
58 |
{result, Reply}. |
186 |
|
|
187 |
|
accumulate_entity_subscriptions(J, Node, Ss, Acc) -> |
188 |
1 |
lists:foldl(fun({subscribed, SubId}, Acc2) -> |
189 |
:-( |
[{Node, subscribed, SubId, J} | Acc2]; |
190 |
|
({pending, _SubId}, Acc2) -> |
191 |
1 |
[{Node, pending, J} | Acc2]; |
192 |
|
(S, Acc2) -> |
193 |
:-( |
[{Node, S, J} | Acc2] |
194 |
|
end, Acc, Ss). |
195 |
|
|
196 |
|
|
197 |
|
node_to_path(Node) -> |
198 |
20 |
node_flat:node_to_path(Node). |
199 |
|
|
200 |
2 |
should_delete_when_owner_removed() -> true. |
201 |
|
|
202 |
|
%%% |
203 |
|
%%% Internal |
204 |
|
%%% |
205 |
|
|
206 |
|
%% @doc Check mod_caps is enabled, otherwise show warning. |
207 |
|
%% The PEP plugin for mod_pubsub requires mod_caps to be enabled in the host. |
208 |
|
%% Check that the mod_caps module is enabled in that Jabber Host |
209 |
|
%% If not, log a warning message. |
210 |
|
complain_if_modcaps_disabled(ServerHost) -> |
211 |
4 |
?WARNING_MSG_IF( |
212 |
4 |
not gen_mod:is_loaded(ServerHost, mod_caps), |
213 |
|
"The PEP plugin is enabled in mod_pubsub " |
214 |
|
"of host ~p. This plugin requires mod_caps " |
215 |
|
"to be enabled, but it isn't.", |
216 |
:-( |
[ServerHost]). |