2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
10 local st
= require
"util.stanza";
11 local dataform_new
= require
"util.dataforms".new
;
12 local usermanager_user_exists
= require
"core.usermanager".user_exists
;
13 local usermanager_create_user
= require
"core.usermanager".create_user
;
14 local usermanager_delete_user
= require
"core.usermanager".delete_user
;
15 local nodeprep
= require
"util.encodings".stringprep
.nodeprep
;
17 local additional_fields
= module
:get_option("additional_registration_fields", {});
18 local require_encryption
= module
:get_option_boolean("c2s_require_encryption",
19 module
:get_option_boolean("require_encryption", false));
22 module
:depends("register_limits");
25 local account_details
= module
:open_store("account_details");
28 username
= { name
= "username", type = "text-single", label
= "Username", required
= true };
29 password
= { name
= "password", type = "text-private", label
= "Password", required
= true };
30 nick
= { name
= "nick", type = "text-single", label
= "Nickname" };
31 name
= { name
= "name", type = "text-single", label
= "Full Name" };
32 first
= { name
= "first", type = "text-single", label
= "Given Name" };
33 last
= { name
= "last", type = "text-single", label
= "Family Name" };
34 email
= { name
= "email", type = "text-single", label
= "Email" };
35 address
= { name
= "address", type = "text-single", label
= "Street" };
36 city
= { name
= "city", type = "text-single", label
= "City" };
37 state
= { name
= "state", type = "text-single", label
= "State" };
38 zip
= { name
= "zip", type = "text-single", label
= "Postal code" };
39 phone
= { name
= "phone", type = "text-single", label
= "Telephone number" };
40 url
= { name
= "url", type = "text-single", label
= "Webpage" };
41 date = { name
= "date", type = "text-single", label
= "Birth date" };
44 local title
= module
:get_option_string("registration_title",
45 "Creating a new account");
46 local instructions
= module
:get_option_string("registration_instructions",
47 "Choose a username and password for use with this service.");
49 local registration_form
= dataform_new
{
51 instructions
= instructions
;
57 local registration_query
= st
.stanza("query", {xmlns
= "jabber:iq:register"})
58 :tag("instructions"):text(instructions
):up()
60 :tag("password"):up();
62 for _
, field
in ipairs(additional_fields
) do
63 if type(field
) == "table" then
64 registration_form
[#registration_form
+ 1] = field
;
65 elseif field_map
[field
] or field_map
[field
:sub(1, -2)] then
66 if field
:match("%+$") then
67 field
= field
:sub(1, -2);
68 field_map
[field
].required
= true;
71 registration_form
[#registration_form
+ 1] = field_map
[field
];
72 registration_query
:tag(field
):up();
74 module
:log("error", "Unknown field %q", field
);
77 registration_query
:add_child(registration_form
:form());
79 local register_stream_feature
= st
.stanza("register", {xmlns
="http://jabber.org/features/iq-register"}):up();
80 module
:hook("stream-features", function(event
)
81 local session
, features
= event
.origin
, event
.features
;
83 -- Advertise registration to unauthorized clients only.
84 if session
.type ~= "c2s_unauthed" or (require_encryption
and not session
.secure
) then
88 features
:add_child(register_stream_feature
);
91 local function parse_response(query
)
92 local form
= query
:get_child("x", "jabber:x:data");
94 return registration_form
:data(form
);
98 for _
, field
in ipairs(registration_form
) do
99 local name
, required
= field
.name
, field
.required
;
100 if field_map
[name
] then
101 data
[name
] = query
:get_child_text(name
);
102 if (not data
[name
] or #data
[name
] == 0) and required
then
103 errors
[name
] = "Required value missing";
114 -- In-band registration
115 module
:hook("stanza/iq/jabber:iq:register:query", function(event
)
116 local session
, stanza
= event
.origin
, event
.stanza
;
117 local log = session
.log or module
._log
;
119 if session
.type ~= "c2s_unauthed" then
120 log("debug", "Attempted registration when disabled or already authenticated");
121 session
.send(st
.error_reply(stanza
, "cancel", "service-unavailable"));
125 if require_encryption
and not session
.secure
then
126 session
.send(st
.error_reply(stanza
, "modify", "policy-violation", "Encryption is required"));
130 local query
= stanza
.tags
[1];
131 if stanza
.attr
.type == "get" then
132 local reply
= st
.reply(stanza
);
133 reply
:add_child(registration_query
);
138 -- stanza.attr.type == "set"
139 if query
.tags
[1] and query
.tags
[1].name
== "remove" then
140 session
.send(st
.error_reply(stanza
, "auth", "registration-required"));
144 local data
, errors
= parse_response(query
);
146 log("debug", "Error parsing registration form:");
147 local textual_errors
= {};
148 for field
, err
in pairs(errors
) do
149 log("debug", "Field %q: %s", field
, err
);
150 table.insert(textual_errors
, ("%s: %s"):format(field
:gsub("^%a", string.upper
), err
));
152 session
.send(st
.error_reply(stanza
, "modify", "not-acceptable", table.concat(textual_errors
, "\n")));
156 local username
, password
= nodeprep(data
.username
), data
.password
;
157 data
.username
, data
.password
= nil, nil;
158 local host
= module
.host
;
159 if not username
or username
== "" then
160 log("debug", "The requested username is invalid.");
161 session
.send(st
.error_reply(stanza
, "modify", "not-acceptable", "The requested username is invalid."));
165 local user
= { username
= username
, password
= password
, host
= host
, additional
= data
, ip
= session
.ip
, session
= session
, allowed
= true }
166 module
:fire_event("user-registering", user
);
167 if not user
.allowed
then
168 log("debug", "Registration disallowed by module: %s", user
.reason
or "no reason given");
169 session
.send(st
.error_reply(stanza
, "modify", "not-acceptable", user
.reason
));
173 if usermanager_user_exists(username
, host
) then
174 log("debug", "Attempt to register with existing username");
175 session
.send(st
.error_reply(stanza
, "cancel", "conflict", "The requested username already exists."));
179 -- TODO unable to write file, file may be locked, etc, what's the correct error?
180 local error_reply
= st
.error_reply(stanza
, "wait", "internal-server-error", "Failed to write data to disk.");
181 if usermanager_create_user(username
, password
, host
) then
182 data
.registered
= os
.time();
183 if not account_details
:set(username
, data
) then
184 log("debug", "Could not store extra details");
185 usermanager_delete_user(username
, host
);
186 session
.send(error_reply
);
189 session
.send(st
.reply(stanza
)); -- user created!
190 log("info", "User account created: %s@%s", username
, host
);
191 module
:fire_event("user-registered", {
192 username
= username
, host
= host
, source
= "mod_register",
193 session
= session
});
195 log("debug", "Could not create user");
196 session
.send(error_reply
);