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 |