init.lua

1
-- Use LuaRocks if available
2
pcall(require, "luarocks.require");
3
 
4
local socket = require"socket";
5
 
6
-- Load LuaSec if available
7
pcall(require, "ssl");
8
 
9
local server = require "prosody.net.server";
10
local events = require "prosody.util.events";
11
local logger = require "prosody.util.logger";
12
 
13
local format = require "prosody.util.format".format;
14
 
15
local verse = {};
16
verse.server = server;
17
 
18
local stream = {};
19
stream.__index = stream;
20
verse.stream_mt = stream;
21
 
22
verse.plugins = {};
23
 
24
function verse.init(...)
25
	for i=1,select("#", ...) do
26
		local ok, err = pcall(require, "verse."..select(i,...));
27
		if not ok then
28
			error("Verse connection module not found: verse."..select(i,...)..err);
29
		end
30
	end
31
	return verse;
32
end
33
 
34
 
35
local max_id = 0;
36
 
37
function verse.new(logger, base)
38
	local t = setmetatable(base or {}, stream);
39
	max_id = max_id + 1;
40
	t.id = tostring(max_id);
41
	t.logger = logger or verse.new_logger("stream"..t.id);
42
	t.events = events.new();
43
	t.plugins = {};
44
	t.verse = verse;
45
	return t;
46
end
47
 
48
verse.add_task = require "prosody.util.timer".add_task;
49
 
50
verse.logger = logger.init; -- COMPAT: Deprecated
51
verse.new_logger = logger.init;
52
verse.log = verse.logger("verse");
53
 
54
function verse.set_log_handler(log_handler, levels)
55
	levels = levels or { "debug", "info", "warn", "error" };
56
	logger.reset();
57
	if io.type(log_handler) == "file" then
58
		local f = log_handler;
59
		function log_handler(name, level, message)
60
			f:write(name, "\t", level, "\t", message, "\n");
61
		end
62
	end
63
	if log_handler then
64
		local function _log_handler(name, level, message, ...)
65
			return log_handler(name, level, format(message, ...));
66
		end
67
		for i, level in ipairs(levels) do
68
			logger.add_level_sink(level, _log_handler);
69
		end
70
	end
71
end
72
 
73
function verse._default_log_handler(name, level, message)
74
	return io.stderr:write(name, "\t", level, "\t", message, "\n");
75
end
76
verse.set_log_handler(verse._default_log_handler, { "error" });
77
 
78
local function error_handler(err)
79
	verse.log("error", "Error: %s", err);
80
	verse.log("error", "Traceback: %s", debug.traceback());
81
end
82
 
83
function verse.set_error_handler(new_error_handler)
84
	error_handler = new_error_handler;
85
end
86
 
87
function verse.loop()
88
	return xpcall(server.loop, error_handler);
89
end
90
 
91
function verse.step()
92
	return xpcall(server.step, error_handler);
93
end
94
 
95
function verse.quit()
96
	return server.setquitting("once");
97
end
98
 
99
function verse.tls_builder(...)
100
	return server.tls_builder(...);
101
end
102
 
103
function stream:listen(host, port)
104
	host = host or "localhost";
105
	port = port or 0;
106
	local conn, err = server.addserver(host, port, verse.new_listener(self, "server"), "*a");
107
	if conn then
108
		self:debug("Bound to %s:%s", host, port);
109
		self.server = conn;
110
	end
111
	return conn, err;
112
end
113
 
114
function stream:connect(connect_host, connect_port)
115
	connect_host = connect_host or "localhost";
116
	connect_port = tonumber(connect_port) or 5222;
117
 
118
	-- Create and initiate connection
119
	local conn = socket.tcp()
120
	conn:settimeout(0);
121
	conn:setoption("keepalive", true);
122
	local success, err = conn:connect(connect_host, connect_port);
123
 
124
	if not success and err ~= "timeout" then
125
		self:warn("connect() to %s:%d failed: %s", connect_host, connect_port, err);
126
		return self:event("disconnected", { reason = err }) or false, err;
127
	end
128
 
129
	local conn = server.wrapclient(conn, connect_host, connect_port, verse.new_listener(self), "*a");
130
	if not conn then
131
		self:warn("connection initialisation failed: %s", err);
132
		return self:event("disconnected", { reason = err }) or false, err;
133
	end
134
	self:set_conn(conn);
135
	return true;
136
end
137
 
138
function stream:set_conn(conn)
139
	self.conn = conn;
140
	self.send = function (stream, data)
141
		self:event("outgoing", data);
142
		data = tostring(data);
143
		self:event("outgoing-raw", data);
144
		return conn:write(data);
145
	end;
146
end
147
 
148
function stream:close(reason)
149
	if not self.conn then
150
		verse.log("error", "Attempt to close disconnected connection - possibly a bug");
151
		return;
152
	end
153
	local conn = self.conn;
154
	self:event("shutdown");
155
	conn:close();
156
end
157
 
158
-- Logging functions
159
function stream:debug(...)
160
	return self.logger("debug", ...);
161
end
162
 
163
function stream:info(...)
164
	return self.logger("info", ...);
165
end
166
 
167
function stream:warn(...)
168
	return self.logger("warn", ...);
169
end
170
 
171
function stream:error(...)
172
	return self.logger("error", ...);
173
end
174
 
175
-- Event handling
176
function stream:event(name, ...)
177
	self:debug("Firing event: "..tostring(name));
178
	return self.events.fire_event(name, ...);
179
end
180
 
181
function stream:hook(name, ...)
182
	return self.events.add_handler(name, ...);
183
end
184
 
185
function stream:unhook(name, handler)
186
	return self.events.remove_handler(name, handler);
187
end
188
 
189
function verse.eventable(object)
190
	object.events = events.new();
191
	object.hook, object.unhook = stream.hook, stream.unhook;
192
	local fire_event = object.events.fire_event;
193
	function object:event(name, ...) return fire_event(name, ...); end
194
	return object;
195
end
196
 
197
function stream:add_plugin(name)
198
	if self.plugins[name] then return true; end
199
	if require("verse.plugins."..name) then
200
		local ok, err = verse.plugins[name](self);
201
		if ok ~= false then
202
			self:debug("Loaded %s plugin", name);
203
			self.plugins[name] = true;
204
		else
205
			self:warn("Failed to load %s plugin: %s", name, err);
206
		end
207
	end
208
	return self;
209
end
210
 
211
-- Listener factory
212
function verse.new_listener(stream)
213
	local conn_listener = {};
214
 
215
	function conn_listener.onconnect(conn)
216
		if stream.server then
217
			local client = verse.new();
218
			conn:setlistener(verse.new_listener(client));
219
			client:set_conn(conn);
220
			stream:event("connected", { client = client });
221
		else
222
			stream.connected = true;
223
			stream:event("connected");
224
		end
225
	end
226
 
227
	function conn_listener.onincoming(conn, data)
228
		stream:event("incoming-raw", data);
229
	end
230
 
231
	function conn_listener.ondisconnect(conn, err)
232
		if conn ~= stream.conn then return end
233
		stream.connected = false;
234
		stream:event("disconnected", { reason = err });
235
	end
236
 
237
	function conn_listener.ondrain(conn)
238
		stream:event("drained");
239
	end
240
 
241
	function conn_listener.onstatus(conn, new_status)
242
		stream:event("status", new_status);
243
	end
244
 
245
	function conn_listener.onreadtimeout(conn)
246
		return stream:event("read-timeout");
247
	end
248
	return conn_listener;
249
end
250
 
251
return verse;