Software /
code /
prosody
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 |