util.httpstream: A little cleanup of the HTTP path.
[prosody.git] / plugins / mod_pubsub.lua
index 769dd49ad8957fd782a2efdba3358a9a306ec002..841de14761b8108e070911b88096f74b8806a292 100644 (file)
@@ -9,6 +9,9 @@ local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
 local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event";
 
+local autocreate_on_publish = module:get_option_boolean("autocreate_on_publish", false);
+local autocreate_on_subscribe = module:get_option_boolean("autocreate_on_subscribe", false);
+
 local service;
 
 local handlers = {};
@@ -29,6 +32,7 @@ local pubsub_errors = {
        ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" };
        ["item-not-found"] = { "cancel", "item-not-found" };
        ["not-subscribed"] = { "modify", "unexpected-request", nil, "not-subscribed" };
+       ["forbidden"] = { "cancel", "forbidden" };
 };
 function pubsub_error_reply(stanza, error)
        local e = pubsub_errors[error];
@@ -43,8 +47,14 @@ function handlers.get_items(origin, stanza, items)
        local node = items.attr.node;
        local item = items:get_child("item");
        local id = item and item.attr.id;
+       
+       local ok, results = service:get_items(node, stanza.attr.from, id);
+       if not ok then
+               return origin.send(pubsub_error_reply(stanza, results));
+       end
+       
        local data = st.stanza("items", { node = node });
-       for _, entry in pairs(service:get(node, stanza.attr.from, id)) do
+       for _, entry in pairs(results) do
                data:add_child(entry);
        end
        if data then
@@ -71,10 +81,14 @@ function handlers.set_create(origin, stanza, create)
                repeat
                        node = uuid_generate();
                        ok, ret = service:create(node, stanza.attr.from);
-               until ok;
-               reply = st.reply(stanza)
-                       :tag("pubsub", { xmlns = xmlns_pubsub })
-                               :tag("create", { node = node });
+               until ok or ret ~= "conflict";
+               if ok then
+                       reply = st.reply(stanza)
+                               :tag("pubsub", { xmlns = xmlns_pubsub })
+                                       :tag("create", { node = node });
+               else
+                       reply = pubsub_error_reply(stanza, ret);
+               end
        end
        return origin.send(reply);
 end
@@ -166,9 +180,39 @@ end
 
 module:hook("iq/host/http://jabber.org/protocol/pubsub:pubsub", handle_pubsub_iq);
 
-local disco_info = st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info" })
-       :tag("identity", { category = "pubsub", type = "service" }):up()
-       :tag("feature", { var = "http://jabber.org/protocol/pubsub" }):up();
+local disco_info;
+
+local feature_map = {
+       create = { "create-nodes", autocreate_on_publish and "instant-nodes", "item-ids" };
+       retract = { "delete-items", "retract-items" };
+       publish = { "publish" };
+       get_items = { "retrieve-items" };
+};
+
+local function add_disco_features_from_service(disco, service)
+       for method, features in pairs(feature_map) do
+               if service[method] then
+                       for _, feature in ipairs(features) do
+                               if feature then
+                                       disco:tag("feature", { var = xmlns_pubsub.."#"..feature }):up();
+                               end
+                       end
+               end
+       end
+       for affiliation in pairs(service.config.capabilities) do
+               if affiliation ~= "none" and affiliation ~= "owner" then
+                       disco:tag("feature", { var = xmlns_pubsub.."#"..affiliation.."-affiliation" }):up();
+               end
+       end
+end
+
+local function build_disco_info(service)
+       local disco_info = st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info" })
+               :tag("identity", { category = "pubsub", type = "service" }):up()
+               :tag("feature", { var = "http://jabber.org/protocol/pubsub" }):up();
+       add_disco_features_from_service(disco_info, service);
+       return disco_info;
+end
 
 module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function (event)
        event.origin.send(st.reply(event.stanza):add_child(disco_info));
@@ -190,8 +234,79 @@ module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function
        return true;
 end);
 
-service = pubsub.new({
-       broadcaster = simple_broadcast
-});
-module.environment.service = service;
+local admin_aff = module:get_option_string("default_admin_affiliation", "owner");
+local function get_affiliation(jid)
+       local bare_jid = jid_bare(jid);
+       if bare_jid == module.host or usermanager.is_admin(bare_jid, module.host) then
+               return admin_aff;
+       end
+end
+
+function set_service(new_service)
+       service = new_service;
+       module.environment.service = service;
+       disco_info = build_disco_info(service);
+end
+
+function module.save()
+       return { service = service };
+end
+
+function module.restore(data)
+       set_service(data.service);
+end
 
+set_service(pubsub.new({
+       capabilities = {
+               none = {
+                       create = false;
+                       publish = false;
+                       retract = false;
+                       get_nodes = true;
+                       
+                       subscribe = true;
+                       unsubscribe = true;
+                       get_subscription = true;
+                       get_items = true;
+                       
+                       subscribe_other = false;
+                       unsubscribe_other = false;
+                       get_subscription_other = false;
+                       
+                       be_subscribed = true;
+                       be_unsubscribed = true;
+                       
+                       set_affiliation = false;
+               };
+               owner = {
+                       create = true;
+                       publish = true;
+                       retract = true;
+                       get_nodes = true;
+                       
+                       subscribe = true;
+                       unsubscribe = true;
+                       get_subscription = true;
+                       get_items = true;
+                       
+                       
+                       subscribe_other = true;
+                       unsubscribe_other = true;
+                       get_subscription_other = true;
+                       
+                       be_subscribed = true;
+                       be_unsubscribed = true;
+                       
+                       set_affiliation = true;
+               };
+       };
+       
+       autocreate_on_publish = autocreate_on_publish;
+       autocreate_on_subscribe = autocreate_on_subscribe;
+       
+       broadcaster = simple_broadcast;
+       get_affiliation = get_affiliation;
+       jids_equal = function (jid1, jid2)
+               return jid_bare(jid1) == jid_bare(jid2);
+       end;
+}));