diff --git a/mod_email/README.markdown b/mod_email/README.markdown new file mode 100644 index 0000000..c566f6b --- /dev/null +++ b/mod_email/README.markdown @@ -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 + ----- -------------- diff --git a/mod_email_pass/README.markdown b/mod_email_pass/README.markdown index 7cda2ab..a99e8fe 100644 --- a/mod_email_pass/README.markdown +++ b/mod_email_pass/README.markdown @@ -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.9 Works + 0.12 Works + 0.9 Works ----- ------- diff --git a/mod_email_pass/mod_email_pass.lua b/mod_email_pass/mod_email_pass.lua index a188736..a8a9c8d 100644 --- a/mod_email_pass/mod_email_pass.lua +++ b/mod_email_pass/mod_email_pass.lua @@ -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) - local request = event.request; +function generate_token_page(event, lang, display_options) + local request = event.request; - return render(token_tpl, { - path = request.path; hostname = module.host; - token = request.url.query; - notice = display_options and display_options.register_error or ""; - }) + -- 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; + 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)) + + 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 + 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 @@ -179,7 +205,7 @@ end expireTokens = function() for token,value in pairs(tokens_mails) do if isExpired(token) then - module:log("info","Expiring password reset request from user '%s', not used.", tokens_mails[token]); + module:log("info", "Expiring password reset request from user '%s', not used.", tokens_mails[token]); tokens_mails[token] = nil; tokens_expiration[token] = nil; end @@ -197,121 +223,427 @@ 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 function sendMessage(jid, subject, message) - local msg = st.message({ from = module.host; to = jid; }): - tag("subject"):text(subject):up(): - tag("body"):text(message); - module:send(msg); + local msg = st.message({ from = module.host; to = jid; }): + tag("subject"):text(subject):up(): + tag("body"):text(message); + module:send(msg); 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"; - end - if #prepped_username == 0 then - return nil, "El campo usuario está vacio"; - end - if not usermanager.user_exists(prepped_username, module.host) then - return nil, "El usuario NO existe"; - 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"; - else - if not vcarduser.EMAIL then - return nil, "Esa cuente no tiene ningún email configurado en su vCard"; + 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 - - email = string.lower(vcarduser.EMAIL[1]); - - if email ~= string.lower(prepped_mail) then - return nil, "Dirección eMail incorrecta"; + -- 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á vacío"; + else + return nil, "The username text field is empty"; 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); + -- 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 - - local url_token = generateToken(jid); - local url = generateUrl(url_token); - local email_body = render(get_template("sendtoken",".mail"), {jid = jid, url = url} ); - - module:log("info", "Sending password reset mail to user %s", jid); - send_email(email, smtp_address, email_body, mail_subject); - return "ok"; + -- end translation end + if #prepped_mail == 0 then + -- begin translation + if lang == "Español" then + return nil, "El campo texto de email está vacío"; + else + return nil, "The email text field is empty"; + end + -- end translation + end + + 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 + -- 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)] + 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, 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); + 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 + local token = form.token; local password = form.newpassword; + local passwordconfirmation = form.newpasswordconfirmation; + form.newpassword, form.newpasswordconfirmation = nil, nil; if not token then - return nil, "El Token es inválido"; + -- 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 - return nil, "El Token no existe o ya fué usado"; + 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 @@ -320,43 +652,100 @@ function handle_form_token(event) local form = http.formdecode(request.body); local token_ok, token_err = send_token_mail(form, request); - response:send(generate_register_response(event, form, token_ok, token_err)); + response:send(generate_register_response(event, form, token_ok, token_err)); 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); - else - return generate_token_page(event, { register_error = err }); - end + local message; + if ok then + return generate_reset_success(event, form.lang); + else + 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 function handle_form_reset(event) local request, response = event.request, event.response; - local form = http.formdecode(request.body); + local form = http.formdecode(request.body); - local reset_ok, reset_err = reset_password_with_token(form, request); - response:send(generate_reset_response(event, form, reset_ok, reset_err)); + local reset_ok, reset_err = reset_password_with_token(form, request); + response:send(generate_reset_response(event, form, reset_ok, reset_err)); - return true; -- Leave connection open until we respond above + 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 /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; }; diff --git a/mod_email_pass/templates/changemail.html b/mod_email_pass/templates/changemail.html new file mode 100644 index 0000000..07910df --- /dev/null +++ b/mod_email_pass/templates/changemail.html @@ -0,0 +1,52 @@ + + +
+ + + +
- 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.
+ {s_text}
{notice}
+ +Your account is
-{jid}-
- happy chatting!
+