util.encodings: Spell out all IDNA 2008 options ICU has
[prosody.git] / plugins / mod_pubsub / mod_pubsub.lua
blobfaf08cb2dbcdde2e21a51a37d3743c80676dc03b
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)
17 local service;
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);
27 end
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)
39 -- get(node_name)
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);
49 end
51 function simple_broadcast(kind, node, jids, item, actor, node_obj)
52 if node_obj then
53 if node_obj.config["notify_"..kind] == false then
54 return;
55 end
56 end
57 if kind == "retract" then
58 kind = "items"; -- XEP-0060 signals retraction in an <items> container
59 end
61 if item then
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);
67 end
68 if expose_publisher and actor then
69 item.attr.publisher = actor
70 end
71 end
72 end
74 local id = new_id();
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 });
80 if item then
81 message:add_child(item);
82 end
84 local summary;
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,
89 });
90 end
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);
96 end
97 new_stanza.attr.to = jid;
98 module:send(new_stanza);
99 end
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
105 return false;
107 if new_config["access_model"] ~= "whitelist"
108 and new_config["access_model"] ~= "open" then
109 return false;
111 return true;
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#");
125 summary = title;
126 if author then
127 summary = author .. " posted " .. summary;
130 return summary;
131 end);
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);
144 end);
146 module:hook("host-disco-items-node", function (event)
147 return lib_pubsub.handle_disco_items_node(event, service);
148 end);
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);
154 if not ok then
155 return;
157 for node, node_obj in pairs(ret) do
158 reply:tag("item", { jid = module.host, node = node, name = node_obj.config.title }):up();
160 end);
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
166 return admin_aff;
170 function get_service()
171 return 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;
203 }));