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
;
6 local storagemanager
= require
"core.storagemanager";
8 local xmlns_pubsub
= "http://jabber.org/protocol/pubsub";
9 local xmlns_pubsub_event
= "http://jabber.org/protocol/pubsub#event";
10 local xmlns_pubsub_owner
= "http://jabber.org/protocol/pubsub#owner";
12 local autocreate_on_publish
= module
:get_option_boolean("autocreate_on_publish", false);
13 local autocreate_on_subscribe
= module
:get_option_boolean("autocreate_on_subscribe", false);
14 local pubsub_disco_name
= module
:get_option_string("name", "Prosody PubSub Service");
15 local expose_publisher
= module
:get_option_boolean("expose_publisher", false)
19 local lib_pubsub
= module
:require
"pubsub";
21 module
:depends("disco");
22 module
:add_identity("pubsub", "service", pubsub_disco_name
);
23 module
:add_feature("http://jabber.org/protocol/pubsub");
25 function handle_pubsub_iq(event
)
26 return lib_pubsub
.handle_pubsub_iq(event
, service
);
29 -- An itemstore supports the following methods:
30 -- items(): iterator over (id, item)
31 -- get(id): return item with id
32 -- set(id, item): set id to item
33 -- clear(): clear all items
34 -- resize(n): set new limit and trim oldest items
35 -- tail(): return the latest item
37 -- A nodestore supports the following methods:
38 -- set(node_name, node_data)
40 -- users(): iterator over (node_name)
43 local node_store
= module
:open_store(module
.name
.."_nodes");
45 local function create_simple_itemstore(node_config
, node_name
)
46 local driver
= storagemanager
.get_driver(module
.host
, "pubsub_data");
47 local archive
= driver
:open("pubsub_"..node_name
, "archive");
48 return lib_pubsub
.archive_itemstore(archive
, node_config
, nil, node_name
);
51 function simple_broadcast(kind
, node
, jids
, item
, actor
, node_obj
)
53 if node_obj
.config
["notify_"..kind
] == false then
57 if kind
== "retract" then
58 kind
= "items"; -- XEP-0060 signals retraction in an <items> container
62 item
= st
.clone(item
);
63 item
.attr
.xmlns
= nil; -- Clear the pubsub namespace
64 if kind
== "items" then
65 if node_obj
and node_obj
.config
.include_payload
== false then
66 item
:maptags(function () return nil; end);
68 if expose_publisher
and actor
then
69 item
.attr
.publisher
= actor
75 local msg_type
= node_obj
and node_obj
.config
.message_type
or "headline";
76 local message
= st
.message({ from
= module
.host
, type = msg_type
, id
= id
})
77 :tag("event", { xmlns
= xmlns_pubsub_event
})
78 :tag(kind
, { node
= node
});
81 message
:add_child(item
);
85 if item
and item
.tags
[1] then
86 local payload
= item
.tags
[1];
87 summary
= module
:fire_event("pubsub-summary/"..payload
.attr
.xmlns
, {
88 kind
= kind
, node
= node
, jids
= jids
, actor
= actor
, item
= item
, payload
= payload
,
92 for jid
, options
in pairs(jids
) do
93 local new_stanza
= st
.clone(message
);
94 if summary
and type(options
) == "table" and options
["pubsub#include_body"] then
95 new_stanza
:body(summary
);
97 new_stanza
.attr
.to
= jid
;
98 module
:send(new_stanza
);
102 local max_max_items
= module
:get_option_number("pubsub_max_items", 256);
103 function check_node_config(node
, actor
, new_config
) -- luacheck: ignore 212/node 212/actor
104 if (new_config
["max_items"] or 1) > max_max_items
then
107 if new_config
["access_model"] ~= "whitelist"
108 and new_config
["access_model"] ~= "open" then
114 function is_item_stanza(item
)
115 return st
.is_stanza(item
) and item
.attr
.xmlns
== xmlns_pubsub
and item
.name
== "item";
118 -- Compose a textual representation of Atom payloads
119 module
:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event
)
120 local payload
= event
.payload
;
121 local title
= payload
:get_child_text("title");
122 local summary
= payload
:get_child_text("summary");
123 if not summary
and title
then
124 local author
= payload
:find("author/name#");
127 summary
= author
.. " posted " .. summary
;
133 module
:hook("iq/host/"..xmlns_pubsub
..":pubsub", handle_pubsub_iq
);
134 module
:hook("iq/host/"..xmlns_pubsub_owner
..":pubsub", handle_pubsub_iq
);
136 local function add_disco_features_from_service(service
) --luacheck: ignore 431/service
137 for feature
in lib_pubsub
.get_feature_set(service
) do
138 module
:add_feature(xmlns_pubsub
.."#"..feature
);
142 module
:hook("host-disco-info-node", function (event
)
143 return lib_pubsub
.handle_disco_info_node(event
, service
);
146 module
:hook("host-disco-items-node", function (event
)
147 return lib_pubsub
.handle_disco_items_node(event
, service
);
151 module
:hook("host-disco-items", function (event
)
152 local stanza
, reply
= event
.stanza
, event
.reply
;
153 local ok
, ret
= service
:get_nodes(stanza
.attr
.from
);
157 for node
, node_obj
in pairs(ret
) do
158 reply
:tag("item", { jid
= module
.host
, node
= node
, name
= node_obj
.config
.title
}):up();
162 local admin_aff
= module
:get_option_string("default_admin_affiliation", "owner");
163 local function get_affiliation(jid
)
164 local bare_jid
= jid_bare(jid
);
165 if bare_jid
== module
.host
or usermanager
.is_admin(bare_jid
, module
.host
) then
170 function get_service()
174 function set_service(new_service
)
175 service
= new_service
;
176 module
.environment
.service
= service
;
177 add_disco_features_from_service(service
);
180 function module
.save()
181 return { service
= service
};
184 function module
.restore(data
)
185 set_service(data
.service
);
188 function module
.load()
189 if module
.reloading
then return; end
191 set_service(pubsub
.new({
192 autocreate_on_publish
= autocreate_on_publish
;
193 autocreate_on_subscribe
= autocreate_on_subscribe
;
195 nodestore
= node_store
;
196 itemstore
= create_simple_itemstore
;
197 broadcaster
= simple_broadcast
;
198 itemcheck
= is_item_stanza
;
199 check_node_config
= check_node_config
;
200 get_affiliation
= get_affiliation
;
202 normalize_jid
= jid_bare
;