5e93ed008b3cbcf0a09704bdb3074083d9abcd61
[prosody.git] / plugins / mod_register.lua
1 -- Prosody IM v0.2
2 -- Copyright (C) 2008 Matthew Wild
3 -- Copyright (C) 2008 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
11 local st = require "util.stanza";
12 local usermanager_user_exists = require "core.usermanager".user_exists;
13 local usermanager_create_user = require "core.usermanager".create_user;
14 local datamanager_store = require "util.datamanager".store;
15 local os_time = os.time;
16
17 module:add_feature("jabber:iq:register");
18
19 module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza)
20         if stanza.tags[1].name == "query" then
21                 local query = stanza.tags[1];
22                 if stanza.attr.type == "get" then
23                         local reply = st.reply(stanza);
24                         reply:tag("query", {xmlns = "jabber:iq:register"})
25                                 :tag("registered"):up()
26                                 :tag("username"):text(session.username):up()
27                                 :tag("password"):up();
28                         session.send(reply);
29                 elseif stanza.attr.type == "set" then
30                         if query.tags[1] and query.tags[1].name == "remove" then
31                                 -- TODO delete user auth data, send iq response, kick all user resources with a <not-authorized/>, delete all user data
32                                 --session.send(st.error_reply(stanza, "cancel", "not-allowed"));
33                                 --return;
34                                 usermanager_create_user(session.username, nil, session.host); -- Disable account
35                                 -- FIXME the disabling currently allows a different user to recreate the account
36                                 -- we should add an in-memory account block mode when we have threading
37                                 session.send(st.reply(stanza));
38                                 local roster = session.roster;
39                                 for _, session in pairs(hosts[session.host].sessions[session.username].sessions) do -- disconnect all resources
40                                         session:disconnect({condition = "not-authorized", text = "Account deleted"});
41                                 end
42                                 -- TODO datamanager should be able to delete all user data itself
43                                 datamanager.store(session.username, session.host, "roster", nil);
44                                 datamanager.store(session.username, session.host, "vcard", nil);
45                                 datamanager.store(session.username, session.host, "private", nil);
46                                 datamanager.store(session.username, session.host, "offline", nil);
47                                 local bare = session.username.."@"..session.host;
48                                 for jid, item in pairs(roster) do
49                                         if jid ~= "pending" then
50                                                 if item.subscription == "both" or item.subscription == "to" then
51                                                         -- TODO unsubscribe
52                                                 end
53                                                 if item.subscription == "both" or item.subscription == "from" then
54                                                         -- TODO unsubscribe
55                                                 end
56                                         end
57                                 end
58                                 datamanager.store(session.username, session.host, "accounts", nil); -- delete accounts datastore at the end
59                         else
60                                 local username = query:child_with_name("username");
61                                 local password = query:child_with_name("password");
62                                 if username and password then
63                                         -- FIXME shouldn't use table.concat
64                                         username = table.concat(username);
65                                         password = table.concat(password);
66                                         if username == session.username then
67                                                 if usermanager_create_user(username, password, session.host) then -- password change -- TODO is this the right way?
68                                                         session.send(st.reply(stanza));
69                                                 else
70                                                         -- TODO unable to write file, file may be locked, etc, what's the correct error?
71                                                         session.send(st.error_reply(stanza, "wait", "internal-server-error"));
72                                                 end
73                                         else
74                                                 session.send(st.error_reply(stanza, "modify", "bad-request"));
75                                         end
76                                 else
77                                         session.send(st.error_reply(stanza, "modify", "bad-request"));
78                                 end
79                         end
80                 end
81         else
82                 session.send(st.error_reply(stanza, "cancel", "service-unavailable"));
83         end;
84 end);
85
86 local recent_ips = {};
87 local min_seconds_between_registrations = config.get(module.host, "core", "min_seconds_between_registrations");
88 local whitelist_only = config.get(module.host, "core", "whitelist_registration_only");
89 local whitelisted_ips = config.get(module.host, "core", "registration_whitelist") or { "127.0.0.1" };
90 local blacklisted_ips = config.get(module.host, "core", "registration_blacklist") or {};
91
92 for _, ip in ipairs(whitelisted_ips) do whitelisted_ips[ip] = true; end
93 for _, ip in ipairs(blacklisted_ips) do blacklisted_ips[ip] = true; end
94
95 module:add_iq_handler("c2s_unauthed", "jabber:iq:register", function (session, stanza)
96         if config.get(module.host, "core", "allow_registration") == false then
97                 session.send(st.error_reply(stanza, "cancel", "service-unavailable"));
98         elseif stanza.tags[1].name == "query" then
99                 local query = stanza.tags[1];
100                 if stanza.attr.type == "get" then
101                         local reply = st.reply(stanza);
102                         reply:tag("query", {xmlns = "jabber:iq:register"})
103                                 :tag("instructions"):text("Choose a username and password for use with this service."):up()
104                                 :tag("username"):up()
105                                 :tag("password"):up();
106                         session.send(reply);
107                 elseif stanza.attr.type == "set" then
108                         if query.tags[1] and query.tags[1].name == "remove" then
109                                 session.send(st.error_reply(stanza, "auth", "registration-required"));
110                         else
111                                 local username = query:child_with_name("username");
112                                 local password = query:child_with_name("password");
113                                 if username and password then
114                                         -- Check that the user is not blacklisted or registering too often
115                                         if blacklisted_ips[session.ip] or (whitelist_only and not whitelisted_ips[session.ip]) then
116                                                         session.send(st.error_reply(stanza, "cancel", "not-acceptable"));
117                                                         return;
118                                         elseif min_seconds_between_registrations and not whitelisted_ips[session.ip] then
119                                                 if not recent_ips[session.ip] then
120                                                         recent_ips[session.ip] = { time = os_time(), count = 1 };
121                                                 else
122                                                 
123                                                         local ip = recent_ips[session.ip];
124                                                         ip.count = ip.count + 1;
125                                                         
126                                                         if os_time() - ip.time < min_seconds_between_registrations then
127                                                                 ip.time = os_time();
128                                                                 session.send(st.error_reply(stanza, "cancel", "not-acceptable"));
129                                                                 return;
130                                                         end
131                                                         ip.time = os_time();
132                                                 end
133                                         end
134                                         -- FIXME shouldn't use table.concat
135                                         username = table.concat(username);
136                                         password = table.concat(password);
137                                         if usermanager_user_exists(username, session.host) then
138                                                 session.send(st.error_reply(stanza, "cancel", "conflict"));
139                                         else
140                                                 if usermanager_create_user(username, password, session.host) then
141                                                         session.send(st.reply(stanza)); -- user created!
142                                                 else
143                                                         -- TODO unable to write file, file may be locked, etc, what's the correct error?
144                                                         session.send(st.error_reply(stanza, "wait", "internal-server-error"));
145                                                 end
146                                         end
147                                 else
148                                         session.send(st.error_reply(stanza, "modify", "not-acceptable"));
149                                 end
150                         end
151                 end
152         else
153                 session.send(st.error_reply(stanza, "cancel", "service-unavailable"));
154         end;
155 end);
156