25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-10-31 15:21:38 +01:00
xmpp.chapril.org-ejabberd/mod_pubsub_ng/pubsub_hooks.erl

571 lines
24 KiB
Erlang

%%% ====================================================================
%%% ``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.
%%%
%%% @copyright 2006-2013 ProcessOne
%%% @author Karim Gemayel <karim.gemayel@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
%%% @headerfile "pubsub_dev.hrl"
-module(pubsub_hooks).
-author('karim.gemayel@process-one.net').
-compile(export_all).
-include("pubsub_dev.hrl").
-import(pubsub_tools,
[
get_option/2,
get_option/3,
get_value/2,
get_value/3,
set_value/3,
%%
check_access_model/3,
check_access_model/7,
check_publish_model/3,
is_contact_subscribed_to_node_owners/3,
is_contact_in_allowed_roster_groups/2,
has_subscriptions/1
]).
-import(pubsub_db_mnesia,
[
table/2
]).
%% 'node_config#send_last_published_item'
-spec(presence_online/3 ::
(
From :: xmpp_jid:entity_full(),
To :: xmpp_jid:entity_full(),
C2SPid :: pid())
-> 'ok'
).
presence_online(#jid{luser = U, lserver = S, lresource = R} = Jid, Jid, C2SPid) ->
?INFO_MSG("PRESENCE ONLINE From ~p ~n, To ~p ~n, C2SPid ~p ~n",
[Jid, Jid, C2SPid]),
Pubsub_Features = node_flat_dev:pubsub_features(),
case lists:member(<<"last-published">>, Pubsub_Features) of
true ->
spawn(?MODULE, last_published_items,
[_Host = <<"localhost">>, {U,S,R}]);
false ->
ok
end;
%%
presence_online(_, _, _) ->
ok.
%%
-spec(last_published_items/2 ::
(
Host :: xmpp_jid:raw_jid_component_bare(),
Entity :: xmpp_jid:usr_full())
-> 'ok'
).
last_published_items(Host, {U,S,_R} = Entity) ->
Table_Pubsub_State = table('pubsub_state', 'dev'),
Table_Pubsub_Node = table('pubsub_node', 'dev'),
Table_Pubsub_Last_Item = table('pubsub_last_item', 'dev'),
lists:foreach(fun
(State) ->
last_published_items(Host, Entity, Table_Pubsub_Node,
Table_Pubsub_Last_Item, State)
end,
mnesia:dirty_select(Table_Pubsub_State,
[{#pubsub_state_dev{
id = {{U,S,undefined}, '$1'},
affiliation = '$2',
access = '$3',
subscriptions = '$4',
_ = '_'
},
[{'=/=', '$2', 'outcast'},
{'=/=', '$3', 'pending'},
{'=/=', '$4', []}],
['$$']}])).
%%
-spec(last_published_items/5 ::
(
Host :: xmpp_jid:raw_jid_component_bare(),
Entity :: xmpp_jid:usr_full(),
Table_Pubsub_Node :: atom(),
Table_Pubsub_Last_Item :: atom(),
State :: [exmpp_pubsub:nodeIdx() |
'member' | 'owner' | 'publisher' |
_ |
exmpp_pubsub:subscriptions(),... ])
-> 'ok'
).
last_published_items(Host, {_, _, Online_Resource} = Entity,
Table_Pubsub_Node, Table_Pubsub_Last_Item,
[NodeIdx, Affiliation, _Access, Subscriptions]) ->
case mnesia:dirty_index_read(Table_Pubsub_Node, NodeIdx, idx) of
[#pubsub_node_dev{
id = {Pubsub_Host, NodeId},
owners = Node_Owners,
options = Node_Options
}] ->
case get_value(Node_Options, 'send_last_published_item') of
'on_sub_and_presence' ->
case mnesia:dirty_read(Table_Pubsub_Last_Item, NodeIdx) of
[#pubsub_last_item_dev{
id = ItemId,
creation = {DateTime, Publisher},
payload = Payload,
options = Item_Options
}] ->
spawn(pubsub_broadcast, broadcast_publish_last2,
[Host, Pubsub_Host, NodeId, Node_Options,
Node_Owners,
[{ItemId, Item_Options, Payload, Publisher, DateTime}],
Entity, Affiliation,
lists:foldl(fun
({Subscription_State, SubId, Resource, Subscription_Options},
Acc)
when Subscription_State =/= 'pending'
andalso (Resource == undefined
orelse
Resource == Online_Resource) ->
[{Subscription_State, SubId,
Online_Resource, Subscription_Options}
| Acc];
(_Subscription, Acc) ->
Acc
end, [], Subscriptions)]);
_ ->
ok
end;
_ ->
ok
end;
_ ->
ok
end.
%% 'node_config#purge_offline'
%% 'node_config#tempsub'
%% 'subscribe_options#expire'
-spec(presence_offline/3 ::
(
_ :: _,
Jid :: xmpp_jid:entity_full(),
_ :: _)
-> 'ok'
).
presence_offline({_DateTime, _Pid},
#jid{luser = U, lserver = S, lresource = Offline_Resource} = _Jid, _) ->
Host = <<"localhost">>,
Pubsub_Features = node_flat_dev:pubsub_features(),
Subscribe = lists:member(<<"subscribe">>, Pubsub_Features),
Leased_Subscription = lists:member(<<"leased-subscription">>, Pubsub_Features),
Purge_Nodes = lists:member(<<"purge-nodes">>, Pubsub_Features),
Persistent_Items = lists:member(<<"persistent-items">>, Pubsub_Features),
Subscription_Notifications = lists:member(<<"subscription-notifications">>,
Pubsub_Features),
Table_Pubsub_State = table('pubsub_state', 'dev'),
Table_Pubsub_Node = table('pubsub_node', 'dev'),
Online_Resources = case ejabberd_sm:get_user_resources(U, S) of
[] -> false;
_ -> true
end,
lists:foreach(fun
(Pubsub_State) ->
presence_offline(Host,
_Entity = {U,S,undefined},
Table_Pubsub_State,
Pubsub_State,
Table_Pubsub_Node,
Offline_Resource,
Online_Resources,
{Subscribe,
Leased_Subscription,
Purge_Nodes,
Persistent_Items,
Subscription_Notifications})
end,
mnesia:dirty_match_object(Table_Pubsub_State,
#pubsub_state_dev{
id = {{U,S,undefined}, '_'},
_ = '_'
})).
%%
-spec(presence_offline/8 ::
(
Host :: xmpp_jid:raw_jid_component_bare(),
Entity :: xmpp_jid:usr_bare(),
Table_Pubsub_State :: atom(),
Pubsub_State :: mod_pubsub_dev:pubsub_state(),
Table_Pubsub_Node :: atom(),
Offline_Resource :: xmpp_jid:resource_jid(),
Online_Resources :: boolean(),
Pubsub_Features :: {Subscribe :: boolean(),
Leased_Subscription :: boolean(),
Purge_Nodes :: boolean(),
Persistent_Items :: boolean(),
Subscription_Notifications :: boolean()})
-> 'ok'
).
presence_offline(Host, Entity, Table_Pubsub_State, Pubsub_State,
Table_Pubsub_Node, Offline_Resource, Online_Resources,
{Subscribe, Leased_Subscription, Purge_Nodes, Persistent_Items,
Subscription_Notifications}) ->
case
mnesia:dirty_index_read(Table_Pubsub_Node,
Pubsub_State#pubsub_state_dev.nodeidx, idx)
of
[#pubsub_node_dev{id = {Pubsub_Host, NodeId}} = Pubsub_Node] ->
Node_Options = Pubsub_Node#pubsub_node_dev.options,
case
Subscribe == true
andalso
get_value(Node_Options, 'tempsub') == true
of
true ->
case
filter_tempsub_subscriptions(
get_value(Node_Options, 'notify_sub'),
Pubsub_State#pubsub_state_dev.subscriptions,
Offline_Resource, Online_Resources, {[], []})
of
%%
{_Unexpired_Subscriptions, [] = _Expired_Subscriptions} ->
ok;
%%
{[] = _Unexpired_Subscriptions, Expired_Subscriptions}
when Pubsub_State#pubsub_state_dev.affiliation == 'member'
andalso Pubsub_State#pubsub_state_dev.itemids == [] ->
mnesia:dirty_delete_object(Table_Pubsub_State,
Pubsub_State),
Notification_Type = get_value(Node_Options,
'notification_type', 'headline'),
notify_subscriptions(Host, Pubsub_Host,
Entity, NodeId, Notification_Type,
_Recipients = case Subscription_Notifications of
true ->
case
lists:member(Entity,
Pubsub_Node#pubsub_node_dev.owners)
of
true ->
Pubsub_Node#pubsub_node_dev.owners;
false ->
[Entity
| Pubsub_Node#pubsub_node_dev.owners]
end;
false ->
Pubsub_Node#pubsub_node_dev.owners
end,
Expired_Subscriptions),
ok;
%%
{Unexpired_Subscriptions, Expired_Subscriptions} ->
mnesia:dirty_write(Table_Pubsub_State,
Pubsub_State#pubsub_state_dev{
subscriptions = Unexpired_Subscriptions
}),
Notification_Type = get_value(Node_Options,
'notification_type', 'headline'),
notify_subscriptions(Host, Pubsub_Host,
Entity, NodeId, Notification_Type,
_Recipients = case Subscription_Notifications of
true ->
case
lists:member(Entity,
Pubsub_Node#pubsub_node_dev.owners)
of
true ->
Pubsub_Node#pubsub_node_dev.owners;
false ->
[Entity
| Pubsub_Node#pubsub_node_dev.owners]
end;
false ->
Pubsub_Node#pubsub_node_dev.owners
end,
Expired_Subscriptions),
ok
end;
_ ->
case Subscribe == true andalso Leased_Subscription == true of
true ->
case
filter_expired_subscriptions(
get_value(Node_Options, 'notify_sub'),
Pubsub_State#pubsub_state_dev.subscriptions,
Offline_Resource, Online_Resources, {[], []})
of
%%
{_Unexpired_Subscriptions, [] = _Expired_Subscriptions} ->
ok;
%%
{[] = _Unexpired_Subscriptions, Expired_Subscriptions}
when Pubsub_State#pubsub_state_dev.affiliation == 'member' ->
mnesia:dirty_delete_object(Table_Pubsub_State,
Pubsub_State),
Notification_Type = get_value(Node_Options,
'notification_type', 'headline'),
notify_subscriptions(Host, Pubsub_Host,
Entity, NodeId, Notification_Type,
_Recipients = case
Subscription_Notifications
of
true ->
case
lists:member(Entity,
Pubsub_Node#pubsub_node_dev.owners)
of
true ->
Pubsub_Node#pubsub_node_dev.owners;
false ->
[Entity
|Pubsub_Node#pubsub_node_dev.owners]
end;
false ->
Pubsub_Node#pubsub_node_dev.owners
end,
Expired_Subscriptions),
ok;
%%
{Unexpired_Subscriptions, Expired_Subscriptions} ->
mnesia:dirty_write(Table_Pubsub_State,
Pubsub_State#pubsub_state_dev{
subscriptions = Unexpired_Subscriptions
}),
Notification_Type = get_value(Node_Options,
'notification_type', 'headline'),
notify_subscriptions(Host, Pubsub_Host,
Entity, NodeId, Notification_Type,
_Recipients = case
Subscription_Notifications
of
true ->
case
lists:member(Entity,
Pubsub_Node#pubsub_node_dev.owners)
of
true ->
Pubsub_Node#pubsub_node_dev.owners;
false ->
[Entity
| Pubsub_Node#pubsub_node_dev.owners]
end;
false ->
Pubsub_Node#pubsub_node_dev.owners
end,
Expired_Subscriptions),
ok
end;
false ->
ok
end
end;
[] ->
ok
end.
%%
-spec(notify_subscriptions/7 ::
(
Host :: xmpp_jid:raw_jid_component_bare(),
Pubsub_Host :: exmpp_pubsub:host(),
Entity :: xmpp_jid:usr_bare(),
NodeId :: exmpp_pubsub:nodeId(),
Notification_Type :: 'message' | 'headline',
Recipients :: [Entity::xmpp_jid:usr_bare(),...],
Subscriptions :: [Subscription::exmpp_pubsub:subscription(),...])
-> 'ok'
).
notify_subscriptions(Host, Pubsub_Host, {U,S,_} = _Entity, NodeId,
Notification_Type, Recipients, Subscriptions) ->
Pubsub_Component_Jid = jlib:make_jid(<<>>, Pubsub_Host, <<>>),
Jids = [pubsub_tools:make_jid(Recipient) || Recipient <- Recipients],
lists:foreach(fun
({_, SubId, Resource, _}) ->
Subscriber = jlib:jid_to_string({U,S,Resource}),
lists:foreach(fun
(Recipient) ->
spawn(pubsub_broadcast, notify_subscription,
[Host, NodeId, Pubsub_Component_Jid, Recipient,
Notification_Type, {Subscriber, 'none', SubId}])
end, Jids)
end, Subscriptions).
%%
filter_purged_offline_itemids([Publisher_ItemId | Publisher_ItemIds],
Table_Pubsub_Item, NodeIdx, {U,S,R} = _Entity,
{Node_ItemIds, Unpurged_ItemIds, Purged_ItemIds}) ->
filter_purged_offline_itemids(Publisher_ItemIds, Table_Pubsub_Item, NodeIdx,
{U,S,R},
case
mnesia:dirty_index_match_object(Table_Pubsub_Item,
#pubsub_item_dev{
id = {Publisher_ItemId, NodeIdx},
nodeidx = NodeIdx,
_ = '_'
},
nodeidx)
of
[#pubsub_item_dev{creation = {_DateTime, {U,S,_}}} = Pubsub_Item] ->
mnesia:dirty_delete_object(Table_Pubsub_Item, Pubsub_Item);
_ ->
{Node_ItemIds,
[Publisher_ItemId | Unpurged_ItemIds],
Purged_ItemIds}
end).
%%
-spec(filter_tempsub_subscriptions/5 ::
(
Notify_Sub :: boolean() | 'none',
Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Offline_Resource :: xmpp_jid:resource_jid(),
Online_Resources :: boolean(),
Filtered_TempSub_Subscriptions :: {
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
})
-> Filtered_TempSub_Subscriptions :: {
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
}
).
filter_tempsub_subscriptions(_Notify_Sub, [] = _Subscriptions,
_Offline_Resource, _Online_Resources, Filtered_TempSub_Subscriptions) ->
Filtered_TempSub_Subscriptions;
%%
filter_tempsub_subscriptions(Notify_Sub,
[{Subscription_State, SubId, Resource, Subscription_Options} | Subscriptions],
Offline_Resource, Online_Resources,
{Unexpired_Subscriptions, Expired_Subscriptions})
when ((Online_Resources == false
andalso
(Resource == undefined orelse Resource == Offline_Resource))
orelse
(Online_Resources == true
andalso
Resource == Offline_Resource)) ->
filter_tempsub_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
Online_Resources,
{Unexpired_Subscriptions,
_Expired_Subscriptions = case Notify_Sub of
true ->
[{Subscription_State, SubId, Resource, Subscription_Options}
| Expired_Subscriptions];
_ ->
Expired_Subscriptions
end});
%%
filter_tempsub_subscriptions(Notify_Sub, [Subscription | Subscriptions],
Offline_Resource, Online_Resources,
{Unexpired_Subscriptions, Expired_Subscriptions}) ->
filter_tempsub_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
Online_Resources,
{[Subscription | Unexpired_Subscriptions], Expired_Subscriptions}).
%%
-spec(filter_expired_subscriptions/5 ::
(
Notify_Sub :: boolean() | 'none',
Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Offline_Resource :: xmpp_jid:resource_jid(),
Online_Resources :: boolean(),
Filtered_Expired_Subscriptions :: {
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
})
-> Filtered_Expired_Subscriptions :: {
Unexpired_Subscriptions :: [] | exmpp_pubsub:subscriptions(),
Expired_Subscriptions :: [] | exmpp_pubsub:subscriptions()
}
).
filter_expired_subscriptions(_Notify_Sub, [] = _Subscriptions, _Offline_Resource,
_Online_Resources, Filtered_Expired_Subscriptions) ->
Filtered_Expired_Subscriptions;
%%
filter_expired_subscriptions(Notify_Sub,
[{Subscription_State, SubId, Resource, Subscription_Options} | Subscriptions],
Offline_Resource, Online_Resources,
{Unexpired_Subscriptions, Expired_Subscriptions})
when Subscription_Options =/= []
andalso ((Online_Resources == false
andalso
(Resource == undefined orelse Resource == Offline_Resource))
orelse
(Online_Resources == true
andalso
Resource == Offline_Resource)) ->
filter_expired_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
Online_Resources,
case get_value(Subscription_Options, 'expire') of
'presence' ->
{Unexpired_Subscriptions,
_Expired_Subscriptions = case Notify_Sub of
true ->
[{Subscription_State, SubId, Resource, Subscription_Options}
| Expired_Subscriptions];
false ->
Expired_Subscriptions
end};
_ ->
{[{Subscription_State, SubId, Resource, Subscription_Options}
| Unexpired_Subscriptions],
Expired_Subscriptions}
end);
%%
filter_expired_subscriptions(Notify_Sub, [Subscription | Subscriptions],
Offline_Resource, Online_Resources,
{Unexpired_Subscriptions, Expired_Subscriptions}) ->
filter_expired_subscriptions(Notify_Sub, Subscriptions, Offline_Resource,
Online_Resources,
{[Subscription | Unexpired_Subscriptions], Expired_Subscriptions}).
%%
roster_process_item(Roster_Item, Server) ->
spawn(pubsub_groups, monitor_roster_groups, [Server, Roster_Item]),
Roster_Item.
roster_in_subscription(Boolean, User, Server, Jid_Contact,
'subscribed' = _Subscription_Type) ->
spawn(pubsub_groups, monitor_contacts2,
['subscribed', {User, Server, undefined}, Jid_Contact]),
Boolean;
roster_in_subscription(Boolean, _User, _Server, _JID, _SubscriptionType) ->
Boolean.