core/moduleapi.lua
changeset 4531 c778ce7e3c78
child 4534 7a0a31c4f6c5
equal deleted inserted replaced
4481:408c2f688e4e 4531:c778ce7e3c78
       
     1 -- Prosody IM
       
     2 -- Copyright (C) 2008-2012 Matthew Wild
       
     3 -- Copyright (C) 2008-2012 Waqas Hussain
       
     4 -- 
       
     5 -- This project is MIT/X11 licensed. Please see the
       
     6 -- COPYING file in the source package for more information.
       
     7 --
       
     8 
       
     9 local config = require "core.configmanager";
       
    10 local modulemanager = require "modulemanager";
       
    11 local array = require "util.array";
       
    12 local set = require "util.set";
       
    13 local logger = require "util.logger";
       
    14 local pluginloader = require "util.pluginloader";
       
    15 
       
    16 local multitable_new = require "util.multitable".new;
       
    17 
       
    18 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
       
    19 local error, setmetatable, setfenv, type = error, setmetatable, setfenv, type;
       
    20 local ipairs, pairs, select, unpack = ipairs, pairs, select, unpack;
       
    21 local tonumber, tostring = tonumber, tostring;
       
    22 
       
    23 local prosody = prosody;
       
    24 local hosts = prosody.hosts;
       
    25 
       
    26 -- Registry of shared module data
       
    27 local shared_data = setmetatable({}, { __mode = "v" });
       
    28 
       
    29 local NULL = {};
       
    30 
       
    31 local api = {};
       
    32 
       
    33 -- Returns the name of the current module
       
    34 function api:get_name()
       
    35 	return self.name;
       
    36 end
       
    37 
       
    38 -- Returns the host that the current module is serving
       
    39 function api:get_host()
       
    40 	return self.host;
       
    41 end
       
    42 
       
    43 function api:get_host_type()
       
    44 	return hosts[self.host].type;
       
    45 end
       
    46 
       
    47 function api:set_global()
       
    48 	self.host = "*";
       
    49 	-- Update the logger
       
    50 	local _log = logger.init("mod_"..self.name);
       
    51 	self.log = function (self, ...) return _log(...); end;
       
    52 	self._log = _log;
       
    53 end
       
    54 
       
    55 function api:add_feature(xmlns)
       
    56 	self:add_item("feature", xmlns);
       
    57 end
       
    58 function api:add_identity(category, type, name)
       
    59 	self:add_item("identity", {category = category, type = type, name = name});
       
    60 end
       
    61 function api:add_extension(data)
       
    62 	self:add_item("extension", data);
       
    63 end
       
    64 
       
    65 function api:fire_event(...)
       
    66 	return (hosts[self.host] or prosody).events.fire_event(...);
       
    67 end
       
    68 
       
    69 function api:hook(event, handler, priority)
       
    70 	hooks:set(self.host, self.name, event, handler, true);
       
    71 	(hosts[self.host] or prosody).events.add_handler(event, handler, priority);
       
    72 end
       
    73 
       
    74 function api:hook_global(event, handler, priority)
       
    75 	hooks:set("*", self.name, event, handler, true);
       
    76 	prosody.events.add_handler(event, handler, priority);
       
    77 end
       
    78 
       
    79 function api:hook_stanza(xmlns, name, handler, priority)
       
    80 	if not handler and type(name) == "function" then
       
    81 		-- If only 2 options then they specified no xmlns
       
    82 		xmlns, name, handler, priority = nil, xmlns, name, handler;
       
    83 	elseif not (handler and name) then
       
    84 		self:log("warn", "Error: Insufficient parameters to module:hook_stanza()");
       
    85 		return;
       
    86 	end
       
    87 	return self:hook("stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority);
       
    88 end
       
    89 
       
    90 function api:require(lib)
       
    91 	local f, n = pluginloader.load_code(self.name, lib..".lib.lua");
       
    92 	if not f then
       
    93 		f, n = pluginloader.load_code(lib, lib..".lib.lua");
       
    94 	end
       
    95 	if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message
       
    96 	setfenv(f, self.environment);
       
    97 	return f();
       
    98 end
       
    99 
       
   100 function api:get_option(name, default_value)
       
   101 	local value = config.get(self.host, self.name, name);
       
   102 	if value == nil then
       
   103 		value = config.get(self.host, "core", name);
       
   104 		if value == nil then
       
   105 			value = default_value;
       
   106 		end
       
   107 	end
       
   108 	return value;
       
   109 end
       
   110 
       
   111 function api:get_option_string(name, default_value)
       
   112 	local value = self:get_option(name, default_value);
       
   113 	if type(value) == "table" then
       
   114 		if #value > 1 then
       
   115 			self:log("error", "Config option '%s' does not take a list, using just the first item", name);
       
   116 		end
       
   117 		value = value[1];
       
   118 	end
       
   119 	if value == nil then
       
   120 		return nil;
       
   121 	end
       
   122 	return tostring(value);
       
   123 end
       
   124 
       
   125 function api:get_option_number(name, ...)
       
   126 	local value = self:get_option(name, ...);
       
   127 	if type(value) == "table" then
       
   128 		if #value > 1 then
       
   129 			self:log("error", "Config option '%s' does not take a list, using just the first item", name);
       
   130 		end
       
   131 		value = value[1];
       
   132 	end
       
   133 	local ret = tonumber(value);
       
   134 	if value ~= nil and ret == nil then
       
   135 		self:log("error", "Config option '%s' not understood, expecting a number", name);
       
   136 	end
       
   137 	return ret;
       
   138 end
       
   139 
       
   140 function api:get_option_boolean(name, ...)
       
   141 	local value = self:get_option(name, ...);
       
   142 	if type(value) == "table" then
       
   143 		if #value > 1 then
       
   144 			self:log("error", "Config option '%s' does not take a list, using just the first item", name);
       
   145 		end
       
   146 		value = value[1];
       
   147 	end
       
   148 	if value == nil then
       
   149 		return nil;
       
   150 	end
       
   151 	local ret = value == true or value == "true" or value == 1 or nil;
       
   152 	if ret == nil then
       
   153 		ret = (value == false or value == "false" or value == 0);
       
   154 		if ret then
       
   155 			ret = false;
       
   156 		else
       
   157 			ret = nil;
       
   158 		end
       
   159 	end
       
   160 	if ret == nil then
       
   161 		self:log("error", "Config option '%s' not understood, expecting true/false", name);
       
   162 	end
       
   163 	return ret;
       
   164 end
       
   165 
       
   166 function api:get_option_array(name, ...)
       
   167 	local value = self:get_option(name, ...);
       
   168 
       
   169 	if value == nil then
       
   170 		return nil;
       
   171 	end
       
   172 	
       
   173 	if type(value) ~= "table" then
       
   174 		return array{ value }; -- Assume any non-list is a single-item list
       
   175 	end
       
   176 	
       
   177 	return array():append(value); -- Clone
       
   178 end
       
   179 
       
   180 function api:get_option_set(name, ...)
       
   181 	local value = self:get_option_array(name, ...);
       
   182 	
       
   183 	if value == nil then
       
   184 		return nil;
       
   185 	end
       
   186 	
       
   187 	return set.new(value);
       
   188 end
       
   189 
       
   190 local module_items = multitable_new();
       
   191 function api:add_item(key, value)
       
   192 	self.items = self.items or {};
       
   193 	self.items[key] = self.items[key] or {};
       
   194 	t_insert(self.items[key], value);
       
   195 	self:fire_event("item-added/"..key, {source = self, item = value});
       
   196 end
       
   197 function api:remove_item(key, value)
       
   198 	local t = self.items and self.items[key] or NULL;
       
   199 	for i = #t,1,-1 do
       
   200 		if t[i] == value then
       
   201 			t_remove(self.items[key], i);
       
   202 			self:fire_event("item-removed/"..key, {source = self, item = value});
       
   203 			return value;
       
   204 		end
       
   205 	end
       
   206 end
       
   207 
       
   208 function api:get_host_items(key)
       
   209 	local result = {};
       
   210 	for mod_name, module in pairs(modulemanager.get_modules(self.host)) do
       
   211 		module = module.module;
       
   212 		if module.items then
       
   213 			for _, item in ipairs(module.items[key] or NULL) do
       
   214 				t_insert(result, item);
       
   215 			end
       
   216 		end
       
   217 	end
       
   218 	for mod_name, module in pairs(modulemanager.get_modules("*")) do
       
   219 		module = module.module;
       
   220 		if module.items then
       
   221 			for _, item in ipairs(module.items[key] or NULL) do
       
   222 				t_insert(result, item);
       
   223 			end
       
   224 		end
       
   225 	end
       
   226 	return result;
       
   227 end
       
   228 
       
   229 function api:handle_items(type, added_cb, removed_cb, existing)
       
   230 	self:hook("item-added/"..type, added_cb);
       
   231 	self:hook("item-removed/"..type, removed_cb);
       
   232 	if existing ~= false then
       
   233 		for _, item in ipairs(self:get_host_items(type)) do
       
   234 			added_cb({ item = item });
       
   235 		end
       
   236 	end
       
   237 end
       
   238 
       
   239 return api;