Merge with 0.4.1
[prosody.git] / prosody
1 #!/usr/bin/env lua
2 -- Prosody IM v0.4
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=nil;
13 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
14 CFG_PLUGINDIR=nil;
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 if CFG_DATADIR then
25         if os.getenv("HOME") then
26                 CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
27         end
28 end
29
30 -- Required to be able to find packages installed with luarocks
31 pcall(require, "luarocks.require")
32
33
34 config = require "core.configmanager"
35
36 do
37         -- TODO: Check for other formats when we add support for them
38         -- Use lfs? Make a new conf/ dir?
39         local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
40         if not ok then
41                 print("\n");
42                 print("**************************");
43                 if level == "parser" then
44                         print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
45                         local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
46                         print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
47                         print("");
48                 elseif level == "file" then
49                         print("Prosody was unable to find the configuration file.");
50                         print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
51                         print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
52                         print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
53                 end
54                 print("More help on configuring Prosody can be found at http://prosody.im/doc/configure");
55                 print("Good luck!");
56                 print("**************************");
57                 print("");
58                 os.exit(1);
59         end
60 end
61
62 --- Initialize logging
63 require "core.loggingmanager"
64
65 --- Check runtime dependencies
66 require "util.dependencies"
67
68 --- Load socket framework
69 local server = require "net.server"
70
71
72
73 -- Maps connections to sessions --
74 sessions = {};
75 hosts = {};
76
77 --- Load and initialise core modules
78 require "util.import"
79 require "core.xmlhandlers"
80 require "core.rostermanager"
81 require "core.eventmanager"
82 require "core.hostmanager"
83 require "core.modulemanager"
84 require "core.usermanager"
85 require "core.sessionmanager"
86 require "core.stanza_router"
87
88 require "util.array"
89 require "util.iterators"
90 require "util.timer"
91
92 -- Commented to protect us from 
93 -- the second kind of people
94 --[[ 
95 pcall(require, "remdebug.engine");
96 if remdebug then remdebug.engine.start() end
97 ]]
98
99 local cl = require "net.connlisteners";
100
101 require "util.stanza"
102 require "util.jid"
103
104 ------------------------------------------------------------------------
105
106
107 ------------- Begin code without a home ---------------------
108
109 local data_path = config.get("*", "core", "data_path") or CFG_DATADIR or "data";
110 require "util.datamanager".set_data_path(data_path);
111 require "util.datamanager".set_callback(function(username, host, datastore)
112         return config.get(host, "core", "anonymous_login");
113 end);
114
115 ----------- End of out-of-place code --------------
116
117 eventmanager.fire_event("server-starting");
118
119 local global_ssl_ctx = ssl and config.get("*", "core", "ssl");
120 if global_ssl_ctx then
121         local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
122         setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
123 end
124
125 -- start listening on sockets
126 function net_activate_ports(option, listener, default, conntype)
127         local ports = config.get("*", "core", option.."_ports") or default;
128         if type(ports) == "number" then ports = {ports} end;
129         
130         if type(ports) ~= "table" then
131                 log("error", "core."..option.." is not a table");
132         else
133                 for _, port in ipairs(ports) do
134                         if type(port) ~= "number" then
135                                 log("error", "Non-numeric "..option.."_ports: "..tostring(port));
136                         else
137                                 cl.start(listener, { 
138                                         ssl = conntype ~= "tcp" and global_ssl_ctx,
139                                         port = port,
140                                         interface = config.get("*", "core", option.."_interface"),
141                                         type = conntype
142                                 });
143                         end
144                 end
145         end
146 end
147
148 net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "tcp");
149 net_activate_ports("s2s", "xmppserver", {5269}, "tcp");
150 net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl");
151
152 if config.get("*", "core", "console_enabled") then
153         if cl.get("console") then
154                 cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" })
155         else
156                 log("error", "Console is enabled, but the console module appears not to be loaded");
157         end
158 end
159
160 -- Global function to initiate prosody shutdown
161 function prosody_shutdown(reason)
162         log("info", "Shutting down: %s", reason or "unknown reason");
163         server.setquitting(true);
164 end
165
166 -- Catch global accesses --
167 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 }
168
169 function unlock_globals()
170         setmetatable(_G, nil);
171 end
172
173 function lock_globals()
174         setmetatable(_G, locked_globals_mt);
175 end
176
177 -- And lock now...
178 lock_globals();
179
180 eventmanager.fire_event("server-started");
181
182 -- Error handler for errors that make it this far
183 local function catch_uncaught_error(err)
184         if err:match("%d*: interrupted!$") then
185                 return "quitting";
186         end
187         
188         log("error", "Top-level error, please report:\n%s", tostring(err));
189         local traceback = debug.traceback("", 2);
190         if traceback then
191                 log("error", "%s", traceback);
192         end
193         
194         eventmanager.fire_event("very-bad-error", "*", err, traceback);
195 end
196
197 while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do
198         socket.sleep(0.2);
199 end
200
201 -- Ok, we're quitting I know, but we
202 -- need to do some tidying before we go :)
203 server.setquitting(false);
204
205 for hostname, host in pairs(hosts) do
206         if host.sessions then
207                 for username, user in pairs(host.sessions) do
208                         for resource, session in pairs(user.sessions) do
209                                 log("debug", "Closing connection for %s@%s/%s", username, hostname, resource);
210                                 session:close("system-shutdown");
211                         end
212                 end
213         end
214         
215         if host.s2sout then
216                 for remotehost, session in pairs(host.s2sout) do
217                         if session.close then
218                                 session:close("system-shutdown");
219                         else
220                                 log("warn", "Unable to close outgoing s2s session to %s, no session:close()?!", remotehost);
221                         end
222                 end
223         end
224 end
225
226 server.closeall();