1 local pubsub
= require
"util.pubsub";
2 local st
= require
"util.stanza";
3 local jid_bare
= require
"util.jid".bare
;
4 local usermanager
= require
"core.usermanager";
5 local new_id
= require
"util.id".medium
;
7 local xmlns_pubsub
= "http://jabber.org/protocol/pubsub";
8 local xmlns_pubsub_event
= "http://jabber.org/protocol/pubsub#event";
9 local xmlns_pubsub_owner
= "http://jabber.org/protocol/pubsub#owner";
11 local autocreate_on_publish
= module
:get_option_boolean("autocreate_on_publish", false);
12 local autocreate_on_subscribe
= module
:get_option_boolean("autocreate_on_subscribe", false);
13 local pubsub_disco_name
= module
:get_option_string("name", "Prosody PubSub Service");
14 local expose_publisher
= module
:get_option_boolean("expose_publisher", false)
18 local lib_pubsub
= module
:require
"pubsub";
20 module
:depends("disco");
21 module
:add_identity("pubsub", "service", pubsub_disco_name
);
22 module
:add_feature("http://jabber.org/protocol/pubsub");
24 function handle_pubsub_iq(event
)
25 return lib_pubsub
.handle_pubsub_iq(event
, service
);
28 -- An itemstore supports the following methods:
29 -- items(): iterator over (id, item)
30 -- get(id): return item with id
31 -- set(id, item): set id to item
32 -- clear(): clear all items
33 -- resize(n): set new limit and trim oldest items
34 -- tail(): return the latest item
36 -- A nodestore supports the following methods:
37 -- set(node_name, node_data)
39 -- users(): iterator over (node_name)
42 local node_store
= module
:open_store(module
.name
.."_nodes");
44 local function create_simple_itemstore(node_config
, node_name
)
45 local archive
= module
:open_store("pubsub_"..node_name
, "archive");
46 return lib_pubsub
.archive_itemstore(archive
, node_config
, nil, node_name
);
49 function simple_broadcast(kind
, node
, jids
, item
, actor
, node_obj
)
51 if node_obj
.config
["notify_"..kind
] == false then
55 if kind
== "retract" then
56 kind
= "items"; -- XEP-0060 signals retraction in an <items> container
60 item
= st
.clone(item
);
61 item
.attr
.xmlns
= nil; -- Clear the pubsub namespace
62 if kind
== "items" then
63 if node_obj
and node_obj
.config
.include_payload
== false then
64 item
:maptags(function () return nil; end);
66 if expose_publisher
and actor
then
67 item
.attr
.publisher
= actor
73 local msg_type
= node_obj
and node_obj
.config
.message_type
or "headline";
74 local message
= st
.message({ from
= module
.host
, type = msg_type
, id
= id
})
75 :tag("event", { xmlns
= xmlns_pubsub_event
})
76 :tag(kind
, { node
= node
})
79 message
:add_child(item
);
83 -- Compose a sensible textual representation of at least Atom payloads
84 if item
and item
.tags
[1] then
85 local payload
= item
.tags
[1];
86 summary
= module
:fire_event("pubsub-summary/"..payload
.attr
.xmlns
, {
87 kind
= kind
, node
= node
, jids
= jids
, actor
= actor
, item
= item
, payload
= payload
,
91 for jid
, options
in pairs(jids
) do
92 local new_stanza
= st
.clone(message
);
93 if summary
and type(options
) == "table" and options
["pubsub#include_body"] then
94 new_stanza
:body(summary
);
96 new_stanza
.attr
.to
= jid
;
97 module
:send(new_stanza
);
101 local max_max_items
= module
:get_option_number("pubsub_max_items", 256);
102 function check_node_config(node
, actor
, new_config
) -- luacheck: ignore 212/actor 212/node
103 if (new_config
["max_items"] or 1) > max_max_items
then
106 if new_config
["access_model"] ~= "whitelist" and new_config
["access_model"] ~= "open" then
112 function is_item_stanza(item
)
113 return st
.is_stanza(item
) and item
.attr
.xmlns
== xmlns_pubsub
and item
.name
== "item";
116 module
:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event
)
117 local payload
= event
.payload
;
118 local title
= payload
:get_child_text("title");
119 local summary
= payload
:get_child_text("summary");
120 if not summary
and title
then
121 local author
= payload
:find("author/name#");
124 summary
= author
.. " posted " .. summary
;
130 module
:hook("iq/host/"..xmlns_pubsub
..":pubsub", handle_pubsub_iq
);
131 module
:hook("iq/host/"..xmlns_pubsub_owner
..":pubsub", handle_pubsub_iq
);
133 local function add_disco_features_from_service(service
) --luacheck: ignore 431/service
134 for feature
in lib_pubsub
.get_feature_set(service
) do
135 module
:add_feature(xmlns_pubsub
.."#"..feature
);
139 module
:hook("host-disco-info-node", function (event
)
140 return lib_pubsub
.handle_disco_info_node(event
, service
);
143 module
:hook("host-disco-items-node", function (event
)
144 return lib_pubsub
.handle_disco_items_node(event
, service
);
148 module
:hook("host-disco-items", function (event
)
149 local stanza
, reply
= event
.stanza
, event
.reply
;
150 local ok
, ret
= service
:get_nodes(stanza
.attr
.from
);
154 for node
, node_obj
in pairs(ret
) do
155 reply
:tag("item", { jid
= module
.host
, node
= node
, name
= node_obj
.config
.title
}):up();
159 local admin_aff
= module
:get_option_string("default_admin_affiliation", "owner");
160 local function get_affiliation(jid
)
161 local bare_jid
= jid_bare(jid
);
162 if bare_jid
== module
.host
or usermanager
.is_admin(bare_jid
, module
.host
) then
167 function get_service()
171 function set_service(new_service
)
172 service
= new_service
;
173 module
.environment
.service
= service
;
174 add_disco_features_from_service(service
);
177 function module
.save()
178 return { service
= service
};
181 function module
.restore(data
)
182 set_service(data
.service
);
185 function module
.load()
186 if module
.reloading
then return; end
188 set_service(pubsub
.new({
189 autocreate_on_publish
= autocreate_on_publish
;
190 autocreate_on_subscribe
= autocreate_on_subscribe
;
192 nodestore
= node_store
;
193 itemstore
= create_simple_itemstore
;
194 broadcaster
= simple_broadcast
;
195 itemcheck
= is_item_stanza
;
196 check_node_config
= check_node_config
;
197 get_affiliation
= get_affiliation
;
199 normalize_jid
= jid_bare
;