Let Google Hangouts contacts appear offline
[prosody.git] / util / prosodyctl.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 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
10 local config = require "core.configmanager";
11 local encodings = require "util.encodings";
12 local stringprep = encodings.stringprep;
13 local storagemanager = require "core.storagemanager";
14 local usermanager = require "core.usermanager";
15 local signal = require "util.signal";
16 local set = require "util.set";
17 local lfs = require "lfs";
18 local pcall = pcall;
19
20 local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep;
21
22 local io, os = io, os;
23 local print = print;
24 local tostring, tonumber = tostring, tonumber;
25
26 local CFG_SOURCEDIR = _G.CFG_SOURCEDIR;
27
28 local _G = _G;
29 local prosody = prosody;
30
31 module "prosodyctl"
32
33 -- UI helpers
34 function show_message(msg, ...)
35         print(msg:format(...));
36 end
37
38 function show_warning(msg, ...)
39         print(msg:format(...));
40 end
41
42 function show_usage(usage, desc)
43         print("Usage: ".._G.arg[0].." "..usage);
44         if desc then
45                 print(" "..desc);
46         end
47 end
48
49 function getchar(n)
50         local stty_ret = os.execute("stty raw -echo 2>/dev/null");
51         local ok, char;
52         if stty_ret == 0 then
53                 ok, char = pcall(io.read, n or 1);
54                 os.execute("stty sane");
55         else
56                 ok, char = pcall(io.read, "*l");
57                 if ok then
58                         char = char:sub(1, n or 1);
59                 end
60         end
61         if ok then
62                 return char;
63         end
64 end
65
66 function getpass()
67         local stty_ret = os.execute("stty -echo 2>/dev/null");
68         if stty_ret ~= 0 then
69                 io.write("\027[08m"); -- ANSI 'hidden' text attribute
70         end
71         local ok, pass = pcall(io.read, "*l");
72         if stty_ret == 0 then
73                 os.execute("stty sane");
74         else
75                 io.write("\027[00m");
76         end
77         io.write("\n");
78         if ok then
79                 return pass;
80         end
81 end
82
83 function show_yesno(prompt)
84         io.write(prompt, " ");
85         local choice = getchar():lower();
86         io.write("\n");
87         if not choice:match("%a") then
88                 choice = prompt:match("%[.-(%U).-%]$");
89                 if not choice then return nil; end
90         end
91         return (choice == "y");
92 end
93
94 function read_password()
95         local password;
96         while true do
97                 io.write("Enter new password: ");
98                 password = getpass();
99                 if not password then
100                         show_message("No password - cancelled");
101                         return;
102                 end
103                 io.write("Retype new password: ");
104                 if getpass() ~= password then
105                         if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then
106                                 return;
107                         end
108                 else
109                         break;
110                 end
111         end
112         return password;
113 end
114
115 -- Server control
116 function adduser(params)
117         local user, host, password = nodeprep(params.user), nameprep(params.host), params.password;
118         if not user then
119                 return false, "invalid-username";
120         elseif not host then
121                 return false, "invalid-hostname";
122         end
123
124         local provider = prosody.hosts[host].users;
125         if not(provider) or provider.name == "null" then
126                 usermanager.initialize_host(host);
127         end
128         storagemanager.initialize_host(host);
129         
130         local ok, errmsg = usermanager.create_user(user, password, host);
131         if not ok then
132                 return false, errmsg;
133         end
134         return true;
135 end
136
137 function user_exists(params)
138         local user, host, password = nodeprep(params.user), nameprep(params.host), params.password;
139         local provider = prosody.hosts[host].users;
140         if not(provider) or provider.name == "null" then
141                 usermanager.initialize_host(host);
142         end
143         storagemanager.initialize_host(host);
144         
145         return usermanager.user_exists(user, host);
146 end
147
148 function passwd(params)
149         if not _M.user_exists(params) then
150                 return false, "no-such-user";
151         end
152         
153         return _M.adduser(params);
154 end
155
156 function deluser(params)
157         if not _M.user_exists(params) then
158                 return false, "no-such-user";
159         end
160         params.password = nil;
161         
162         return _M.adduser(params);
163 end
164
165 function getpid()
166         local pidfile = config.get("*", "core", "pidfile");
167         if not pidfile then
168                 return false, "no-pidfile";
169         end
170         
171         local modules_enabled = set.new(config.get("*", "core", "modules_enabled"));
172         if not modules_enabled:contains("posix") then
173                 return false, "no-posix";
174         end
175         
176         local file, err = io.open(pidfile, "r+");
177         if not file then
178                 return false, "pidfile-read-failed", err;
179         end
180         
181         local locked, err = lfs.lock(file, "w");
182         if locked then
183                 file:close();
184                 return false, "pidfile-not-locked";
185         end
186         
187         local pid = tonumber(file:read("*a"));
188         file:close();
189         
190         if not pid then
191                 return false, "invalid-pid";
192         end
193         
194         return true, pid;
195 end
196
197 function isrunning()
198         local ok, pid, err = _M.getpid();
199         if not ok then
200                 if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then
201                         -- Report as not running, since we can't open the pidfile
202                         -- (it probably doesn't exist)
203                         return true, false;
204                 end
205                 return ok, pid;
206         end
207         return true, signal.kill(pid, 0) == 0;
208 end
209
210 function start()
211         local ok, ret = _M.isrunning();
212         if not ok then
213                 return ok, ret;
214         end
215         if ret then
216                 return false, "already-running";
217         end
218         if not CFG_SOURCEDIR then
219                 os.execute("./prosody");
220         else
221                 os.execute(CFG_SOURCEDIR.."/../../bin/prosody");
222         end
223         return true;
224 end
225
226 function stop()
227         local ok, ret = _M.isrunning();
228         if not ok then
229                 return ok, ret;
230         end
231         if not ret then
232                 return false, "not-running";
233         end
234         
235         local ok, pid = _M.getpid()
236         if not ok then return false, pid; end
237         
238         signal.kill(pid, signal.SIGTERM);
239         return true;
240 end