Comparison

plugins/mod_pubsub/pubsub.lib.lua @ 9115:fddebfaae7d9

pubsub.lib: Support for publish-options behind a feature flag, 'enable_publish_options'
author Matthew Wild <mwild1@gmail.com>
date Sun, 05 Aug 2018 00:08:07 +0100
parent 9110:3dbecb399dfb
child 9116:9f36f5c14545
comparison
equal deleted inserted replaced
9114:ca5228a7d907 9115:fddebfaae7d9
9 local dataform = require"util.dataforms".new; 9 local dataform = require"util.dataforms".new;
10 10
11 local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; 11 local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
12 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors"; 12 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
13 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; 13 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner";
14
15 local enable_publish_options = module:get_option_boolean("enable_publish_options", false);
14 16
15 local _M = {}; 17 local _M = {};
16 18
17 local handlers = {}; 19 local handlers = {};
18 _M.handlers = handlers; 20 _M.handlers = handlers;
39 end 41 end
40 return reply; 42 return reply;
41 end 43 end
42 _M.pubsub_error_reply = pubsub_error_reply; 44 _M.pubsub_error_reply = pubsub_error_reply;
43 45
46 -- Note: If any config options are added that are of complex types,
47 -- (not simply strings/numbers) then the publish-options code will
48 -- need to be revisited
49 local node_config_form = dataform {
50 {
51 type = "hidden";
52 name = "FORM_TYPE";
53 value = "http://jabber.org/protocol/pubsub#node_config";
54 };
55 {
56 type = "text-single";
57 name = "pubsub#title";
58 label = "Title";
59 };
60 {
61 type = "text-single";
62 name = "pubsub#description";
63 label = "Description";
64 };
65 {
66 type = "text-single";
67 name = "pubsub#max_items";
68 label = "Max # of items to persist";
69 };
70 {
71 type = "boolean";
72 name = "pubsub#persist_items";
73 label = "Persist items to storage";
74 };
75 {
76 type = "list-single";
77 name = "pubsub#access_model";
78 label = "Specify the subscriber model";
79 options = {
80 { value = "authorize" },
81 { value = "open" },
82 { value = "presence" },
83 { value = "roster" },
84 { value = "whitelist" },
85 };
86 };
87 {
88 type = "list-single";
89 name = "pubsub#notification_type";
90 label = "Specify the delivery style for notifications";
91 options = {
92 { label = "Messages of type normal", value = "normal" },
93 { label = "Messages of type headline", value = "headline", default = true },
94 };
95 };
96 };
97
98 local options_form = dataform {
99 {
100 type = "hidden";
101 name = "FORM_TYPE";
102 value = "http://jabber.org/protocol/pubsub#subscribe_options";
103 };
104 {
105 type = "boolean";
106 name = "pubsub#include_body";
107 label = "Receive message body in addition to payload?";
108 };
109 };
110
111 local node_metadata_form = dataform {
112 {
113 type = "hidden";
114 name = "FORM_TYPE";
115 value = "http://jabber.org/protocol/pubsub#meta-data";
116 };
117 {
118 type = "text-single";
119 name = "pubsub#title";
120 };
121 {
122 type = "text-single";
123 name = "pubsub#description";
124 };
125 };
126
127 local config_field_map = {
128 title = "pubsub#title";
129 description = "pubsub#description";
130 max_items = "pubsub#max_items";
131 persist_items = "pubsub#persist_items";
132 notification_type = "pubsub#notification_type";
133 access_model = "pubsub#access_model";
134 };
135 local reverse_config_field_map = {};
136 for k, v in pairs(config_field_map) do reverse_config_field_map[v] = k; end
137
44 -- util.pubsub is meant to be agnostic to XEP-0060 138 -- util.pubsub is meant to be agnostic to XEP-0060
45 local function config_to_xep0060(node_config) 139 local function config_to_xep0060(node_config)
46 return { 140 return {
47 ["pubsub#title"] = node_config["title"]; 141 ["pubsub#title"] = node_config["title"];
48 ["pubsub#description"] = node_config["description"]; 142 ["pubsub#description"] = node_config["description"];
51 ["pubsub#notification_type"] = node_config["notification_type"]; 145 ["pubsub#notification_type"] = node_config["notification_type"];
52 ["pubsub#access_model"] = node_config["access_model"]; 146 ["pubsub#access_model"] = node_config["access_model"];
53 } 147 }
54 end 148 end
55 149
56 local function config_from_xep0060(config) 150 local function config_from_xep0060(config, strict)
57 return { 151 local ret = {};
58 ["title"] = config["pubsub#title"]; 152 for config_field, config_value in pairs(config) do
59 ["description"] = config["pubsub#description"]; 153 local mapped_name = reverse_config_field_map[config_field];
60 ["max_items"] = tonumber(config["pubsub#max_items"]); 154 if mapped_name then
61 ["persist_items"] = config["pubsub#persist_items"]; 155 if mapped_name == "max_items" then
62 ["notification_type"] = config["pubsub#notification_type"]; 156 config_value = tonumber(config_value);
63 ["access_model"] = config["pubsub#access_model"]; 157 end
64 } 158 ret[mapped_name] = config_value;
65 end 159 elseif strict then
66 160 return nil, "unknown-field", config_field;
67 local node_config_form = dataform { 161 end
68 { 162 end
69 type = "hidden"; 163 return ret;
70 name = "FORM_TYPE"; 164 end
71 value = "http://jabber.org/protocol/pubsub#node_config"; 165
72 }; 166 -- Used to check that the config of a node is as expected (i.e. 'publish-options')
73 { 167 local function check_preconditions(node_config, required_config)
74 type = "text-single"; 168 if not (node_config and required_config) then
75 name = "pubsub#title"; 169 return false;
76 label = "Title"; 170 end
77 }; 171 for config_field, value in pairs(required_config) do
78 { 172 if node_config[config_field] ~= value then
79 type = "text-single"; 173 return false;
80 name = "pubsub#description"; 174 end
81 label = "Description"; 175 end
82 }; 176 return true;
83 { 177 end
84 type = "text-single";
85 name = "pubsub#max_items";
86 label = "Max # of items to persist";
87 };
88 {
89 type = "boolean";
90 name = "pubsub#persist_items";
91 label = "Persist items to storage";
92 };
93 {
94 type = "list-single";
95 name = "pubsub#access_model";
96 label = "Specify the subscriber model";
97 options = {
98 { value = "authorize" },
99 { value = "open" },
100 { value = "presence" },
101 { value = "roster" },
102 { value = "whitelist" },
103 };
104 };
105 {
106 type = "list-single";
107 name = "pubsub#notification_type";
108 label = "Specify the delivery style for notifications";
109 options = {
110 { label = "Messages of type normal", value = "normal" },
111 { label = "Messages of type headline", value = "headline", default = true },
112 };
113 };
114 };
115
116 local options_form = dataform {
117 {
118 type = "hidden";
119 name = "FORM_TYPE";
120 value = "http://jabber.org/protocol/pubsub#subscribe_options";
121 };
122 {
123 type = "boolean";
124 name = "pubsub#include_body";
125 label = "Receive message body in addition to payload?";
126 };
127 };
128
129 local node_metadata_form = dataform {
130 {
131 type = "hidden";
132 name = "FORM_TYPE";
133 value = "http://jabber.org/protocol/pubsub#meta-data";
134 };
135 {
136 type = "text-single";
137 name = "pubsub#title";
138 };
139 {
140 type = "text-single";
141 name = "pubsub#description";
142 };
143 };
144 178
145 local service_method_feature_map = { 179 local service_method_feature_map = {
146 add_subscription = { "subscribe" }; 180 add_subscription = { "subscribe" };
147 create = { "create-nodes", "instant-nodes", "item-ids", "create-and-configure" }; 181 create = { "create-nodes", "instant-nodes", "item-ids", "create-and-configure" };
148 delete = { "delete-nodes" }; 182 delete = { "delete-nodes" };
149 get_items = { "retrieve-items" }; 183 get_items = { "retrieve-items" };
150 get_subscriptions = { "retrieve-subscriptions" }; 184 get_subscriptions = { "retrieve-subscriptions" };
151 node_defaults = { "retrieve-default" }; 185 node_defaults = { "retrieve-default" };
152 publish = { "publish" }; 186 publish = { "publish", "multi-items", enable_publish_options and "publish-options" or nil };
153 purge = { "purge-nodes" }; 187 purge = { "purge-nodes" };
154 retract = { "delete-items", "retract-items" }; 188 retract = { "delete-items", "retract-items" };
155 set_node_config = { "config-node" }; 189 set_node_config = { "config-node" };
156 set_affiliation = { "modify-affiliations" }; 190 set_affiliation = { "modify-affiliations" };
157 }; 191 };
511 local node = publish.attr.node; 545 local node = publish.attr.node;
512 if not node then 546 if not node then
513 origin.send(pubsub_error_reply(stanza, "nodeid-required")); 547 origin.send(pubsub_error_reply(stanza, "nodeid-required"));
514 return true; 548 return true;
515 end 549 end
550 local publish_options = stanza.tags[1]:get_child("publish-options");
551 if enable_publish_options and publish_options then
552 -- Ensure that the node configuration matches the values in publish-options
553 local publish_options_form = publish_options:get_child("x", "jabber:x:data");
554 local required_config = config_from_xep0060(node_config_form:data(publish_options_form), true);
555 local node_config = service:get_node_config(node, stanza.attr.from);
556 if not check_preconditions(node_config, required_config) then
557 local reply = pubsub_error_reply(stanza, "precondition-not-met");
558 origin.send(reply);
559 return true;
560 end
561 end
516 local item = publish:get_child("item"); 562 local item = publish:get_child("item");
517 local id = (item and item.attr.id); 563 local id = (item and item.attr.id);
518 if not id then 564 if not id then
519 id = uuid_generate(); 565 id = uuid_generate();
520 if item then 566 if item then