plugins/mod_blocklist.lua
changeset 13349 a74251a790ed
parent 13217 50324f66ca2a
child 13485 1c87c0a7ece6
equal deleted inserted replaced
13348:958c759d3897 13349:a74251a790ed
    22 
    22 
    23 local storage = module:open_store();
    23 local storage = module:open_store();
    24 local sessions = prosody.hosts[module.host].sessions;
    24 local sessions = prosody.hosts[module.host].sessions;
    25 local full_sessions = prosody.full_sessions;
    25 local full_sessions = prosody.full_sessions;
    26 
    26 
    27 -- First level cache of blocklists by username.
    27 -- Cache of blocklists, keeps a fixed number of items.
    28 -- Weak table so may randomly expire at any time.
       
    29 local cache = setmetatable({}, { __mode = "v" });
       
    30 
       
    31 -- Second level of caching, keeps a fixed number of items, also anchors
       
    32 -- items in the above cache.
       
    33 --
    28 --
    34 -- The size of this affects how often we will need to load a blocklist from
    29 -- The size of this affects how often we will need to load a blocklist from
    35 -- disk, which we want to avoid during routing. On the other hand, we don't
    30 -- disk, which we want to avoid during routing. On the other hand, we don't
    36 -- want to use too much memory either, so this can be tuned by advanced
    31 -- want to use too much memory either, so this can be tuned by advanced
    37 -- users. TODO use science to figure out a better default, 64 is just a guess.
    32 -- users. TODO use science to figure out a better default, 64 is just a guess.
    38 local cache_size = module:get_option_integer("blocklist_cache_size", 64, 1);
    33 local cache_size = module:get_option_integer("blocklist_cache_size", 256, 1);
    39 local cache2 = require"prosody.util.cache".new(cache_size);
    34 local blocklist_cache = require"prosody.util.cache".new(cache_size);
    40 
    35 
    41 local null_blocklist = {};
    36 local null_blocklist = {};
    42 
    37 
    43 module:add_feature("urn:xmpp:blocking");
    38 module:add_feature("urn:xmpp:blocking");
    44 
    39 
    46 	local ok, err = storage:set(username, blocklist);
    41 	local ok, err = storage:set(username, blocklist);
    47 	if not ok then
    42 	if not ok then
    48 		return ok, err;
    43 		return ok, err;
    49 	end
    44 	end
    50 	-- Successful save, update the cache
    45 	-- Successful save, update the cache
    51 	cache2:set(username, blocklist);
    46 	blocklist_cache:set(username, blocklist);
    52 	cache[username] = blocklist;
       
    53 	return true;
    47 	return true;
    54 end
    48 end
    55 
    49 
    56 -- Migrates from the old mod_privacy storage
    50 -- Migrates from the old mod_privacy storage
    57 -- TODO mod_privacy was removed in 0.10.0, this should be phased out
    51 -- TODO mod_privacy was removed in 0.10.0, this should be phased out
    84 		return nil;
    78 		return nil;
    85 	end
    79 	end
    86 end
    80 end
    87 
    81 
    88 local function get_blocklist(username)
    82 local function get_blocklist(username)
    89 	local blocklist = cache2:get(username);
    83 	local blocklist = blocklist_cache:get(username);
    90 	if not blocklist then
    84 	if not blocklist then
    91 		if not user_exists(username, module.host) then
    85 		if not user_exists(username, module.host) then
    92 			return null_blocklist;
    86 			return null_blocklist;
    93 		end
    87 		end
    94 		blocklist = storage:get(username);
    88 		blocklist = storage:get(username);
    96 			blocklist = migrate_privacy_list(username);
    90 			blocklist = migrate_privacy_list(username);
    97 		end
    91 		end
    98 		if not blocklist then
    92 		if not blocklist then
    99 			blocklist = { [false] = { created = os.time(); }; };
    93 			blocklist = { [false] = { created = os.time(); }; };
   100 		end
    94 		end
   101 		cache2:set(username, blocklist);
    95 		blocklist_cache:set(username, blocklist);
   102 	end
    96 	end
   103 	cache[username] = blocklist;
       
   104 	return blocklist;
    97 	return blocklist;
   105 end
    98 end
   106 
    99 
   107 module:hook("iq-get/self/urn:xmpp:blocking:blocklist", function (event)
   100 module:hook("iq-get/self/urn:xmpp:blocking:blocklist", function (event)
   108 	local origin, stanza = event.origin, event.stanza;
   101 	local origin, stanza = event.origin, event.stanza;
   109 	local username = origin.username;
   102 	local username = origin.username;
   110 	local reply = st.reply(stanza):tag("blocklist", { xmlns = "urn:xmpp:blocking" });
   103 	local reply = st.reply(stanza):tag("blocklist", { xmlns = "urn:xmpp:blocking" });
   111 	local blocklist = cache[username] or get_blocklist(username);
   104 	local blocklist = get_blocklist(username);
   112 	for jid in pairs(blocklist) do
   105 	for jid in pairs(blocklist) do
   113 		if jid then
   106 		if jid then
   114 			reply:tag("item", { jid = jid }):up();
   107 			reply:tag("item", { jid = jid }):up();
   115 		end
   108 		end
   116 	end
   109 	end
   165 		-- <block/> element does not contain at least one <item/> child element
   158 		-- <block/> element does not contain at least one <item/> child element
   166 		origin.send(st_error_reply(stanza, "modify", "bad-request"));
   159 		origin.send(st_error_reply(stanza, "modify", "bad-request"));
   167 		return true;
   160 		return true;
   168 	end
   161 	end
   169 
   162 
   170 	local blocklist = cache[username] or get_blocklist(username);
   163 	local blocklist = get_blocklist(username);
   171 
   164 
   172 	local new_blocklist = {
   165 	local new_blocklist = {
   173 		-- We set the [false] key to something as a signal not to migrate privacy lists
   166 		-- We set the [false] key to something as a signal not to migrate privacy lists
   174 		[false] = blocklist[false] or { created = now; };
   167 		[false] = blocklist[false] or { created = now; };
   175 	};
   168 	};
   239 module:hook("iq-set/self/urn:xmpp:blocking:unblock", edit_blocklist, -1);
   232 module:hook("iq-set/self/urn:xmpp:blocking:unblock", edit_blocklist, -1);
   240 
   233 
   241 -- Cache invalidation, solved!
   234 -- Cache invalidation, solved!
   242 module:hook_global("user-deleted", function (event)
   235 module:hook_global("user-deleted", function (event)
   243 	if event.host == module.host then
   236 	if event.host == module.host then
   244 		cache2:set(event.username, nil);
   237 		blocklist_cache:set(event.username, nil);
   245 		cache[event.username] = nil;
       
   246 	end
   238 	end
   247 end);
   239 end);
   248 
   240 
   249 -- Buggy clients
   241 -- Buggy clients
   250 module:hook("iq-error/self/blocklist-push", function (event)
   242 module:hook("iq-error/self/blocklist-push", function (event)
   255 		module.name, condition, text and ": " or "", text or "");
   247 		module.name, condition, text and ": " or "", text or "");
   256 	return true;
   248 	return true;
   257 end);
   249 end);
   258 
   250 
   259 local function is_blocked(user, jid)
   251 local function is_blocked(user, jid)
   260 	local blocklist = cache[user] or get_blocklist(user);
   252 	local blocklist = get_blocklist(user);
   261 	if blocklist[jid] then return true; end
   253 	if blocklist[jid] then return true; end
   262 	local node, host = jid_split(jid);
   254 	local node, host = jid_split(jid);
   263 	return blocklist[host] or node and blocklist[node..'@'..host];
   255 	return blocklist[host] or node and blocklist[node..'@'..host];
   264 end
   256 end
   265 
   257