mod_proxy65: Don't read data from the connection untill the proxying is activated
[prosody.git] / prosody
1 #!/usr/bin/env lua
2 -- Prosody IM
3 -- Copyright (C) 2008-2009 Matthew Wild
4 -- Copyright (C) 2008-2009 Waqas Hussain
5 -- 
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
8 --
9
10 -- Will be modified by configure script if run --
11
12 CFG_SOURCEDIR=os.getenv("PROSODY_SRCDIR");
13 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
14 CFG_PLUGINDIR=os.getenv("PROSODY_PLUGINDIR");
15 CFG_DATADIR=os.getenv("PROSODY_DATADIR");
16
17 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
18
19 if CFG_SOURCEDIR then
20         package.path = CFG_SOURCEDIR.."/?.lua;"..package.path;
21         package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
22 end
23
24 package.path = package.path..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.lua";
25 package.cpath = package.cpath..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.so";
26
27 if CFG_DATADIR then
28         if os.getenv("HOME") then
29                 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
30         end
31 end
32
33 -- Required to be able to find packages installed with luarocks
34 pcall(require, "luarocks.require")
35
36 -- Replace require with one that doesn't pollute _G
37 do
38         local _realG = _G;
39         local _real_require = require;
40         function require(...)
41                 local curr_env = getfenv(2);
42                 local curr_env_mt = getmetatable(getfenv(2));
43                 local _realG_mt = getmetatable(_realG);
44                 if curr_env_mt and curr_env_mt.__index and not curr_env_mt.__newindex and _realG_mt then
45                         local old_newindex
46                         old_newindex, _realG_mt.__newindex = _realG_mt.__newindex, curr_env;
47                         local ret = _real_require(...);
48                         _realG_mt.__newindex = old_newindex;
49                         return ret;
50                 end
51                 return _real_require(...);
52         end
53 end
54
55
56 config = require "core.configmanager"
57
58 function read_config()
59         -- TODO: Check for other formats when we add support for them
60         -- Use lfs? Make a new conf/ dir?
61         local filenames = {};
62         
63         local filename;
64         if arg[1] == "--config" and arg[2] then
65                 table.insert(filenames, arg[2]);
66                 if CFG_CONFIGDIR then
67                         table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
68                 end
69         else
70                 table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
71         end
72         for _,_filename in ipairs(filenames) do
73                 filename = _filename;
74                 local file = io.open(filename);
75                 if file then
76                         file:close();
77                         CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
78                         break;
79                 end
80         end
81         local ok, level, err = config.load(filename);
82         if not ok then
83                 print("\n");
84                 print("**************************");
85                 if level == "parser" then
86                         print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
87                         local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
88                         print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
89                         print("");
90                 elseif level == "file" then
91                         print("Prosody was unable to find the configuration file.");
92                         print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
93                         print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
94                         print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
95                 end
96                 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
97                 print("Good luck!");
98                 print("**************************");
99                 print("");
100                 os.exit(1);
101         end
102 end
103
104 function load_libraries()
105         -- Initialize logging
106         require "core.loggingmanager"
107         
108         -- Check runtime dependencies
109         require "util.dependencies"
110         
111         -- Load socket framework
112         server = require "net.server"
113 end     
114
115 function init_global_state()
116         bare_sessions = {};
117         full_sessions = {};
118         hosts = {};
119
120         -- Global 'prosody' object
121         prosody = {};
122         local prosody = prosody;
123         
124         prosody.bare_sessions = bare_sessions;
125         prosody.full_sessions = full_sessions;
126         prosody.hosts = hosts;
127         
128         prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR, 
129                           plugins = CFG_PLUGINDIR, data = CFG_DATADIR };
130         
131         prosody.arg = _G.arg;
132
133         prosody.events = require "util.events".new();
134         
135         prosody.platform = "unknown";
136         if os.getenv("WINDIR") then
137                 prosody.platform = "windows";
138         elseif package.config:sub(1,1) == "/" then
139                 prosody.platform = "posix";
140         end
141         
142         prosody.installed = nil;
143         if CFG_SOURCEDIR and (prosody.platform == "windows" or CFG_SOURCEDIR:match("^/")) then
144                 prosody.installed = true;
145         end
146         
147         -- Function to reload the config file
148         function prosody.reload_config()
149                 log("info", "Reloading configuration file");
150                 prosody.events.fire_event("reloading-config");
151                 local ok, level, err = config.load((rawget(_G, "CFG_CONFIGDIR") or ".").."/prosody.cfg.lua");
152                 if not ok then
153                         if level == "parser" then
154                                 log("error", "There was an error parsing the configuration file: %s", tostring(err));
155                         elseif level == "file" then
156                                 log("error", "Couldn't read the config file when trying to reload: %s", tostring(err));
157                         end
158                 end
159                 return ok, (err and tostring(level)..": "..tostring(err)) or nil;
160         end
161
162         -- Function to reopen logfiles
163         function prosody.reopen_logfiles()
164                 log("info", "Re-opening log files");
165                 eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
166                 prosody.events.fire_event("reopen-log-files");
167         end
168
169         -- Function to initiate prosody shutdown
170         function prosody.shutdown(reason)
171                 log("info", "Shutting down: %s", reason or "unknown reason");
172                 prosody.shutdown_reason = reason;
173                 prosody.events.fire_event("server-stopping", {reason = reason});
174                 server.setquitting(true);
175         end
176
177         -- Load SSL settings from config, and create a ctx table
178         local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
179         if global_ssl_ctx then
180                 local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
181                 setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
182         end
183
184         local cl = require "net.connlisteners";
185         function prosody.net_activate_ports(option, listener, default, conntype)
186                 conntype = conntype or (global_ssl_ctx and "tls") or "tcp";
187                 if not cl.get(listener) then return; end
188                 local ports = config.get("*", "core", option.."_ports") or default;
189                 if type(ports) == "number" then ports = {ports} end;
190                 
191                 if type(ports) ~= "table" then
192                         log("error", "core."..option.." is not a table");
193                 else
194                         for _, port in ipairs(ports) do
195                                 if type(port) ~= "number" then
196                                         log("error", "Non-numeric "..option.."_ports: "..tostring(port));
197                                 else
198                                         cl.start(listener, { 
199                                                 ssl = conntype ~= "tcp" and global_ssl_ctx,
200                                                 port = port,
201                                                 interface = config.get("*", "core", option.."_interface") 
202                                                         or cl.get(listener).default_interface 
203                                                         or config.get("*", "core", "interface"),
204                                                 type = conntype
205                                         });
206                                 end
207                         end
208                 end
209         end
210 end
211
212 function read_version()
213         -- Try to determine version
214         local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
215         if version_file then
216                 prosody.version = version_file:read("*a"):gsub("%s*$", "");
217                 version_file:close();
218                 if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
219                         prosody.version = "hg:"..prosody.version;
220                 end
221         else
222                 prosody.version = "unknown";
223         end
224 end
225
226 function load_secondary_libraries()
227         --- Load and initialise core modules
228         require "util.import"
229         require "core.xmlhandlers"
230         require "core.rostermanager"
231         require "core.eventmanager"
232         require "core.hostmanager"
233         require "core.modulemanager"
234         require "core.usermanager"
235         require "core.sessionmanager"
236         require "core.stanza_router"
237
238         require "net.http"
239         
240         require "util.array"
241         require "util.datetime"
242         require "util.iterators"
243         require "util.timer"
244         require "util.helpers"
245         
246         pcall(require, "util.signal") -- Not on Windows
247         
248         -- Commented to protect us from 
249         -- the second kind of people
250         --[[ 
251         pcall(require, "remdebug.engine");
252         if remdebug then remdebug.engine.start() end
253         ]]
254
255         require "net.connlisteners";
256         
257         require "util.stanza"
258         require "util.jid"
259 end
260
261 function init_data_store()
262         local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
263         require "util.datamanager".set_data_path(data_path);
264         require "util.datamanager".add_callback(function(username, host, datastore, data)
265                 if config.get(host, "core", "anonymous_login") then
266                         return false;
267                 end
268                 return username, host, datastore, data;
269         end);
270 end
271
272 function prepare_to_start()
273         -- Signal to modules that we are ready to start
274         eventmanager.fire_event("server-starting");
275         prosody.events.fire_event("server-starting");
276
277         -- start listening on sockets
278         prosody.net_activate_ports("c2s", "xmppclient", {5222});
279         prosody.net_activate_ports("s2s", "xmppserver", {5269});
280         prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp");
281         prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
282
283         prosody.start_time = os.time();
284 end     
285
286 function init_global_protection()
287         -- Catch global accesses --
288         local locked_globals_mt = { __index = function (t, k) error("Attempt to read a non-existent global '"..k.."'", 2); end, __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end }
289                 
290         function prosody.unlock_globals()
291                 setmetatable(_G, nil);
292         end
293         
294         function prosody.lock_globals()
295                 setmetatable(_G, locked_globals_mt);
296         end
297
298         -- And lock now...
299         prosody.lock_globals();
300 end
301
302 function loop()
303         -- Error handler for errors that make it this far
304         local function catch_uncaught_error(err)
305                 if type(err) == "string" and err:match("%d*: interrupted!$") then
306                         return "quitting";
307                 end
308                 
309                 log("error", "Top-level error, please report:\n%s", tostring(err));
310                 local traceback = debug.traceback("", 2);
311                 if traceback then
312                         log("error", "%s", traceback);
313                 end
314                 
315                 prosody.events.fire_event("very-bad-error", {error = err, traceback = traceback});
316         end
317         
318         while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
319                 socket.sleep(0.2);
320         end
321 end
322
323 function cleanup()
324         log("info", "Shutdown status: Cleaning up");
325         prosody.events.fire_event("server-cleanup");
326         
327         -- Ok, we're quitting I know, but we
328         -- need to do some tidying before we go :)
329         server.setquitting(false);
330         
331         log("info", "Shutdown status: Closing all active sessions");
332         for hostname, host in pairs(hosts) do
333                 log("debug", "Shutdown status: Closing client connections for %s", hostname)
334                 if host.sessions then
335                         local reason = { condition = "system-shutdown", text = "Server is shutting down" };
336                         if prosody.shutdown_reason then
337                                 reason.text = reason.text..": "..prosody.shutdown_reason;
338                         end
339                         for username, user in pairs(host.sessions) do
340                                 for resource, session in pairs(user.sessions) do
341                                         log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
342                                         session:close(reason);
343                                 end
344                         end
345                 end
346         
347                 log("debug", "Shutdown status: Closing outgoing s2s connections from %s", hostname);
348                 if host.s2sout then
349                         for remotehost, session in pairs(host.s2sout) do
350                                 if session.close then
351                                         session:close("system-shutdown");
352                                 else
353                                         log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
354                                 end
355                         end
356                 end
357         end
358
359         log("info", "Shutdown status: Closing all server connections");
360         server.closeall();
361         
362         server.setquitting(true);
363 end
364
365 read_config();
366 load_libraries();
367 init_global_state();
368 read_version();
369 log("info", "Hello and welcome to Prosody version %s", prosody.version);
370 load_secondary_libraries();
371 init_data_store();
372 init_global_protection();
373 prepare_to_start();
374
375 eventmanager.fire_event("server-started");
376 prosody.events.fire_event("server-started");
377
378 loop();
379
380 log("info", "Shutting down...");
381 cleanup();
382 eventmanager.fire_event("server-stopped");
383 prosody.events.fire_event("server-stopped");
384 log("info", "Shutdown complete");
385