import files from my cvs repository
This commit is contained in:
parent
ac47a4133b
commit
555cc7d254
54
mod_email/README.markdown
Normal file
54
mod_email/README.markdown
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
labels:
|
||||
- 'Stage-Stable'
|
||||
...
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Gives server owners and module developers the ability to send emails.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
For users and developers, add the following to your configuration file and
|
||||
edit depending on your SMTP setup:
|
||||
|
||||
smtp = {
|
||||
origin = "username@hostname.domainname";
|
||||
|
||||
-- Use sendmail and have sendmail handle SMTP
|
||||
exec = "/usr/sbin/sendmail";
|
||||
|
||||
-- Use SMTP directly
|
||||
--user = "username";
|
||||
--password = "password";
|
||||
--server = "localhost";
|
||||
--port = 25;
|
||||
--domain = "hostname.domainname";
|
||||
}
|
||||
|
||||
For developers you can do something like this to send emails from your module:
|
||||
|
||||
local moduleapi = require "core.moduleapi";
|
||||
module:depends("email");
|
||||
local ok, err = moduleapi:send_email({to = mail_address, subject = mail_subject, body = mail_body});
|
||||
if not ok then
|
||||
module:log("error", "Failed to deliver to %s: %s", tostring(mail_address), tostring(err));
|
||||
end
|
||||
|
||||
Todo
|
||||
----
|
||||
|
||||
- Loading socket.smtp causes a stack trace on Prosody start up.
|
||||
Everything still works fine, but this should probably be fixed.
|
||||
|
||||
- No SSL/STARTTLS support. This will require implementing something like LuaSec.
|
||||
If needed, I would recommend to just set up OpenSMTPD and use sendmail.
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
----- --------------
|
||||
0.12 Works
|
||||
----- --------------
|
@ -6,44 +6,42 @@ labels:
|
||||
Introduction
|
||||
============
|
||||
|
||||
This module aims to help in the procedure of user password restoration.
|
||||
This module aims to help user password restoration.
|
||||
To start the restoration, the user must go to an URL provided by this
|
||||
module, fill the JID and email and submit the request.
|
||||
|
||||
The module will generate a token valid for 24h and send an email with a
|
||||
specially crafted url to the vCard email address. If the user goes to
|
||||
this url, will be able to change his password.
|
||||
The module will generate a token valid for 1h and send an email with a
|
||||
specially crafted URL to the email address set in account details (not vCard).
|
||||
If the user goes to this URL, the user will be able to change his password.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Simply add "email\_pass" to your modules\_enabled list and copy files
|
||||
"**mod\_email\_pass.lua**" and "**vcard.lib.lua**" to prosody modules
|
||||
folder. This module need to that **https\_host** or **http\_host** must
|
||||
be configured. This parameter is necessary to construct the URL that has
|
||||
been sended to the user.
|
||||
Add "email\_pass" to your modules\_enabled list and copy the mod\_email\_pass
|
||||
folder to the Prosody modules folder.
|
||||
|
||||
This module only send emails to the vCard user email address, then the
|
||||
user must set this address in order to be capable of do the restoration.
|
||||
This module also requires the "email" module which you can find here:
|
||||
https://modules.prosody.im/mod\_email.html
|
||||
|
||||
This module only sends emails to the user email address set in Prosody account
|
||||
details.
|
||||
The user must set this email address in order to be capable of do the
|
||||
restoration by going to: /email\_pass/changemail.html
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
--------------- ------------------------------------------------------------
|
||||
smtp\_server The Host/ip of your SMTP server
|
||||
smtp\_port Port used by your SMTP server. Default 25
|
||||
smtp\_ssl Use of SMTP SSL legacy (No STARTTLS)
|
||||
smtp\_user Username used to do SMTP auth
|
||||
smtp\_pass Password used to do SMTP auth
|
||||
smtp\_address EMail address that will be apears as From in mails
|
||||
msg\_subject Subject used for messages/mails
|
||||
msg\_body Message send when password has been changed
|
||||
url\_path Path where the module will be visible. Default /resetpass/
|
||||
--------------- ------------------------------------------------------------
|
||||
--------------- ------------------------------------------------------------ ---------------------
|
||||
token\_expire Time in seconds before token expires 3600
|
||||
attempt\_limit Maximum attempts for 'update email'/'reset with token' page 10
|
||||
attempt\_wait Time in seconds to block IP from going beyond limit 1800
|
||||
email\_pass\_url URL prefix used for sending the token https://hostname:5281
|
||||
--------------- ------------------------------------------------------------ ---------------------
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
----- -------
|
||||
0.12 Works
|
||||
0.9 Works
|
||||
----- -------
|
||||
|
@ -3,23 +3,13 @@ local st = require "util.stanza";
|
||||
local nodeprep = require "util.encodings".stringprep.nodeprep;
|
||||
local usermanager = require "core.usermanager";
|
||||
local http = require "net.http";
|
||||
local vcard = module:require "vcard";
|
||||
local datetime = require "util.datetime";
|
||||
local timer = require "util.timer";
|
||||
local jidutil = require "util.jid";
|
||||
local moduleapi = require "core.moduleapi";
|
||||
|
||||
-- SMTP related params. Readed from config
|
||||
local os_time = os.time;
|
||||
local smtp = require "socket.smtp";
|
||||
local smtp_server = module:get_option_string("smtp_server", "localhost");
|
||||
local smtp_port = module:get_option_string("smtp_port", "25");
|
||||
local smtp_ssl = module:get_option_boolean("smtp_ssl", false);
|
||||
local smtp_user = module:get_option_string("smtp_username");
|
||||
local smtp_pass = module:get_option_string("smtp_password");
|
||||
local smtp_address = module:get_option("smtp_from") or ((smtp_user or "no-responder").."@"..(smtp_server or module.host));
|
||||
local mail_subject = module:get_option_string("msg_subject")
|
||||
local mail_body = module:get_option_string("msg_body");
|
||||
local url_path = module:get_option_string("url_path", "/resetpass");
|
||||
local token_expire = module:get_option_number("token_expire", 3600);
|
||||
|
||||
|
||||
-- This table has the tokens submited by the server
|
||||
@ -27,30 +17,18 @@ tokens_mails = {};
|
||||
tokens_expiration = {};
|
||||
|
||||
-- URL
|
||||
local https_host = module:get_option_string("https_host");
|
||||
local http_host = module:get_option_string("http_host");
|
||||
local https_port = module:get_option("https_ports", { 443 });
|
||||
local http_port = module:get_option("http_ports", { 80 });
|
||||
local email_pass_url = module:get_option_string("email_pass_url", "https://" .. module.host .. ":5281");
|
||||
|
||||
local timer_repeat = 120; -- repeat after 120 secs
|
||||
|
||||
function enablessl()
|
||||
local sock = socket.tcp()
|
||||
return setmetatable({
|
||||
connect = function(_, host, port)
|
||||
local r, e = sock:connect(host, port)
|
||||
if not r then return r, e end
|
||||
sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'})
|
||||
return sock:dohandshake()
|
||||
end
|
||||
}, {
|
||||
__index = function(t,n)
|
||||
return function(_, ...)
|
||||
return sock[n](sock, ...)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
local account_details = module:open_store("account_details");
|
||||
|
||||
local attempt_limit = module:get_option_number("attempt_limit", 10);
|
||||
local attempt_wait = module:get_option_number("attempt_wait", 1800);
|
||||
attempt_wait_list = {};
|
||||
attempt_wait_list_time = {};
|
||||
|
||||
module:depends("email");
|
||||
|
||||
function template(data)
|
||||
-- Like util.template, but deals with plain text
|
||||
@ -68,91 +46,139 @@ local function render(template, data)
|
||||
return tostring(template.apply(data));
|
||||
end
|
||||
|
||||
function send_email(address, smtp_address, message_text, subject)
|
||||
local rcpt = "<"..address..">";
|
||||
|
||||
local mesgt = {
|
||||
headers = {
|
||||
to = address;
|
||||
subject = subject or ("Jabber password reset "..jid_bare(from_address));
|
||||
};
|
||||
body = message_text;
|
||||
};
|
||||
local ok, err = nil;
|
||||
|
||||
if not smtp_ssl then
|
||||
ok, err = smtp.send{ from = smtp_address, rcpt = rcpt, source = smtp.message(mesgt),
|
||||
server = smtp_server, user = smtp_user, password = smtp_pass, port = 25 };
|
||||
else
|
||||
ok, err = smtp.send{ from = smtp_address, rcpt = rcpt, source = smtp.message(mesgt),
|
||||
server = smtp_server, user = smtp_user, password = smtp_pass, port = smtp_port, create = enablessl };
|
||||
end
|
||||
|
||||
if not ok then
|
||||
module:log("error", "Failed to deliver to %s: %s", tostring(address), tostring(err));
|
||||
return;
|
||||
end
|
||||
return true;
|
||||
end
|
||||
|
||||
local vCard_mt = {
|
||||
__index = function(t, k)
|
||||
if type(k) ~= "string" then return nil end
|
||||
for i=1,#t do
|
||||
local t_i = rawget(t, i);
|
||||
if t_i and t_i.name == k then
|
||||
rawset(t, k, t_i);
|
||||
return t_i;
|
||||
end
|
||||
end
|
||||
end
|
||||
};
|
||||
|
||||
local function get_user_vcard(user, host)
|
||||
local vCard = dm_load(user, host or base_host, "vcard");
|
||||
if vCard then
|
||||
vCard = st.deserialize(vCard);
|
||||
vCard = vcard.from_xep54(vCard);
|
||||
return setmetatable(vCard, vCard_mt);
|
||||
end
|
||||
end
|
||||
|
||||
local changemail_tpl = get_template("changemail",".html");
|
||||
local changepass_tpl = get_template("changepass",".html");
|
||||
local sendmail_success_tpl = get_template("sendmailok",".html");
|
||||
local reset_success_tpl = get_template("resetok",".html");
|
||||
local update_success_tpl = get_template("updateok",".html");
|
||||
local token_tpl = get_template("token",".html");
|
||||
|
||||
function generate_page(event, display_options)
|
||||
function generate_page(event, lang, display_options)
|
||||
local request = event.request;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "Reseteo de la contraseña de tu cuenta Jabber";
|
||||
s_username = "Nombre de Usuario";
|
||||
s_usernamemessage = "Introduce tu nombre de usuario";
|
||||
s_email = "Email";
|
||||
s_emailmessage = "Introduce tu email";
|
||||
s_send = "¡Enviar!";
|
||||
s_text = "Al pulsar sobre el botón, se enviará a la dirección de correo que figura en tu cuenta un enlace en el que deberás entrar.";
|
||||
else
|
||||
s_title = "Reset your Jabber account password";
|
||||
s_username = "Username";
|
||||
s_usernamemessage = "Enter your username";
|
||||
s_email = "Email";
|
||||
s_emailmessage = "Enter your email";
|
||||
s_send = "Send!";
|
||||
s_text = "When you click the button, a link will be sent to the email address in your account.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
return render(changepass_tpl, {
|
||||
path = request.path; hostname = module.host;
|
||||
path = request.path;
|
||||
hostname = module.host;
|
||||
notice = display_options and display_options.register_error or "";
|
||||
})
|
||||
s_title = s_title;
|
||||
s_username = s_username;
|
||||
s_usernamemessage = s_usernamemessage;
|
||||
s_email = s_email;
|
||||
s_emailmessage = s_emailmessage;
|
||||
s_send = s_send;
|
||||
s_text = s_text;
|
||||
s_lang = lang;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_token_page(event, display_options)
|
||||
function generate_token_page(event, lang, display_options)
|
||||
local request = event.request;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "Reseto de la contraseña de tu cuenta Jabber";
|
||||
s_token = "Token";
|
||||
s_password = "Contraseña";
|
||||
s_passwordconfirm = "Contraseña (Confirmació)";
|
||||
s_change = "¡Cambiar!";
|
||||
else
|
||||
s_title = "Reset the password for your Jabber account";
|
||||
s_token = "Token";
|
||||
s_password = "Password";
|
||||
s_passwordconfirm = "Password (Confirm)";
|
||||
s_change = "Change!";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
return render(token_tpl, {
|
||||
path = request.path; hostname = module.host;
|
||||
path = request.path;
|
||||
hostname = module.host;
|
||||
token = request.url.query;
|
||||
notice = display_options and display_options.register_error or "";
|
||||
})
|
||||
s_title = s_title;
|
||||
s_token = s_token;
|
||||
s_password = s_password;
|
||||
s_passwordconfirm = s_passwordconfirm;
|
||||
s_change = s_change;
|
||||
s_lang = lang;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_mail_page(event, lang, display_options)
|
||||
local request = event.request;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "Cambiar la eMail de tu cuenta Jabber";
|
||||
s_username = "Nombre de Usuario";
|
||||
s_usernamemessage = "Introduce tu nombre de usuario";
|
||||
s_password = "Contraseña";
|
||||
s_passwordmessage = "Introduce tu contraseña";
|
||||
s_email = "Email";
|
||||
s_emailmessage = "Introduce tu email";
|
||||
s_change = "¡Cambiar!";
|
||||
else
|
||||
s_title = "Change the eMail of your Jabber account";
|
||||
s_username = "Username";
|
||||
s_usernamemessage = "Enter your username";
|
||||
s_password = "Password";
|
||||
s_passwordmessage = "Enter your password";
|
||||
s_email = "Email";
|
||||
s_emailmessage = "Enter your email";
|
||||
s_change = "Change!";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
return render(changemail_tpl, {
|
||||
path = request.path;
|
||||
hostname = module.host;
|
||||
notice = display_options and display_options.register_error or "";
|
||||
s_title = s_title;
|
||||
s_username = s_username;
|
||||
s_usernamemessage = s_usernamemessage;
|
||||
s_password = s_password;
|
||||
s_passwordmessage = s_passwordmessage;
|
||||
s_email = s_email;
|
||||
s_emailmessage = s_emailmessage;
|
||||
s_change = s_change;
|
||||
s_lang = lang;
|
||||
});
|
||||
end
|
||||
|
||||
function generateToken(address)
|
||||
math.randomseed(os.time())
|
||||
length = 16
|
||||
|
||||
if length < 1 then return nil end
|
||||
|
||||
local array = {}
|
||||
for i = 1, length, 2 do
|
||||
array[i] = string.char(math.random(48,57))
|
||||
array[i+1] = string.char(math.random(97,122))
|
||||
end
|
||||
|
||||
local token = table.concat(array);
|
||||
if not tokens_mails[token] then
|
||||
|
||||
tokens_mails[token] = address;
|
||||
tokens_expiration[token] = os.time();
|
||||
return token
|
||||
@ -166,7 +192,7 @@ function isExpired(token)
|
||||
if not tokens_expiration[token] then
|
||||
return nil;
|
||||
end
|
||||
if os.difftime(os.time(), tokens_expiration[token]) < 86400 then -- 86400 secs == 24h
|
||||
if os.difftime(os.time(), tokens_expiration[token]) < token_expire then
|
||||
-- token is valid yet
|
||||
return nil;
|
||||
else
|
||||
@ -197,22 +223,10 @@ function hasTokenActive(address)
|
||||
return nil;
|
||||
end
|
||||
|
||||
function generateUrl(token)
|
||||
function generateUrl(token, path)
|
||||
local url;
|
||||
|
||||
if https_host then
|
||||
url = "https://" .. https_host;
|
||||
else
|
||||
url = "http://" .. http_host;
|
||||
end
|
||||
|
||||
if https_port then
|
||||
url = url .. ":" .. https_port[1];
|
||||
else
|
||||
url = url .. ":" .. http_port[1];
|
||||
end
|
||||
|
||||
url = url .. url_path .. "token.html?" .. token;
|
||||
url = email_pass_url .. path .. "token.html?" .. token;
|
||||
|
||||
return url;
|
||||
end
|
||||
@ -225,93 +239,411 @@ function sendMessage(jid, subject, message)
|
||||
end
|
||||
|
||||
function send_token_mail(form, origin)
|
||||
local prepped_username = nodeprep(form.username);
|
||||
if form.langchange == "true" then
|
||||
return nil;
|
||||
end
|
||||
|
||||
local lang = form.lang;
|
||||
local prepped_username = nodeprep(form.username, true);
|
||||
local prepped_mail = form.email;
|
||||
local jid = prepped_username .. "@" .. module.host;
|
||||
|
||||
if not prepped_username then
|
||||
return nil, "El usuario contiene caracteres incorrectos";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El nombre de usuario contiene caracteres incorrectos";
|
||||
else
|
||||
return nil, "The username contains invalid characters";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if #prepped_username == 0 then
|
||||
return nil, "El campo usuario está vacio";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de nombre de usuario está vacío";
|
||||
else
|
||||
return nil, "The username text field is empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if not usermanager.user_exists(prepped_username, module.host) then
|
||||
return nil, "El usuario NO existe";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El usuario no existe";
|
||||
else
|
||||
return nil, "The user does not exist";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
if #prepped_mail == 0 then
|
||||
return nil, "El campo email está vacio";
|
||||
end
|
||||
|
||||
local vcarduser = get_user_vcard(prepped_username, module.host);
|
||||
|
||||
if not vcarduser then
|
||||
return nil, "User has not vCard";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de email está vacío";
|
||||
else
|
||||
if not vcarduser.EMAIL then
|
||||
return nil, "Esa cuente no tiene ningún email configurado en su vCard";
|
||||
return nil, "The email text field is empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
email = string.lower(vcarduser.EMAIL[1]);
|
||||
local account_detail_table = account_details:get(prepped_username);
|
||||
if account_detail_table == nil then
|
||||
account_detail_table = {["email"] = ""};
|
||||
end
|
||||
local account_detail_email = account_detail_table["email"];
|
||||
if not account_detail_email then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El cuente no tiene ningún email configurado";
|
||||
else
|
||||
return nil, "The account has no email configured";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
email = string.lower( account_detail_email );
|
||||
|
||||
if #email == 0 then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El cuente no tiene ningún email configurado";
|
||||
else
|
||||
return nil, "The account has no email configured";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
if email ~= string.lower(prepped_mail) then
|
||||
return nil, "Dirección eMail incorrecta";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "eMail incorrecta";
|
||||
else
|
||||
return nil, "Incorrect eMail";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
-- Check if has already a valid token, not used yet.
|
||||
if hasTokenActive(jid) then
|
||||
local valid_until = tokens_expiration[hasTokenActive(jid)] + 86400;
|
||||
return nil, "Ya tienes una petición de restablecimiento de clave válida hasta: " .. datetime.date(valid_until) .. " " .. datetime.time(valid_until);
|
||||
local valid_until = tokens_expiration[hasTokenActive(jid)] + token_expire;
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Ya tienes una petición de reseteada de contraseña, válida hasta: " .. datetime.date(valid_until) .. " " .. datetime.time(valid_until);
|
||||
else
|
||||
return nil, "You already have a password reset request, valid until: " .. datetime.date(valid_until) .. " " .. datetime.time(valid_until);
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
local url_path = origin.path;
|
||||
local url_token = generateToken(jid);
|
||||
local url = generateUrl(url_token);
|
||||
local email_body = render(get_template("sendtoken",".mail"), {jid = jid, url = url} );
|
||||
local url = generateUrl(url_token, url_path);
|
||||
|
||||
local mail_subject = nil;
|
||||
local mail_body = nil;
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Jabber/XMPP - Reseto de la Contraseña";
|
||||
mail_body = render( get_template("sendtoken", ".mail"), {jid = jid, url = url, time = ((token_expire / 60) / 60), host = module.host} );
|
||||
else
|
||||
mail_subject = "Jabber/XMPP - Password Reset";
|
||||
mail_body = render( get_template("sendtoken-en", ".mail"), {jid = jid, url = url, time = ((token_expire / 60) / 60), host = module.host} );
|
||||
end
|
||||
-- end translation
|
||||
|
||||
module:log("info", "Sending password reset mail to user %s", jid);
|
||||
send_email(email, smtp_address, email_body, mail_subject);
|
||||
local ok, err = moduleapi:send_email({to = email, subject = mail_subject, body = mail_body});
|
||||
if not ok then
|
||||
module:log("error", "Failed to deliver to %s: %s", tostring(address), tostring(err));
|
||||
end
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Gestión de Cuentas";
|
||||
mail_body = "Token para reseto su contraseña ha sido enviado a su email por el sistema de reseto de contraseña.";
|
||||
else
|
||||
mail_subject = "Account Management";
|
||||
mail_body = "Token to reset your password has been sent to your email by password reset system.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
sendMessage(jid, mail_subject, mail_body);
|
||||
|
||||
return "ok";
|
||||
end
|
||||
|
||||
function reset_password_with_token(form, origin)
|
||||
local lang = form.lang;
|
||||
|
||||
if form.langchange == "true" then
|
||||
return nil;
|
||||
end
|
||||
if attempt_wait_list[origin.ip] == nil then attempt_wait_list[origin.ip] = 0; end
|
||||
if attempt_wait_list[origin.ip] >= attempt_limit then
|
||||
if os.difftime(os.time(), attempt_wait_list_time[origin.ip]) > attempt_wait then
|
||||
attempt_wait_list[origin.ip] = 0;
|
||||
attempt_wait_list_time[origin.ip] = 0;
|
||||
else
|
||||
module:log("info", "Too many attempts at guessing token from IP %s", origin.ip);
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Demasiados intentos. Inténtalo otra vez en "..(attempt_wait / 60).."m.";
|
||||
else
|
||||
return nil, "Too many attempts. Try again in "..(attempt_wait / 60).."m.";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
end
|
||||
|
||||
function reset_password_with_token(form, origin)
|
||||
local token = form.token;
|
||||
local password = form.newpassword;
|
||||
local passwordconfirmation = form.newpasswordconfirmation;
|
||||
form.newpassword, form.newpasswordconfirmation = nil, nil;
|
||||
|
||||
if not token then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El Token es inválido";
|
||||
else
|
||||
return nil, "The token is invalid";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if not tokens_mails[token] then
|
||||
attempt_wait_list[origin.ip] = attempt_wait_list[origin.ip]+1;
|
||||
attempt_wait_list_time[origin.ip] = os.time();
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El Token no existe o ya fué usado";
|
||||
else
|
||||
return nil, "The token does not exist or has already been used";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if not password then
|
||||
return nil, "La campo clave no puede estar vacio";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de contraseña está vacío";
|
||||
else
|
||||
return nil, "The password text field is empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if #password < 5 then
|
||||
return nil, "La clave debe tener una longitud de al menos 5 caracteres";
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El contraseña debe tener una longitud de al menos cinco caracteres";
|
||||
else
|
||||
return nil, "The password must be at least five characters long";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if password ~= passwordconfirmation then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El confirmació de contraseña es inválido";
|
||||
else
|
||||
return nil, "The password confirmation is invalid";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
local jid = tokens_mails[token];
|
||||
local user, host, resource = jidutil.split(jid);
|
||||
|
||||
local mail_subject = nil;
|
||||
local mail_body = nil;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Gestión de Cuentas";
|
||||
mail_body = "La contraseña se ha cambiado con el sistema de reseto de contraseña.";
|
||||
else
|
||||
mail_subject = "Account Management";
|
||||
mail_body = "Password has been changed with password reset system.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
usermanager.set_password(user, password, host);
|
||||
module:log("info", "Password changed with token for user %s", jid);
|
||||
tokens_mails[token] = nil;
|
||||
tokens_expiration[token] = nil;
|
||||
sendMessage(jid, mail_subject, mail_body);
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Jabber/XMPP - Contraseña Cambiado";
|
||||
mail_body = render( get_template("updatepass", ".mail"), {jid = jid, host = module.host} );
|
||||
else
|
||||
mail_subject = "Jabber/XMPP - Password Changed";
|
||||
mail_body = render( get_template("updatepass-en", ".mail"), {jid = jid, host = module.host} );
|
||||
end
|
||||
-- end translation
|
||||
|
||||
module:log("info", "Sending password update mail to user %s", jid);
|
||||
local ok, err = moduleapi:send_email({to = email, subject = mail_subject, body = mail_body});
|
||||
if not ok then
|
||||
module:log("error", "Failed to deliver to %s: %s", tostring(address), tostring(err));
|
||||
end
|
||||
|
||||
return "ok";
|
||||
end
|
||||
|
||||
function generate_success(event, form)
|
||||
return render(sendmail_success_tpl, { jid = nodeprep(form.username).."@"..module.host });
|
||||
function change_mail_with_password(form, origin)
|
||||
local lang = form.lang;
|
||||
|
||||
if form.langchange == "true" then
|
||||
return nil;
|
||||
end
|
||||
if attempt_wait_list[origin.ip] == nil then attempt_wait_list[origin.ip] = 0; end
|
||||
if attempt_wait_list[origin.ip] >= attempt_limit then
|
||||
if os.difftime(os.time(), attempt_wait_list_time[origin.ip]) > attempt_wait then
|
||||
attempt_wait_list[origin.ip] = 0;
|
||||
attempt_wait_list_time[origin.ip] = 0;
|
||||
else
|
||||
module:log("info", "Too many attempts at guessing password from IP %s", origin.ip);
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Demasiados intentos. Inténtalo otra vez en "..(attempt_wait / 60).."m";
|
||||
else
|
||||
return nil, "Too many attempts. Try again in "..(attempt_wait / 60).."m";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
end
|
||||
|
||||
local prepped_username = nodeprep(form.username, true);
|
||||
local prepped_mail = form.email;
|
||||
local password = form.password;
|
||||
local jid = prepped_username .. "@" .. module.host;
|
||||
form.password = nil;
|
||||
|
||||
if not prepped_username then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El nombre de usuario contiene caracteres incorrectos";
|
||||
else
|
||||
return nil, "The username contains invalid characters";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if #prepped_username == 0 then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de nombre de usuario está vacio";
|
||||
else
|
||||
return nil, "The username text field is empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if not usermanager.user_exists(prepped_username, module.host) then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El usuario no existe";
|
||||
else
|
||||
return nil, "The user does not exist";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
if not password then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de contraseña está vacío";
|
||||
else
|
||||
return nil, "The password text field is empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
|
||||
|
||||
if usermanager.test_password(prepped_username, module.host, password) then
|
||||
local account_detail_table = account_details:get(prepped_username);
|
||||
if account_detail_table == nil then
|
||||
account_detail_table = {["email"] = ""};
|
||||
end
|
||||
local remove_email;
|
||||
if prepped_mail == "" then
|
||||
remove_email = true;
|
||||
else
|
||||
remove_email = false;
|
||||
end
|
||||
email = string.lower(prepped_mail);
|
||||
account_detail_table["email"] = email;
|
||||
account_details:set(prepped_username, account_detail_table);
|
||||
module:log("info", "Email changed with password for user %s", jid);
|
||||
|
||||
local mail_subject = nil;
|
||||
local mail_body = nil;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Gestión de Cuentas";
|
||||
mail_body = "El email se ha actualizado a <"..email.."> con el sistema de reseto de contraseña.";
|
||||
else
|
||||
mail_subject = "Account Management";
|
||||
mail_body = "Email has been updated to <"..email.."> with password reset system.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
sendMessage(jid, mail_subject, mail_body);
|
||||
|
||||
if not remove_email then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
mail_subject = "Jabber/XMPP - Email Cambiado";
|
||||
mail_body = render( get_template("updatemail", ".mail"), {jid = jid, email = email, host = module.host} );
|
||||
else
|
||||
mail_subject = "Jabber/XMPP - Email Changed";
|
||||
mail_body = render( get_template("updatemail-en", ".mail"), {jid = jid, email = email, host = module.host} );
|
||||
end
|
||||
-- end translation
|
||||
|
||||
module:log("info", "Sending email update mail to user %s", jid);
|
||||
local ok, err = moduleapi:send_email({to = email, subject = mail_subject, body = mail_body});
|
||||
if not ok then
|
||||
module:log("error", "Failed to deliver to %s: %s", tostring(address), tostring(err));
|
||||
end
|
||||
end
|
||||
|
||||
return "ok";
|
||||
else
|
||||
attempt_wait_list[origin.ip] = attempt_wait_list[origin.ip]+1;
|
||||
attempt_wait_list_time[origin.ip] = os.time();
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Contraseña incorrecta";
|
||||
else
|
||||
return nil, "Incorrect password";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
end
|
||||
|
||||
function generate_success(event, lang, username)
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "¡Enlace enviado!";
|
||||
s_text = "Acabamos de enviarte un email con un enlace que tendrás que visitar.";
|
||||
else
|
||||
s_title = "Link sent!";
|
||||
s_text = "We just sent you an email with a link you’ll need to visit.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
return render(sendmail_success_tpl, {
|
||||
jid = nodeprep(username, true).."@"..module.host;
|
||||
s_title = s_title;
|
||||
s_text = s_text;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_register_response(event, form, ok, err)
|
||||
local message;
|
||||
if ok then
|
||||
return generate_success(event, form);
|
||||
return generate_success(event, form.lang, form.username);
|
||||
else
|
||||
return generate_page(event, { register_error = err });
|
||||
return generate_page(event, form.lang, { register_error = err });
|
||||
end
|
||||
end
|
||||
|
||||
@ -325,16 +657,61 @@ function handle_form_token(event)
|
||||
return true; -- Leave connection open until we respond above
|
||||
end
|
||||
|
||||
function generate_reset_success(event, form)
|
||||
return render(reset_success_tpl, { });
|
||||
function generate_reset_success(event, lang)
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "¡Contraseña reseteada!";
|
||||
s_p1 = "Tu contraseña ha sido cambiada.";
|
||||
s_p2 = "Ya puedes iniciar sesión de Jabber.";
|
||||
else
|
||||
s_title = "Password reset!";
|
||||
s_p1 = "Your password has been changed.";
|
||||
s_p2 = "You can now log into Jabber.";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
return render(reset_success_tpl, {
|
||||
s_title = s_title;
|
||||
s_p1 = s_p1;
|
||||
s_p2 = s_p2;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_update_success(event, lang, email)
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "¡Email cambiada!";
|
||||
s_p1 = "Tu email ha sido cambiada.";
|
||||
s_p2 = "Nuevo email";
|
||||
else
|
||||
s_title = "Email changed!";
|
||||
s_p1 = "Your email has been changed.";
|
||||
s_p2 = "New email";
|
||||
end
|
||||
-- end translation
|
||||
return render(update_success_tpl, {
|
||||
s_title = s_title;
|
||||
s_p1 = s_p1;
|
||||
s_p2 = s_p2;
|
||||
email = email;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_reset_response(event, form, ok, err)
|
||||
local message;
|
||||
if ok then
|
||||
return generate_reset_success(event, form);
|
||||
return generate_reset_success(event, form.lang);
|
||||
else
|
||||
return generate_token_page(event, { register_error = err });
|
||||
return generate_token_page(event, form.lang, { register_error = err });
|
||||
end
|
||||
end
|
||||
|
||||
function generate_update_response(event, form, ok, err)
|
||||
local message;
|
||||
if ok then
|
||||
return generate_update_success(event, form.lang, form.email);
|
||||
else
|
||||
return generate_mail_page(event, form.lang, { register_error = err });
|
||||
end
|
||||
end
|
||||
|
||||
@ -346,17 +723,29 @@ function handle_form_reset(event)
|
||||
response:send(generate_reset_response(event, form, reset_ok, reset_err));
|
||||
|
||||
return true; -- Leave connection open until we respond above
|
||||
end
|
||||
|
||||
function handle_form_mailchange(event)
|
||||
local request, response = event.request, event.response;
|
||||
local form = http.formdecode(request.body);
|
||||
|
||||
local change_ok, change_err = change_mail_with_password(form, request);
|
||||
response:send(generate_update_response(event, form, change_ok, change_err));
|
||||
|
||||
return true; -- Leave connection open until we respond above
|
||||
end
|
||||
|
||||
timer.add_task(timer_repeat, expireTokens);
|
||||
|
||||
module:provides("http", {
|
||||
default_path = url_path;
|
||||
route = {
|
||||
["GET /style.css"] = render(get_template("style",".css"), {});
|
||||
["GET /changepass.html"] = generate_page;
|
||||
["GET /changemail.html"] = generate_mail_page;
|
||||
["GET /token.html"] = generate_token_page;
|
||||
["GET /"] = generate_page;
|
||||
["POST /changepass.html"] = handle_form_token;
|
||||
["POST /changemail.html"] = handle_form_mailchange;
|
||||
["POST /token.html"] = handle_form_reset;
|
||||
["POST /"] = handle_form_token;
|
||||
};
|
||||
|
52
mod_email_pass/templates/changemail.html
Normal file
52
mod_email_pass/templates/changemail.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo3" class="formulario">
|
||||
<h1>{s_title}</h1>
|
||||
</div>
|
||||
<div id="estilo2" class="formulario2">
|
||||
<div>
|
||||
<form action="{path}" method="POST">
|
||||
<input name="lang" type="submit" value="English">
|
||||
<input name="lang" type="submit" value="Español">
|
||||
<input name="langchange" type="hidden" value="true">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="estilo" class="formulario">
|
||||
<form action="{path}" method="POST">
|
||||
<p class="error">{notice}</p>
|
||||
<label>
|
||||
{s_username}:
|
||||
<span class="small">{s_usernamemessage}</span>
|
||||
</label>
|
||||
<input type="text" name="username" required>@{hostname}
|
||||
<div class="spacer"></div>
|
||||
<label>
|
||||
{s_password}:
|
||||
<span class="small">{s_passwordmessage}</span>
|
||||
</label>
|
||||
<input type="password" name="password" required>
|
||||
<div class="spacer"></div>
|
||||
<label>
|
||||
{s_email}:
|
||||
<span class="small">{s_emailmessage}</span>
|
||||
</label>
|
||||
<input type="text" name="email">
|
||||
<div class="spacer"></div>
|
||||
|
||||
<input name="lang" type="hidden" value="{s_lang}">
|
||||
<input name="langchange" type="hidden" value="false">
|
||||
<input id="button" class="button" type="submit" value="{s_change}">
|
||||
<div class="spacer"></div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,33 +3,47 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<title>Reseteo de la clave de tu cuenta Jabber</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo3" class="formulario">
|
||||
<h1>{s_title}</h1>
|
||||
</div>
|
||||
<div id="estilo2" class="formulario2">
|
||||
<div>
|
||||
<form action="{path}" method="POST">
|
||||
<input name="lang" type="submit" value="English">
|
||||
<input name="lang" type="submit" value="Español">
|
||||
<input name="langchange" type="hidden" value="true">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>Reseteo de la clave de tu cuenta Jabber</h1>
|
||||
<form action="{path}" method="POST">
|
||||
<p class="error">{notice}</p>
|
||||
<label>
|
||||
Usuario:
|
||||
<span class="small">Introduce tu usuario</span>
|
||||
{s_username}:
|
||||
<span class="small">{s_usernamemessage}</span>
|
||||
</label>
|
||||
<input type="text" name="username" required>@{hostname}
|
||||
<div class="spacer"></div>
|
||||
<label>
|
||||
Email:
|
||||
<span class="small">Introduce tu email</span>
|
||||
{s_email}:
|
||||
<span class="small">{s_emailmessage}</span>
|
||||
</label>
|
||||
<input type="text" name="email" required>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<input id="button" class="button" type="submit" value="Enviar!">
|
||||
<input name="lang" type="hidden" value="{s_lang}">
|
||||
<input name="langchange" type="hidden" value="false">
|
||||
<input id="button" class="button" type="submit" value="{s_send}">
|
||||
<div class="spacer"></div>
|
||||
</form>
|
||||
<p>
|
||||
Al pulsar sobre el botón, se enviará a la dirección de correo que figura
|
||||
en tu vCard un enlace en el que deberás entrar.<br />
|
||||
{s_text}
|
||||
</p>
|
||||
<br />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,14 +1,15 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta charset="utf-8">
|
||||
<title>Clave reseteada!</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>Tu clave ha sido cambiada correctamente. Ya puedes iniciar sesión con ella.</h1>
|
||||
<h1>{s_p1}</h1>
|
||||
<h1>{s_p2}</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,14 +1,14 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta charset="utf-8">
|
||||
<title>Enlace enviado!</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>Acabamos de enviarte un email con un enlace que tendrás que visitar.</h1>
|
||||
<h1>{s_text}</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
16
mod_email_pass/templates/sendtoken-en.mail
Normal file
16
mod_email_pass/templates/sendtoken-en.mail
Normal file
@ -0,0 +1,16 @@
|
||||
Hello
|
||||
|
||||
You have received this email because you have requested the
|
||||
reset of your Jabber/XMPP account password.
|
||||
|
||||
To proceed with the password change, use the following link:
|
||||
|
||||
Account ID: {jid}
|
||||
Reset Link: {url}
|
||||
|
||||
If you have not requested to reset your password, ignore this message.
|
||||
The link expires in {time}h.
|
||||
|
||||
Sincerely, the team of {host}
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
Hola:
|
||||
Hola
|
||||
|
||||
Si has recibido este email es porque has solicitado el reseteo de la
|
||||
clave de tu cuenta Jabber/XMPP {jid}
|
||||
Tú tienen recibido este email porque has solicitado el reseteo de la
|
||||
contraseña de tu cuenta Jabber/XMPP.
|
||||
|
||||
Para proceder con el cambio de clave, haz click en el siguiente enlace:
|
||||
Para proceder con el cambio de contraseña, usar el siguiente enlace:
|
||||
|
||||
{url}
|
||||
ID de Cuenta: {jid}
|
||||
Enlace de Reseteo: {url}
|
||||
|
||||
Si no has solicitado resetear tu clave, ignora este mensaje.
|
||||
Si no has solicitado resetear tu contraseña, ignora este mensaje.
|
||||
El enlace expira en {time}h.
|
||||
|
||||
Atentamente, el equipo de mijabber.es
|
||||
Atentamente, el equipo de {host}
|
||||
|
||||
|
||||
|
@ -3,8 +3,20 @@ body{
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
.spacer{clear:both; height:1px;}
|
||||
h1 {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
p, h1, form, button {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
clear:both;
|
||||
height:1px;
|
||||
}
|
||||
|
||||
/* ----------- My Form ----------- */
|
||||
.formulario {
|
||||
@ -12,12 +24,30 @@ p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
width:500px;
|
||||
padding:14px;
|
||||
}
|
||||
|
||||
.formulario2 {
|
||||
margin:0 auto;
|
||||
width:500px;
|
||||
padding:14px;
|
||||
}
|
||||
|
||||
/* ----------- stylized ----------- */
|
||||
#estilo {
|
||||
border:solid 2px #b7ddf2;
|
||||
background:#ebf4fb;
|
||||
}
|
||||
|
||||
#estilo2 {
|
||||
border-left:solid 2px #b7ddf2;
|
||||
border-right:solid 2px #b7ddf2;
|
||||
background:#ebf4fb;
|
||||
}
|
||||
|
||||
#estilo3 {
|
||||
border:solid 2px #b7ddf2;
|
||||
background:#ebf4fb;
|
||||
}
|
||||
|
||||
#estilo h1 {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
@ -39,6 +69,7 @@ p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
margin-bottom:20px;
|
||||
border-bottom:solid 1px #b7ddf2;
|
||||
padding-bottom:10px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#estilo label {
|
||||
@ -59,7 +90,7 @@ p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
}
|
||||
|
||||
#estilo input {
|
||||
float:left;
|
||||
/* float:left; */
|
||||
font-size:12px;
|
||||
padding:4px 2px;
|
||||
border:solid 1px #aacfe4;
|
||||
@ -67,6 +98,24 @@ p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
margin:2px 0 20px 10px;
|
||||
}
|
||||
|
||||
#estilo2 div {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#estilo2 input {
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
#estilo3 h1 {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
margin-bottom:8px;
|
||||
}
|
||||
|
||||
#estilo .button {
|
||||
margin:2px 150px 20px 150px;
|
||||
}
|
||||
|
||||
.button {
|
||||
-moz-box-shadow:inset 0px 1px 0px 0px #cae3fc;
|
||||
-webkit-box-shadow:inset 0px 1px 0px 0px #cae3fc;
|
||||
@ -99,9 +148,11 @@ p, h1, form, button{border:0; margin:0; padding:0;}
|
||||
text-align:center;
|
||||
text-shadow:1px 1px 0px #287ace;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color:#4197ee;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
position:relative;
|
||||
top:1px;
|
||||
|
@ -1,28 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta charset="utf-8">
|
||||
<title>Reseto de la clave de tu cuenta Jabber</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo3" class="formulario">
|
||||
<h1>{s_title}</h1>
|
||||
</div>
|
||||
<div id="estilo2" class="formulario2">
|
||||
<div>
|
||||
<form action="{path}?{token}" method="POST">
|
||||
<input name="lang" type="submit" value="English">
|
||||
<input name="lang" type="submit" value="Español">
|
||||
<input name="langchange" type="hidden" value="true">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>Reseteo de la clave de tu cuenta Jabber</h1>
|
||||
<form action="{path}?{token}" method="POST">
|
||||
<p class="error">{notice}</p>
|
||||
<label>
|
||||
Token:
|
||||
{s_token}:
|
||||
</label>
|
||||
<input name="token" value="{token}" required readonly>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<label>
|
||||
Contraseña:
|
||||
{s_password}:
|
||||
</label>
|
||||
<input name="newpassword" type="password" required size="35">
|
||||
<div class="spacer"></div>
|
||||
<input id="button" class="button" type="submit" value="Cambiar!">
|
||||
|
||||
<label>
|
||||
{s_passwordconfirm}:
|
||||
</label>
|
||||
<input name="newpasswordconfirmation" type="password" required size="35">
|
||||
<div class="spacer"></div>
|
||||
<input id="button" class="button" type="submit" value="{s_change}">
|
||||
<div class="spacer"></div>
|
||||
<input name="lang" type="hidden" value="{s_lang}">
|
||||
<input name="langchange" type="hidden" value="false">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
11
mod_email_pass/templates/updatemail-en.mail
Normal file
11
mod_email_pass/templates/updatemail-en.mail
Normal file
@ -0,0 +1,11 @@
|
||||
Hello
|
||||
|
||||
You have received this email because your Jabber/XMPP
|
||||
account email has been changed.
|
||||
|
||||
Account ID: {jid}
|
||||
Account Email: {email}
|
||||
|
||||
Sincerely, the team of {host}
|
||||
|
||||
|
11
mod_email_pass/templates/updatemail.mail
Normal file
11
mod_email_pass/templates/updatemail.mail
Normal file
@ -0,0 +1,11 @@
|
||||
Hola
|
||||
|
||||
Tú tienen recibido este email porque has cambiado el email de tu
|
||||
cuenta de Jabber/XMPP.
|
||||
|
||||
ID de Cuenta: {jid}
|
||||
Email de Cuenta: {email}
|
||||
|
||||
Atentamente, el equipo de {host}
|
||||
|
||||
|
17
mod_email_pass/templates/updateok.html
Normal file
17
mod_email_pass/templates/updateok.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>{s_p1}</h1>
|
||||
<h1>{s_p2}:</h1>
|
||||
<h1><i>{email}</i></h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
10
mod_email_pass/templates/updatepass-en.mail
Normal file
10
mod_email_pass/templates/updatepass-en.mail
Normal file
@ -0,0 +1,10 @@
|
||||
Hello
|
||||
|
||||
You have received this email because your Jabber/XMPP
|
||||
account password has been reset.
|
||||
|
||||
Account ID: {jid}
|
||||
|
||||
Sincerely, the team of {host}
|
||||
|
||||
|
10
mod_email_pass/templates/updatepass.mail
Normal file
10
mod_email_pass/templates/updatepass.mail
Normal file
@ -0,0 +1,10 @@
|
||||
Hola
|
||||
|
||||
Tú tienen recibido este email porque has reseteo la contraseña de tu
|
||||
cuenta de Jabber/XMPP.
|
||||
|
||||
ID de Cuenta: {jid}
|
||||
|
||||
Atentamente, el equipo de {host}
|
||||
|
||||
|
@ -1,464 +0,0 @@
|
||||
-- Copyright (C) 2011-2012 Kim Alvefur
|
||||
--
|
||||
-- This project is MIT/X11 licensed. Please see the
|
||||
-- COPYING file in the source package for more information.
|
||||
--
|
||||
|
||||
-- TODO
|
||||
-- Fix folding.
|
||||
|
||||
local st = require "util.stanza";
|
||||
local t_insert, t_concat = table.insert, table.concat;
|
||||
local type = type;
|
||||
local next, pairs, ipairs = next, pairs, ipairs;
|
||||
|
||||
local from_text, to_text, from_xep54, to_xep54;
|
||||
|
||||
local line_sep = "\n";
|
||||
|
||||
local vCard_dtd; -- See end of file
|
||||
|
||||
local function fold_line()
|
||||
error "Not implemented" --TODO
|
||||
end
|
||||
local function unfold_line()
|
||||
error "Not implemented"
|
||||
-- gsub("\r?\n[ \t]([^\r\n])", "%1");
|
||||
end
|
||||
|
||||
local function vCard_esc(s)
|
||||
return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n");
|
||||
end
|
||||
|
||||
local function vCard_unesc(s)
|
||||
return s:gsub("\\?[\\nt:;,]", {
|
||||
["\\\\"] = "\\",
|
||||
["\\n"] = "\n",
|
||||
["\\r"] = "\r",
|
||||
["\\t"] = "\t",
|
||||
["\\:"] = ":", -- FIXME Shouldn't need to espace : in values, just params
|
||||
["\\;"] = ";",
|
||||
["\\,"] = ",",
|
||||
[":"] = "\29",
|
||||
[";"] = "\30",
|
||||
[","] = "\31",
|
||||
});
|
||||
end
|
||||
|
||||
local function item_to_xep54(item)
|
||||
local t = st.stanza(item.name, { xmlns = "vcard-temp" });
|
||||
|
||||
local prop_def = vCard_dtd[item.name];
|
||||
if prop_def == "text" then
|
||||
t:text(item[1]);
|
||||
elseif type(prop_def) == "table" then
|
||||
if prop_def.types and item.TYPE then
|
||||
if type(item.TYPE) == "table" then
|
||||
for _,v in pairs(prop_def.types) do
|
||||
for _,typ in pairs(item.TYPE) do
|
||||
if typ:upper() == v then
|
||||
t:tag(v):up();
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
t:tag(item.TYPE:upper()):up();
|
||||
end
|
||||
end
|
||||
|
||||
if prop_def.props then
|
||||
for _,v in pairs(prop_def.props) do
|
||||
if item[v] then
|
||||
t:tag(v):up();
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if prop_def.value then
|
||||
t:tag(prop_def.value):text(item[1]):up();
|
||||
elseif prop_def.values then
|
||||
local prop_def_values = prop_def.values;
|
||||
local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values];
|
||||
for i=1,#item do
|
||||
t:tag(prop_def.values[i] or repeat_last):text(item[i]):up();
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return t;
|
||||
end
|
||||
|
||||
local function vcard_to_xep54(vCard)
|
||||
local t = st.stanza("vCard", { xmlns = "vcard-temp" });
|
||||
for i=1,#vCard do
|
||||
t:add_child(item_to_xep54(vCard[i]));
|
||||
end
|
||||
return t;
|
||||
end
|
||||
|
||||
function to_xep54(vCards)
|
||||
if not vCards[1] or vCards[1].name then
|
||||
return vcard_to_xep54(vCards)
|
||||
else
|
||||
local t = st.stanza("xCard", { xmlns = "vcard-temp" });
|
||||
for i=1,#vCards do
|
||||
t:add_child(vcard_to_xep54(vCards[i]));
|
||||
end
|
||||
return t;
|
||||
end
|
||||
end
|
||||
|
||||
function from_text(data)
|
||||
data = data -- unfold and remove empty lines
|
||||
:gsub("\r\n","\n")
|
||||
:gsub("\n ", "")
|
||||
:gsub("\n\n+","\n");
|
||||
local vCards = {};
|
||||
local c; -- current item
|
||||
for line in data:gmatch("[^\n]+") do
|
||||
local line = vCard_unesc(line);
|
||||
local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$");
|
||||
value = value:gsub("\29",":");
|
||||
if #params > 0 then
|
||||
local _params = {};
|
||||
for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do
|
||||
k = k:upper();
|
||||
local _vt = {};
|
||||
for _p in v:gmatch("[^\31]+") do
|
||||
_vt[#_vt+1]=_p
|
||||
_vt[_p]=true;
|
||||
end
|
||||
if isval == "=" then
|
||||
_params[k]=_vt;
|
||||
else
|
||||
_params[k]=true;
|
||||
end
|
||||
end
|
||||
params = _params;
|
||||
end
|
||||
if name == "BEGIN" and value == "VCARD" then
|
||||
c = {};
|
||||
vCards[#vCards+1] = c;
|
||||
elseif name == "END" and value == "VCARD" then
|
||||
c = nil;
|
||||
elseif vCard_dtd[name] then
|
||||
local dtd = vCard_dtd[name];
|
||||
local p = { name = name };
|
||||
c[#c+1]=p;
|
||||
--c[name]=p;
|
||||
local up = c;
|
||||
c = p;
|
||||
if dtd.types then
|
||||
for _, t in ipairs(dtd.types) do
|
||||
local t = t:lower();
|
||||
if ( params.TYPE and params.TYPE[t] == true)
|
||||
or params[t] == true then
|
||||
c.TYPE=t;
|
||||
end
|
||||
end
|
||||
end
|
||||
if dtd.props then
|
||||
for _, p in ipairs(dtd.props) do
|
||||
if params[p] then
|
||||
if params[p] == true then
|
||||
c[p]=true;
|
||||
else
|
||||
for _, prop in ipairs(params[p]) do
|
||||
c[p]=prop;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if dtd == "text" or dtd.value then
|
||||
t_insert(c, value);
|
||||
elseif dtd.values then
|
||||
local value = "\30"..value;
|
||||
for p in value:gmatch("\30([^\30]*)") do
|
||||
t_insert(c, p);
|
||||
end
|
||||
end
|
||||
c = up;
|
||||
end
|
||||
end
|
||||
return vCards;
|
||||
end
|
||||
|
||||
local function item_to_text(item)
|
||||
local value = {};
|
||||
for i=1,#item do
|
||||
value[i] = vCard_esc(item[i]);
|
||||
end
|
||||
value = t_concat(value, ";");
|
||||
|
||||
local params = "";
|
||||
for k,v in pairs(item) do
|
||||
if type(k) == "string" and k ~= "name" then
|
||||
params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v);
|
||||
end
|
||||
end
|
||||
|
||||
return ("%s%s:%s"):format(item.name, params, value)
|
||||
end
|
||||
|
||||
local function vcard_to_text(vcard)
|
||||
local t={};
|
||||
t_insert(t, "BEGIN:VCARD")
|
||||
for i=1,#vcard do
|
||||
t_insert(t, item_to_text(vcard[i]));
|
||||
end
|
||||
t_insert(t, "END:VCARD")
|
||||
return t_concat(t, line_sep);
|
||||
end
|
||||
|
||||
function to_text(vCards)
|
||||
if vCards[1] and vCards[1].name then
|
||||
return vcard_to_text(vCards)
|
||||
else
|
||||
local t = {};
|
||||
for i=1,#vCards do
|
||||
t[i]=vcard_to_text(vCards[i]);
|
||||
end
|
||||
return t_concat(t, line_sep);
|
||||
end
|
||||
end
|
||||
|
||||
local function from_xep54_item(item)
|
||||
local prop_name = item.name;
|
||||
local prop_def = vCard_dtd[prop_name];
|
||||
|
||||
local prop = { name = prop_name };
|
||||
|
||||
if prop_def == "text" then
|
||||
prop[1] = item:get_text();
|
||||
elseif type(prop_def) == "table" then
|
||||
if prop_def.value then --single item
|
||||
prop[1] = item:get_child_text(prop_def.value) or "";
|
||||
elseif prop_def.values then --array
|
||||
local value_names = prop_def.values;
|
||||
if value_names.behaviour == "repeat-last" then
|
||||
for i=1,#item.tags do
|
||||
t_insert(prop, item.tags[i]:get_text() or "");
|
||||
end
|
||||
else
|
||||
for i=1,#value_names do
|
||||
t_insert(prop, item:get_child_text(value_names[i]) or "");
|
||||
end
|
||||
end
|
||||
elseif prop_def.names then
|
||||
local names = prop_def.names;
|
||||
for i=1,#names do
|
||||
if item:get_child(names[i]) then
|
||||
prop[1] = names[i];
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if prop_def.props_verbatim then
|
||||
for k,v in pairs(prop_def.props_verbatim) do
|
||||
prop[k] = v;
|
||||
end
|
||||
end
|
||||
|
||||
if prop_def.types then
|
||||
local types = prop_def.types;
|
||||
prop.TYPE = {};
|
||||
for i=1,#types do
|
||||
if item:get_child(types[i]) then
|
||||
t_insert(prop.TYPE, types[i]:lower());
|
||||
end
|
||||
end
|
||||
if #prop.TYPE == 0 then
|
||||
prop.TYPE = nil;
|
||||
end
|
||||
end
|
||||
|
||||
-- A key-value pair, within a key-value pair?
|
||||
if prop_def.props then
|
||||
local params = prop_def.props;
|
||||
for i=1,#params do
|
||||
local name = params[i]
|
||||
local data = item:get_child_text(name);
|
||||
if data then
|
||||
prop[name] = prop[name] or {};
|
||||
t_insert(prop[name], data);
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
return prop;
|
||||
end
|
||||
|
||||
local function from_xep54_vCard(vCard)
|
||||
local tags = vCard.tags;
|
||||
local t = {};
|
||||
for i=1,#tags do
|
||||
t_insert(t, from_xep54_item(tags[i]));
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function from_xep54(vCard)
|
||||
if vCard.attr.xmlns ~= "vcard-temp" then
|
||||
return nil, "wrong-xmlns";
|
||||
end
|
||||
if vCard.name == "xCard" then -- A collection of vCards
|
||||
local t = {};
|
||||
local vCards = vCard.tags;
|
||||
for i=1,#vCards do
|
||||
t[i] = from_xep54_vCard(vCards[i]);
|
||||
end
|
||||
return t
|
||||
elseif vCard.name == "vCard" then -- A single vCard
|
||||
return from_xep54_vCard(vCard)
|
||||
end
|
||||
end
|
||||
|
||||
-- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd
|
||||
vCard_dtd = {
|
||||
VERSION = "text", --MUST be 3.0, so parsing is redundant
|
||||
FN = "text",
|
||||
N = {
|
||||
values = {
|
||||
"FAMILY",
|
||||
"GIVEN",
|
||||
"MIDDLE",
|
||||
"PREFIX",
|
||||
"SUFFIX",
|
||||
},
|
||||
},
|
||||
NICKNAME = "text",
|
||||
PHOTO = {
|
||||
props_verbatim = { ENCODING = { "b" } },
|
||||
props = { "TYPE" },
|
||||
value = "BINVAL", --{ "EXTVAL", },
|
||||
},
|
||||
BDAY = "text",
|
||||
ADR = {
|
||||
types = {
|
||||
"HOME",
|
||||
"WORK",
|
||||
"POSTAL",
|
||||
"PARCEL",
|
||||
"DOM",
|
||||
"INTL",
|
||||
"PREF",
|
||||
},
|
||||
values = {
|
||||
"POBOX",
|
||||
"EXTADD",
|
||||
"STREET",
|
||||
"LOCALITY",
|
||||
"REGION",
|
||||
"PCODE",
|
||||
"CTRY",
|
||||
}
|
||||
},
|
||||
LABEL = {
|
||||
types = {
|
||||
"HOME",
|
||||
"WORK",
|
||||
"POSTAL",
|
||||
"PARCEL",
|
||||
"DOM",
|
||||
"INTL",
|
||||
"PREF",
|
||||
},
|
||||
value = "LINE",
|
||||
},
|
||||
TEL = {
|
||||
types = {
|
||||
"HOME",
|
||||
"WORK",
|
||||
"VOICE",
|
||||
"FAX",
|
||||
"PAGER",
|
||||
"MSG",
|
||||
"CELL",
|
||||
"VIDEO",
|
||||
"BBS",
|
||||
"MODEM",
|
||||
"ISDN",
|
||||
"PCS",
|
||||
"PREF",
|
||||
},
|
||||
value = "NUMBER",
|
||||
},
|
||||
EMAIL = {
|
||||
types = {
|
||||
"HOME",
|
||||
"WORK",
|
||||
"INTERNET",
|
||||
"PREF",
|
||||
"X400",
|
||||
},
|
||||
value = "USERID",
|
||||
},
|
||||
JABBERID = "text",
|
||||
MAILER = "text",
|
||||
TZ = "text",
|
||||
GEO = {
|
||||
values = {
|
||||
"LAT",
|
||||
"LON",
|
||||
},
|
||||
},
|
||||
TITLE = "text",
|
||||
ROLE = "text",
|
||||
LOGO = "copy of PHOTO",
|
||||
AGENT = "text",
|
||||
ORG = {
|
||||
values = {
|
||||
behaviour = "repeat-last",
|
||||
"ORGNAME",
|
||||
"ORGUNIT",
|
||||
}
|
||||
},
|
||||
CATEGORIES = {
|
||||
values = "KEYWORD",
|
||||
},
|
||||
NOTE = "text",
|
||||
PRODID = "text",
|
||||
REV = "text",
|
||||
SORTSTRING = "text",
|
||||
SOUND = "copy of PHOTO",
|
||||
UID = "text",
|
||||
URL = "text",
|
||||
CLASS = {
|
||||
names = { -- The item.name is the value if it's one of these.
|
||||
"PUBLIC",
|
||||
"PRIVATE",
|
||||
"CONFIDENTIAL",
|
||||
},
|
||||
},
|
||||
KEY = {
|
||||
props = { "TYPE" },
|
||||
value = "CRED",
|
||||
},
|
||||
DESC = "text",
|
||||
};
|
||||
vCard_dtd.LOGO = vCard_dtd.PHOTO;
|
||||
vCard_dtd.SOUND = vCard_dtd.PHOTO;
|
||||
|
||||
return {
|
||||
from_text = from_text;
|
||||
to_text = to_text;
|
||||
|
||||
from_xep54 = from_xep54;
|
||||
to_xep54 = to_xep54;
|
||||
|
||||
-- COMPAT:
|
||||
lua_to_text = to_text;
|
||||
lua_to_xep54 = to_xep54;
|
||||
|
||||
text_to_lua = from_text;
|
||||
text_to_xep54 = function (...) return to_xep54(from_text(...)); end;
|
||||
|
||||
xep54_to_lua = from_xep54;
|
||||
xep54_to_text = function (...) return to_text(from_xep54(...)) end;
|
||||
};
|
@ -19,27 +19,35 @@ Details
|
||||
-------
|
||||
|
||||
mod\_register\_web has Prosody serve a web page where users can sign up
|
||||
for an account. It implements reCAPTCHA to prevent automated sign-ups
|
||||
(from bots, etc.).
|
||||
for an account. It implements a CAPTCHA system to prevent automated
|
||||
sign-ups (from bots, etc.).
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The module is served on Prosody's default HTTP ports at the path
|
||||
`/register_web`. More details on configuring HTTP modules in Prosody can
|
||||
be found in our [HTTP documentation](http://prosody.im/doc/http).
|
||||
be found in our [HTTP documentation](https://prosody.im/doc/http).
|
||||
|
||||
To configure the CAPTCHA you need to supply a 'captcha\_options' option:
|
||||
|
||||
captcha_options = {
|
||||
recaptcha_private_key = "12345";
|
||||
recaptcha_public_key = "78901";
|
||||
-- provider = "recaptcha";
|
||||
-- recaptcha_private_key = "12345";
|
||||
-- recaptcha_public_key = "78901";
|
||||
|
||||
-- provider = "hcaptcha";
|
||||
-- hcaptcha_private_key = "12345";
|
||||
-- hcaptcha_public_key = " 78901";
|
||||
|
||||
---- Default if no reCaptcha or hCaptcha options are set
|
||||
-- provider = "simplecaptcha";
|
||||
}
|
||||
|
||||
The keys for reCAPTCHA are available in your reCAPTCHA account, visit
|
||||
[reCAPTCHA](https://developers.google.com/recaptcha/) for more info.
|
||||
To only allow registration through this module, enter in the following
|
||||
to your configuration:
|
||||
|
||||
If no reCaptcha options are set, a simple built in captcha is used.
|
||||
registration_web_only = true
|
||||
|
||||
Customization
|
||||
-------------
|
||||
@ -52,6 +60,7 @@ Compatibility
|
||||
-------------
|
||||
|
||||
----- --------------
|
||||
0.12 Works
|
||||
0.10 Works
|
||||
0.9 Works
|
||||
0.8 Doesn't work
|
||||
|
@ -25,8 +25,8 @@ function template(data)
|
||||
return { apply = function(values) return (data:gsub("{([^}]+)}", values)); end }
|
||||
end
|
||||
|
||||
local function get_template(name)
|
||||
local fh = assert(module:load_resource(template_path..path_sep..name..".html"));
|
||||
local function get_template(name, extension)
|
||||
local fh = assert(module:load_resource(template_path..path_sep..name..extension));
|
||||
local data = assert(fh:read("*a"));
|
||||
fh:close();
|
||||
return template(data);
|
||||
@ -36,15 +36,30 @@ local function render(template, data)
|
||||
return tostring(template.apply(data));
|
||||
end
|
||||
|
||||
local register_tpl = get_template "register";
|
||||
local success_tpl = get_template "success";
|
||||
local register_tpl = get_template("register", ".html");
|
||||
local success_tpl = get_template("success", ".html");
|
||||
|
||||
local web_verified;
|
||||
local web_only = module:get_option_boolean("registration_web_only", false);
|
||||
if web_only then
|
||||
-- from mod_invites_register.lua
|
||||
module:hook("user-registering", function (event)
|
||||
local web_verified = event.web_verified;
|
||||
|
||||
if not web_verified then
|
||||
event.allowed = false;
|
||||
event.reason = "Registration on this server is through website only";
|
||||
return;
|
||||
end
|
||||
end);
|
||||
end
|
||||
|
||||
-- COMPAT `or request.conn:ip()`
|
||||
|
||||
if next(captcha_options) ~= nil then
|
||||
local provider = captcha_options.provider;
|
||||
if provider == nil or provider == "recaptcha" then
|
||||
local recaptcha_tpl = get_template "recaptcha";
|
||||
local recaptcha_tpl = get_template("recaptcha", ".html");
|
||||
|
||||
function generate_captcha(display_options)
|
||||
return recaptcha_tpl.apply(setmetatable({
|
||||
@ -77,7 +92,7 @@ if next(captcha_options) ~= nil then
|
||||
end);
|
||||
end
|
||||
elseif provider == "hcaptcha" then
|
||||
local captcha_tpl = get_template "hcaptcha";
|
||||
local captcha_tpl = get_template("hcaptcha", ".html");
|
||||
|
||||
function generate_captcha(display_options)
|
||||
return captcha_tpl.apply(setmetatable({
|
||||
@ -93,7 +108,7 @@ if next(captcha_options) ~= nil then
|
||||
function verify_captcha(request, form, callback)
|
||||
http.request("https://hcaptcha.com/siteverify", {
|
||||
body = http.formencode {
|
||||
secret = captcha_options.captcha_private_key;
|
||||
secret = captcha_options.hcaptcha_private_key;
|
||||
remoteip = request.ip or request.conn:ip();
|
||||
response = form["h-captcha-response"];
|
||||
};
|
||||
@ -116,8 +131,16 @@ else
|
||||
local hmac_sha1 = require "util.hashes".hmac_sha1;
|
||||
local secret = require "util.uuid".generate()
|
||||
local ops = { '+', '-' };
|
||||
local captcha_tpl = get_template "simplecaptcha";
|
||||
function generate_captcha()
|
||||
local captcha_tpl = get_template("simplecaptcha", ".html");
|
||||
function generate_captcha(display_options, lang)
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_question = "¿Qué es";
|
||||
else
|
||||
s_question = "What is";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
local op = ops[random(1, #ops)];
|
||||
local x, y = random(1, 9)
|
||||
repeat
|
||||
@ -135,54 +158,114 @@ else
|
||||
end
|
||||
local challenge = hmac_sha1(secret, answer, true);
|
||||
return captcha_tpl.apply {
|
||||
op = op, x = x, y = y, challenge = challenge;
|
||||
op = op, x = x, y = y, challenge = challenge, s_question = s_question;
|
||||
};
|
||||
end
|
||||
function verify_captcha(request, form, callback)
|
||||
if hmac_sha1(secret, form.captcha_reply or "", true) == form.captcha_challenge then
|
||||
callback(true);
|
||||
else
|
||||
-- begin translation
|
||||
if form.lang == "Español" then
|
||||
callback(false, "Verificación de Captcha fallida");
|
||||
else
|
||||
callback(false, "Captcha verification failed");
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function generate_page(event, display_options)
|
||||
function generate_page(event, lang, display_options)
|
||||
local request, response = event.request, event.response;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "Registro de cuenta XMPP";
|
||||
s_username = "Nombre de Usuario";
|
||||
s_password = "Contraseña";
|
||||
s_passwordconfirm = "Contraseña Confirmación";
|
||||
s_register = "¡Registro!";
|
||||
else
|
||||
s_title = "XMPP Account Registration";
|
||||
s_username = "Username";
|
||||
s_password = "Password";
|
||||
s_passwordconfirm = "Confirm Password";
|
||||
s_register = "Register!";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
response.headers.content_type = "text/html; charset=utf-8";
|
||||
return render(register_tpl, {
|
||||
path = request.path; hostname = module.host;
|
||||
notice = display_options and display_options.register_error or "";
|
||||
captcha = generate_captcha(display_options);
|
||||
})
|
||||
captcha = generate_captcha(display_options, lang);
|
||||
s_title = s_title;
|
||||
s_username = s_username;
|
||||
s_password = s_password;
|
||||
s_passwordconfirm = s_passwordconfirm;
|
||||
s_register = s_register;
|
||||
s_lang = lang;
|
||||
});
|
||||
end
|
||||
|
||||
function register_user(form, origin)
|
||||
local lang = form.lang;
|
||||
local username = form.username;
|
||||
local password = form.password;
|
||||
local confirm_password = form.confirm_password;
|
||||
local jid = nil;
|
||||
form.username, form.password, form.confirm_password = nil, nil, nil;
|
||||
form.password, form.confirm_password = nil, nil;
|
||||
|
||||
local prepped_username = nodeprep(username, true);
|
||||
if not prepped_username then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Nombre de usuario contiene caracteres prohibidos";
|
||||
else
|
||||
return nil, "Username contains forbidden characters";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if #prepped_username == 0 then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "El campo texto de nombre de usuario estaba vacío";
|
||||
else
|
||||
return nil, "The username field was empty";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if usermanager.user_exists(prepped_username, module.host) then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Nombre de usuario ya ocupado";
|
||||
else
|
||||
return nil, "Username already taken";
|
||||
end
|
||||
local registering = { username = prepped_username , host = module.host, additional = form, ip = origin.ip or origin.conn:ip(), allowed = true }
|
||||
-- end translation
|
||||
end
|
||||
|
||||
local registering = { username = prepped_username , host = module.host, additional = form, ip = origin.ip or origin.conn:ip(), allowed = true, web_verified = true }
|
||||
module:fire_event("user-registering", registering);
|
||||
if not registering.allowed then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, registering.reason or "Registro no permitido";
|
||||
else
|
||||
return nil, registering.reason or "Registration not allowed";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
if confirm_password ~= password then
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
return nil, "Las contraseñas no igualar";
|
||||
else
|
||||
return nil, "Passwords don't match";
|
||||
end
|
||||
-- end translation
|
||||
end
|
||||
local ok, err = usermanager.create_user(prepped_username, password, module.host);
|
||||
if ok then
|
||||
jid = prepped_username.."@"..module.host
|
||||
@ -202,20 +285,41 @@ function register_user(form, origin)
|
||||
source = module.name,
|
||||
ip = origin.ip or origin.conn:ip(),
|
||||
});
|
||||
module:log("info", "New Account Registered: %s#%s@%s", prepped_username, origin.ip, module.host);
|
||||
end
|
||||
|
||||
return jid, err;
|
||||
end
|
||||
|
||||
function generate_success(event, jid)
|
||||
return render(success_tpl, { jid = jid });
|
||||
function generate_success(event, jid, lang)
|
||||
local request, response = event.request, event.response;
|
||||
|
||||
-- begin translation
|
||||
if lang == "Español" then
|
||||
s_title = "¡Registro exitoso!";
|
||||
s_message = "Tu cuenta es";
|
||||
else
|
||||
s_title = "Registration succeeded!";
|
||||
s_message = "Your account is";
|
||||
end
|
||||
-- end translation
|
||||
|
||||
response.headers.content_type = "text/html; charset=utf-8";
|
||||
return render(success_tpl, {
|
||||
path = request.path;
|
||||
jid = jid;
|
||||
lang = lang;
|
||||
s_title = s_title;
|
||||
s_message = s_message;
|
||||
});
|
||||
end
|
||||
|
||||
function generate_register_response(event, jid, err)
|
||||
function generate_register_response(event, jid, lang, err)
|
||||
event.response.headers.content_type = "text/html; charset=utf-8";
|
||||
if jid then
|
||||
return generate_success(event, jid);
|
||||
return generate_success(event, jid, lang);
|
||||
else
|
||||
return generate_page(event, { register_error = err });
|
||||
return generate_page(event, lang, { register_error = err });
|
||||
end
|
||||
end
|
||||
|
||||
@ -225,9 +329,9 @@ function handle_form(event)
|
||||
verify_captcha(request, form, function (ok, err)
|
||||
if ok then
|
||||
local jid, register_err = register_user(form, request);
|
||||
response:send(generate_register_response(event, jid, register_err));
|
||||
response:send(generate_register_response(event, jid, form.lang, register_err));
|
||||
else
|
||||
response:send(generate_page(event, { register_error = err }));
|
||||
response:send(generate_page(event, form.lang, { register_error = err }));
|
||||
end
|
||||
end);
|
||||
return true; -- Leave connection open until we respond above
|
||||
@ -236,6 +340,7 @@ end
|
||||
module:provides("http", {
|
||||
title = module:get_option_string("register_web_title", "Account Registration");
|
||||
route = {
|
||||
["GET /style.css"] = render(get_template("style", ".css"), {});
|
||||
GET = generate_page;
|
||||
["GET /"] = generate_page;
|
||||
POST = handle_form;
|
||||
|
@ -2,32 +2,47 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>XMPP Account Registration</title>
|
||||
<link rel="stylesheet" type="text/css" href="{path}/style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>XMPP Account Registration</h1>
|
||||
<div id="estilo3" class="formulario">
|
||||
<h1>{s_title}</h1>
|
||||
</div>
|
||||
<div id="estilo2" class="formulario2">
|
||||
<div>
|
||||
<form action="{path}" method="POST">
|
||||
<input name="lang" type="submit" value="English">
|
||||
<input name="lang" type="submit" value="Español">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="estilo" class="formulario">
|
||||
<p class="error">{notice}</p>
|
||||
<form action="{path}" method="POST">
|
||||
<p>{notice}</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Username:</th>
|
||||
<th>{s_username}:</th>
|
||||
<td><input name="username" required>@{hostname}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<th>{s_password}:</th>
|
||||
<td><input name="password" required type="password"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Confirm Password:</th>
|
||||
<th>{s_passwordconfirm}:</th>
|
||||
<td><input name="confirm_password" required type="password"></td>
|
||||
</tr>
|
||||
{captcha}
|
||||
<input name="lang" type="hidden" value="{s_lang}">
|
||||
<tr>
|
||||
<td colspan="2"><input type="submit" value="Register!"></td>
|
||||
<td colspan="2"><input id="button" class="button" type="submit" value="{s_register}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<tr>
|
||||
<th>What is {x} {op} {y}?</th><td>
|
||||
<th>{s_question} {x} {op} {y}?</th>
|
||||
<td>
|
||||
<input name="captcha_challenge" type="hidden" value="{challenge}">
|
||||
<input name="captcha_reply" pattern="[0-9]+" required type="number">
|
||||
</td></tr>
|
||||
</td>
|
||||
</tr>
|
||||
|
164
mod_register_web/templates/style.css
Normal file
164
mod_register_web/templates/style.css
Normal file
@ -0,0 +1,164 @@
|
||||
body {
|
||||
font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
p, h1, form, button {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
clear:both;
|
||||
height:1px;
|
||||
}
|
||||
|
||||
/* ----------- My Form ----------- */
|
||||
.formulario {
|
||||
margin:0 auto;
|
||||
width:500px;
|
||||
padding:14px;
|
||||
}
|
||||
|
||||
.formulario2 {
|
||||
margin:0 auto;
|
||||
width:500px;
|
||||
padding:14px;
|
||||
}
|
||||
|
||||
/* ----------- stylized ----------- */
|
||||
#estilo {
|
||||
border:solid 2px #f2ddb7;
|
||||
background:#fbf4eb;
|
||||
}
|
||||
|
||||
#estilo2 {
|
||||
border-left:solid 2px #f2ddb7;
|
||||
border-right:solid 2px #f2ddb7;
|
||||
background:#fbf4eb;
|
||||
}
|
||||
|
||||
#estilo3 {
|
||||
border:solid 2px #f2ddb7;
|
||||
background:#fbf4eb;
|
||||
}
|
||||
|
||||
#estilo h1 {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
margin-bottom:8px;
|
||||
}
|
||||
|
||||
#estilo p {
|
||||
font-size:11px;
|
||||
color:#666666;
|
||||
margin-bottom:20px;
|
||||
border-bottom:solid 1px #f2ddb7;
|
||||
padding-bottom:10px;
|
||||
}
|
||||
|
||||
#estilo p.error {
|
||||
font-size:12px;
|
||||
font-weight:bold;
|
||||
color:red;
|
||||
margin-bottom:20px;
|
||||
border-bottom:solid 1px #f2ddb7;
|
||||
padding-bottom:10px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#estilo label {
|
||||
display:block;
|
||||
font-weight:bold;
|
||||
text-align:right;
|
||||
width:140px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
#estilo .small {
|
||||
color:#666666;
|
||||
display:block;
|
||||
font-size:11px;
|
||||
font-weight:normal;
|
||||
text-align:right;
|
||||
width:140px;
|
||||
}
|
||||
|
||||
#estilo input {
|
||||
/* float:left; */
|
||||
font-size:12px;
|
||||
padding:4px 2px;
|
||||
border:solid 1px #e4cfaa;
|
||||
width:200px;
|
||||
margin:2px 0 20px 10px;
|
||||
}
|
||||
|
||||
#estilo th {
|
||||
padding-bottom:18px;
|
||||
}
|
||||
|
||||
#estilo2 div {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
#estilo2 input {
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
#estilo3 h1 {
|
||||
font-size:14px;
|
||||
font-weight:bold;
|
||||
margin-bottom:8px;
|
||||
}
|
||||
|
||||
#estilo .button {
|
||||
margin:2px 150px 20px 150px;
|
||||
}
|
||||
|
||||
.button {
|
||||
-moz-box-shadow:inset 0px 1px 0px 0px #fce3ca;
|
||||
-webkit-box-shadow:inset 0px 1px 0px 0px #fce3ca;
|
||||
box-shadow:inset 0px 1px 0px 0px #fce3ca;
|
||||
background-color:#ffbb79;
|
||||
-webkit-border-top-left-radius:18px;
|
||||
-moz-border-radius-topleft:18px;
|
||||
border-top-left-radius:18px;
|
||||
-webkit-border-top-right-radius:18px;
|
||||
-moz-border-radius-topright:18px;
|
||||
border-top-right-radius:18px;
|
||||
-webkit-border-bottom-right-radius:18px;
|
||||
-moz-border-radius-bottomright:18px;
|
||||
border-bottom-right-radius:18px;
|
||||
-webkit-border-bottom-left-radius:18px;
|
||||
-moz-border-radius-bottomleft:18px;
|
||||
border-bottom-left-radius:18px;
|
||||
text-indent:0;
|
||||
border:1px solid #f59d46;
|
||||
display:inline-block;
|
||||
color:#ffffff;
|
||||
font-family:Arial;
|
||||
font-size:15px;
|
||||
font-weight:bold;
|
||||
font-style:normal;
|
||||
height:40px;
|
||||
line-height:40px;
|
||||
width:100px;
|
||||
text-decoration:none;
|
||||
text-align:center;
|
||||
text-shadow:1px 1px 0px #ce7a28;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color:#ee9741;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
position:relative;
|
||||
top:1px;
|
||||
}
|
||||
|
@ -2,12 +2,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Registration succeeded!</title>
|
||||
<link rel="stylesheet" type="text/css" href="{path}/style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7" />
|
||||
<title>{s_title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Registration succeeded!</h1>
|
||||
<p>Your account is</p>
|
||||
<pre>{jid}</pre>
|
||||
<p>- happy chatting!</p>
|
||||
<div id="estilo" class="formulario">
|
||||
<h1>{s_title}</h1>
|
||||
<p></p>
|
||||
<h1>{s_message}:</h1>
|
||||
<h1><i>{jid}</i></h1>
|
||||
<!--
|
||||
<br />
|
||||
<h1>- happy chatting! -</h1>
|
||||
-->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user