swad-core/swad_password.c

935 lines
34 KiB
C
Raw Normal View History

2014-12-01 23:55:08 +01:00
// swad_password.c: Users' passwords
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
2019-01-07 21:52:19 +01:00
Copyright (C) 1999-2019 Antonio Ca<EFBFBD>as Vargas
2014-12-01 23:55:08 +01:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
2018-10-24 09:25:09 +02:00
#define _GNU_SOURCE // For asprintf
#include <stdio.h> // For asprintf
2014-12-01 23:55:08 +01:00
#include <stdlib.h> // For system, getenv, etc.
#include <string.h> // For string functions
#include <sys/wait.h> // For the macro WEXITSTATUS
#include <unistd.h> // For unlink
2017-06-10 21:38:10 +02:00
#include "swad_box.h"
2014-12-01 23:55:08 +01:00
#include "swad_database.h"
2017-03-30 11:20:06 +02:00
#include "swad_enrolment.h"
2018-11-09 20:47:39 +01:00
#include "swad_form.h"
2014-12-01 23:55:08 +01:00
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_password.h"
#include "swad_parameter.h"
2017-06-11 20:09:59 +02:00
#include "swad_table.h"
2014-12-01 23:55:08 +01:00
#include "swad_user.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
// If there are X users with the same password,
// it means than the password is trivial
// and another user can not change his/her password to this
#define Pwd_MAX_OTHER_USERS_USING_THE_SAME_PASSWORD 2
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
2018-10-16 01:36:13 +02:00
const char *Pwd_PASSWORD_SECTION_ID = "password_section";
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
2018-10-16 09:01:02 +02:00
static void Pwd_CheckAndUpdateNewPwd (struct UsrData *UsrDat);
2015-04-02 18:39:49 +02:00
static void Pwd_PutLinkToSendNewPasswdParams (void);
2017-03-07 01:56:41 +01:00
static void Pwd_CreateANewPassword (char PlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1]);
2014-12-01 23:55:08 +01:00
static bool Pwd_CheckIfPasswdIsUsrIDorName (const char *PlainPassword);
static unsigned Pwd_GetNumOtherUsrsWhoUseThisPassword (const char *EncryptedPassword,long UsrCod);
/*****************************************************************************/
/************* Get parameter with my plain password from a form **************/
/*****************************************************************************/
void Pwd_GetParamUsrPwdLogin (void)
{
/***** Get plain password from form *****/
2017-03-07 01:56:41 +01:00
Par_GetParToText ("UsrPwd",Gbl.Usrs.Me.LoginPlainPassword,
Pwd_MAX_BYTES_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
/***** Encrypt password *****/
Cry_EncryptSHA512Base64 (Gbl.Usrs.Me.LoginPlainPassword,Gbl.Usrs.Me.LoginEncryptedPassword);
}
/*****************************************************************************/
/**** Check if login password is the same as current password in database ****/
/*****************************************************************************/
// Returns true if there's no current password in database, or if login password is the same
// Returns false if there's a current password in database and is not the same as the login password
bool Pwd_CheckCurrentPassword (void)
{
return (Gbl.Usrs.Me.UsrDat.Password[0] ?
!strcmp (Gbl.Usrs.Me.LoginEncryptedPassword,Gbl.Usrs.Me.UsrDat.Password) : true);
}
/*****************************************************************************/
/**** Check if login password is the same as current password in database ****/
/*****************************************************************************/
// Returns true if there's pending password in database and login password is the same
// Returns false if there's no a pending password in database, or if pending password is not the same as the login password
bool Pwd_CheckPendingPassword (void)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Get pending password from database *****/
2018-11-01 13:03:25 +01:00
if (DB_QuerySELECT (&mysql_res,"can not get pending password",
"SELECT PendingPassword FROM pending_passwd"
" WHERE UsrCod=%ld",
Gbl.Usrs.Me.UsrDat.UsrCod))
2014-12-01 23:55:08 +01:00
{
/* Get encrypted pending password */
row = mysql_fetch_row (mysql_res);
2017-01-15 22:58:26 +01:00
Str_Copy (Gbl.Usrs.Me.PendingPassword,row[0],
2017-03-13 14:22:36 +01:00
Pwd_BYTES_ENCRYPTED_PASSWORD);
2014-12-01 23:55:08 +01:00
}
else
Gbl.Usrs.Me.PendingPassword[0] = '\0';
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
return (Gbl.Usrs.Me.PendingPassword[0] ?
!strcmp (Gbl.Usrs.Me.LoginEncryptedPassword,Gbl.Usrs.Me.PendingPassword) :
false);
}
/*****************************************************************************/
/************ Assign my pending password to my current password **************/
/*****************************************************************************/
void Pwd_AssignMyPendingPasswordToMyCurrentPassword (void)
{
/***** Update my current password in database *****/
2018-11-03 12:16:40 +01:00
DB_QueryUPDATE ("can not update your password",
"UPDATE usr_data SET Password='%s'"
" WHERE UsrCod=%ld",
Gbl.Usrs.Me.PendingPassword,
Gbl.Usrs.Me.UsrDat.UsrCod);
2014-12-01 23:55:08 +01:00
/***** Update my current password *****/
2017-01-15 22:58:26 +01:00
Str_Copy (Gbl.Usrs.Me.UsrDat.Password,Gbl.Usrs.Me.PendingPassword,
2017-03-13 14:22:36 +01:00
Pwd_BYTES_ENCRYPTED_PASSWORD);
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
2018-10-16 08:48:51 +02:00
/*********************** Update my password in database **********************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2018-10-16 08:48:51 +02:00
void Pwd_UpdateMyPwd (void)
2014-12-01 23:55:08 +01:00
{
extern const char *Txt_You_have_not_entered_your_password_correctly;
2017-03-07 01:56:41 +01:00
char PlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1];
2014-12-01 23:55:08 +01:00
/***** Get plain password from form *****/
2017-03-07 01:56:41 +01:00
Par_GetParToText ("UsrPwd",PlainPassword,Pwd_MAX_BYTES_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
/***** Encrypt password *****/
Cry_EncryptSHA512Base64 (PlainPassword,Gbl.Usrs.Me.LoginEncryptedPassword);
2018-10-16 09:01:02 +02:00
/***** Check current password *****/
2014-12-01 23:55:08 +01:00
if (Pwd_CheckCurrentPassword ())
2018-10-16 09:01:02 +02:00
/***** Check and update new password *****/
2018-10-25 13:05:36 +02:00
Pwd_CheckAndUpdateNewPwd (&Gbl.Usrs.Me.UsrDat);
2014-12-01 23:55:08 +01:00
else
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_You_have_not_entered_your_password_correctly);
2014-12-01 23:55:08 +01:00
}
2018-10-16 08:48:51 +02:00
/*****************************************************************************/
/********************* Update another user's password ************************/
/*****************************************************************************/
void Pwd_UpdateOtherUsrPwd (void)
{
/***** Get other user's code from form and get user's data *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ())
{
if (Usr_ICanEditOtherUsr (&Gbl.Usrs.Other.UsrDat))
2018-10-16 09:01:02 +02:00
/***** Check and update password *****/
Pwd_CheckAndUpdateNewPwd (&Gbl.Usrs.Other.UsrDat);
2018-10-16 08:48:51 +02:00
else
2019-03-09 20:12:44 +01:00
Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission ();
2018-10-16 08:48:51 +02:00
}
else // User not found
2019-03-09 20:12:44 +01:00
Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission ();
2018-10-16 08:48:51 +02:00
}
2018-10-16 09:01:02 +02:00
/*****************************************************************************/
/********************* Check and update new password *************************/
/*****************************************************************************/
static void Pwd_CheckAndUpdateNewPwd (struct UsrData *UsrDat)
{
extern const char *Txt_You_have_not_written_twice_the_same_new_password;
extern const char *Txt_The_password_has_been_changed_successfully;
char NewPlainPassword[2][Pwd_MAX_BYTES_PLAIN_PASSWORD + 1];
char NewEncryptedPassword[Pwd_BYTES_ENCRYPTED_PASSWORD + 1];
Par_GetParToText ("Paswd1",NewPlainPassword[0],Pwd_MAX_BYTES_PLAIN_PASSWORD);
Par_GetParToText ("Paswd2",NewPlainPassword[1],Pwd_MAX_BYTES_PLAIN_PASSWORD);
/***** Check if I have written twice the same password *****/
if (strcmp (NewPlainPassword[0],NewPlainPassword[1]))
// Passwords don't match
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_You_have_not_written_twice_the_same_new_password);
2018-10-16 09:01:02 +02:00
else
{
Cry_EncryptSHA512Base64 (NewPlainPassword[0],NewEncryptedPassword);
if (Pwd_SlowCheckIfPasswordIsGood (NewPlainPassword[0],
NewEncryptedPassword,
UsrDat->UsrCod)) // New password is good?
{
2019-02-15 21:09:18 +01:00
/* Update user's data */
2018-10-16 09:01:02 +02:00
Str_Copy (UsrDat->Password,NewEncryptedPassword,
Pwd_BYTES_ENCRYPTED_PASSWORD);
Ses_UpdateSessionDataInDB ();
Enr_UpdateUsrData (UsrDat);
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_SUCCESS,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_has_been_changed_successfully);
2018-10-16 09:01:02 +02:00
}
}
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2016-11-16 23:19:52 +01:00
/*************** Show form to send a new password by email *******************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2015-04-02 18:39:49 +02:00
void Pwd_PutLinkToSendNewPasswd (void)
{
2015-12-08 12:09:18 +01:00
extern const char *Txt_Forgotten_password;
2015-04-02 18:39:49 +02:00
2019-01-12 03:00:59 +01:00
Lay_PutContextualLinkIconText (ActReqSndNewPwd,NULL,
Pwd_PutLinkToSendNewPasswdParams,
"key.svg",
Txt_Forgotten_password);
2015-04-02 18:39:49 +02:00
}
static void Pwd_PutLinkToSendNewPasswdParams (void)
{
Par_PutHiddenParamString ("UsrId",Gbl.Usrs.Me.UsrIdLogin);
}
/*****************************************************************************/
2016-11-16 23:19:52 +01:00
/*************** Show form to send a new password by email *******************/
2015-04-02 18:39:49 +02:00
/*****************************************************************************/
2014-12-01 23:55:08 +01:00
void Pwd_ShowFormSendNewPwd (void)
{
2016-11-13 11:46:04 +01:00
extern const char *Hlp_PROFILE_Password;
2019-02-22 21:47:50 +01:00
extern const char *The_ClassFormInBox[The_NUM_THEMES];
2014-12-01 23:55:08 +01:00
extern const char *Txt_If_you_have_forgotten_your_password_;
2015-12-08 12:09:18 +01:00
extern const char *Txt_Forgotten_password;
2014-12-01 23:55:08 +01:00
extern const char *Txt_nick_email_or_ID;
2015-12-08 12:09:18 +01:00
extern const char *Txt_Get_a_new_password;
2014-12-01 23:55:08 +01:00
2017-06-12 14:16:33 +02:00
/***** Start form *****/
2019-02-14 20:51:08 +01:00
Frm_StartForm (ActSndNewPwd);
2014-12-01 23:55:08 +01:00
2017-06-12 14:16:33 +02:00
/***** Start box *****/
2017-06-10 21:38:10 +02:00
Box_StartBox (NULL,Txt_Forgotten_password,NULL,
2017-06-12 15:03:29 +02:00
Hlp_PROFILE_Password,Box_NOT_CLOSABLE);
2015-04-11 23:46:21 +02:00
/***** Help text *****/
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_INFO,Txt_If_you_have_forgotten_your_password_);
2015-04-11 23:46:21 +02:00
2014-12-01 23:55:08 +01:00
/***** User's ID/nickname *****/
2015-04-11 23:46:21 +02:00
fprintf (Gbl.F.Out,"<label class=\"%s\">"
2016-12-20 02:18:50 +01:00
"%s:&nbsp;"
2015-04-12 18:01:06 +02:00
"<input type=\"text\" name=\"UsrId\""
2019-02-14 20:51:08 +01:00
" size=\"8\" maxlength=\"%u\" value=\"%s\""
" required=\"required\" />"
2016-12-20 02:18:50 +01:00
"</label>",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],Txt_nick_email_or_ID,
2017-03-13 13:17:53 +01:00
Cns_MAX_CHARS_EMAIL_ADDRESS,Gbl.Usrs.Me.UsrIdLogin);
2014-12-01 23:55:08 +01:00
2017-06-12 14:16:33 +02:00
/***** Send button and end box *****/
2017-06-11 19:02:40 +02:00
Box_EndBoxWithButton (Btn_CONFIRM_BUTTON,Txt_Get_a_new_password);
2015-04-11 23:46:21 +02:00
2014-12-01 23:55:08 +01:00
/***** End form *****/
2018-11-09 20:47:39 +01:00
Frm_EndForm ();
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
2016-11-16 23:19:52 +01:00
/*********************** Send a new password by email ************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
void Pwd_ChkIdLoginAndSendNewPwd (void)
{
2015-03-27 21:41:53 +01:00
extern const char *Txt_You_must_enter_your_nick_email_or_ID;
2014-12-01 23:55:08 +01:00
extern const char *Txt_There_was_a_problem_sending_an_email_automatically;
extern const char *Txt_If_you_have_written_your_ID_nickname_or_email_correctly_;
struct ListUsrCods ListUsrCods;
2019-02-14 20:51:08 +01:00
unsigned NumUsr;
2017-03-07 01:56:41 +01:00
char NewRandomPlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1];
2014-12-01 23:55:08 +01:00
int ReturnCode;
2019-02-17 01:14:55 +01:00
char ErrorTxt[256];
2014-12-01 23:55:08 +01:00
/***** Check if user's ID or nickname is not empty *****/
if (!Gbl.Usrs.Me.UsrIdLogin[0])
{
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_WARNING,Txt_You_must_enter_your_nick_email_or_ID);
2014-12-01 23:55:08 +01:00
Pwd_ShowFormSendNewPwd ();
return;
}
/***** Reset default list of users' codes *****/
ListUsrCods.NumUsrs = 0;
ListUsrCods.Lst = NULL;
/***** Check if user exists *****/
/* Check if user has typed his user's ID or his nickname */
if (Nck_CheckIfNickWithArrobaIsValid (Gbl.Usrs.Me.UsrIdLogin)) // 1: It's a nickname
{
if ((Gbl.Usrs.Me.UsrDat.UsrCod = Nck_GetUsrCodFromNickname (Gbl.Usrs.Me.UsrIdLogin)) > 0)
{
/* Get user's data */
ListUsrCods.NumUsrs = 1;
Usr_AllocateListUsrCods (&ListUsrCods);
2019-03-06 14:31:21 +01:00
ListUsrCods.Lst[0] = Gbl.Usrs.Me.UsrDat.UsrCod;
2014-12-01 23:55:08 +01:00
}
}
2016-11-16 23:19:52 +01:00
else if (Mai_CheckIfEmailIsValid (Gbl.Usrs.Me.UsrIdLogin)) // 2: It's an email
2014-12-01 23:55:08 +01:00
{
if ((Gbl.Usrs.Me.UsrDat.UsrCod = Mai_GetUsrCodFromEmail (Gbl.Usrs.Me.UsrIdLogin)) > 0)
{
/* Get user's data */
ListUsrCods.NumUsrs = 1;
Usr_AllocateListUsrCods (&ListUsrCods);
2019-03-06 14:31:21 +01:00
ListUsrCods.Lst[0] = Gbl.Usrs.Me.UsrDat.UsrCod;
2014-12-01 23:55:08 +01:00
}
}
2016-11-16 23:19:52 +01:00
else // 3: It's not a nickname nor email
2014-12-01 23:55:08 +01:00
{
// Users' IDs are always stored internally in capitals and without leading zeros
Str_RemoveLeadingZeros (Gbl.Usrs.Me.UsrIdLogin);
if (ID_CheckIfUsrIDIsValid (Gbl.Usrs.Me.UsrIdLogin))
{
/***** Allocate space for the list *****/
ID_ReallocateListIDs (&Gbl.Usrs.Me.UsrDat,1);
// User has typed a user's ID
2017-01-15 22:58:26 +01:00
Str_Copy (Gbl.Usrs.Me.UsrDat.IDs.List[0].ID,Gbl.Usrs.Me.UsrIdLogin,
2017-03-07 01:56:41 +01:00
ID_MAX_BYTES_USR_ID);
2016-03-21 19:52:40 +01:00
Str_ConvertToUpperText (Gbl.Usrs.Me.UsrDat.IDs.List[0].ID);
2014-12-01 23:55:08 +01:00
/* Get users' codes for this ID */
if (!ID_GetListUsrCodsFromUsrID (&Gbl.Usrs.Me.UsrDat,NULL,&ListUsrCods,true)) // Only confirmed IDs
// If no users found with confirmed IDs, try to get all users (confirmed or not)
ID_GetListUsrCodsFromUsrID (&Gbl.Usrs.Me.UsrDat,NULL,&ListUsrCods,false); // All users (confirmed or not)
}
}
2016-11-16 23:19:52 +01:00
/***** Send a new password via email when user exists *****/
2019-02-14 20:51:08 +01:00
for (NumUsr = 0;
NumUsr < ListUsrCods.NumUsrs;
NumUsr++)
2014-12-01 23:55:08 +01:00
{
2019-02-14 20:51:08 +01:00
Gbl.Usrs.Me.UsrDat.UsrCod = ListUsrCods.Lst[NumUsr];
2019-03-19 13:22:14 +01:00
Usr_GetUsrDataFromUsrCod (&Gbl.Usrs.Me.UsrDat,Usr_DONT_GET_PREFS); // Get my data
2019-02-14 20:51:08 +01:00
if (Gbl.Usrs.Me.UsrDat.Email[0])
switch ((ReturnCode = Pwd_SendNewPasswordByEmail (NewRandomPlainPassword)))
{
case 0: // Message sent successfully
Pwd_SetMyPendingPassword (NewRandomPlainPassword);
break;
case 1:
Lay_ShowErrorAndExit (Txt_There_was_a_problem_sending_an_email_automatically);
break;
default:
2019-02-17 01:14:55 +01:00
snprintf (ErrorTxt,sizeof (ErrorTxt),
2019-02-14 20:51:08 +01:00
"Internal error: an email message has not been sent successfully."
" Error code returned by the script: %d",
ReturnCode);
2019-02-17 01:14:55 +01:00
Lay_ShowErrorAndExit (ErrorTxt);
2019-02-14 20:51:08 +01:00
break;
}
2014-12-01 23:55:08 +01:00
}
2019-02-14 20:51:08 +01:00
/***** Free list of users' codes *****/
Usr_FreeListUsrCods (&ListUsrCods);
/***** Help message *****/
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_INFO,Txt_If_you_have_written_your_ID_nickname_or_email_correctly_);
2019-02-14 20:51:08 +01:00
/**** Show forms to login / create account again *****/
Usr_WriteLandingPage ();
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
2016-11-16 23:19:52 +01:00
/*********************** Send a new password by email ************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
// Gbl.Usrs.Me.UsrDat must be filled
// Return code returned by command
2017-03-07 01:56:41 +01:00
int Pwd_SendNewPasswordByEmail (char NewRandomPlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1])
2014-12-01 23:55:08 +01:00
{
extern const char *Txt_The_following_password_has_been_assigned_to_you_to_log_in_X_NO_HTML;
2018-12-08 16:43:13 +01:00
extern const char *Txt_New_password_NO_HTML[1 + Lan_NUM_LANGUAGES];
2018-10-04 21:57:25 +02:00
char Command[2048 +
Cfg_MAX_BYTES_SMTP_PASSWORD +
Cns_MAX_BYTES_EMAIL_ADDRESS +
PATH_MAX]; // Command to execute for sending an email
2014-12-01 23:55:08 +01:00
int ReturnCode;
/***** Create temporary file for mail content *****/
Mai_CreateFileNameMail ();
/***** Create a new random password *****/
Pwd_CreateANewPassword (NewRandomPlainPassword);
/***** Write mail content into file and close file *****/
/* Welcome note */
Mai_WriteWelcomeNoteEMail (&Gbl.Usrs.Me.UsrDat);
/* Message body */
fprintf (Gbl.Msg.FileMail,Txt_The_following_password_has_been_assigned_to_you_to_log_in_X_NO_HTML,
2017-01-28 15:58:46 +01:00
Cfg_PLATFORM_SHORT_NAME,NewRandomPlainPassword,Cfg_URL_SWAD_CGI,
(unsigned) (Cfg_TIME_TO_DELETE_OLD_PENDING_PASSWORDS / (24L * 60L * 60L)),
2014-12-01 23:55:08 +01:00
Gbl.Usrs.Me.UsrDat.Email);
/* Footer note */
Mai_WriteFootNoteEMail (Gbl.Prefs.Language);
fclose (Gbl.Msg.FileMail);
2016-11-16 23:19:52 +01:00
/***** Call the script to send an email *****/
2018-10-18 02:02:32 +02:00
snprintf (Command,sizeof (Command),
"%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"[%s] %s\" \"%s\"",
Cfg_COMMAND_SEND_AUTOMATIC_EMAIL,
Cfg_AUTOMATIC_EMAIL_SMTP_SERVER,
Cfg_AUTOMATIC_EMAIL_SMTP_PORT,
Cfg_AUTOMATIC_EMAIL_FROM,
Gbl.Config.SMTPPassword,
Gbl.Usrs.Me.UsrDat.Email,
Cfg_PLATFORM_SHORT_NAME,
Txt_New_password_NO_HTML[Gbl.Usrs.Me.UsrDat.Prefs.Language],
Gbl.Msg.FileNameMail);
2014-12-01 23:55:08 +01:00
ReturnCode = system (Command);
if (ReturnCode == -1)
2016-11-16 23:19:52 +01:00
Lay_ShowErrorAndExit ("Error when running script to send email.");
2014-12-01 23:55:08 +01:00
/***** Remove temporary file *****/
unlink (Gbl.Msg.FileNameMail);
/***** Write message depending on return code *****/
return WEXITSTATUS (ReturnCode);
}
/*****************************************************************************/
/*********************** Create a new random password ************************/
/*****************************************************************************/
2017-03-07 01:56:41 +01:00
static void Pwd_CreateANewPassword (char PlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1])
2014-12-01 23:55:08 +01:00
{
2017-03-07 01:56:41 +01:00
Str_CreateRandomAlphanumStr (PlainPassword,Pwd_MIN_CHARS_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/*************************** Set my pending password *************************/
/*****************************************************************************/
2017-03-07 01:56:41 +01:00
void Pwd_SetMyPendingPassword (char PlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1])
2014-12-01 23:55:08 +01:00
{
/***** Encrypt my pending password *****/
Cry_EncryptSHA512Base64 (PlainPassword,Gbl.Usrs.Me.PendingPassword);
/***** Remove expired pending passwords from database *****/
2018-11-02 22:00:31 +01:00
DB_QueryDELETE ("can not remove expired pending passwords",
2019-02-13 13:32:11 +01:00
"DELETE LOW_PRIORITY FROM pending_passwd"
" WHERE DateAndTime<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)",
2018-11-02 22:00:31 +01:00
Cfg_TIME_TO_DELETE_OLD_PENDING_PASSWORDS);
2014-12-01 23:55:08 +01:00
/***** Update my current password in database *****/
2018-11-02 16:39:35 +01:00
DB_QueryREPLACE ("can not create pending password",
"REPLACE INTO pending_passwd"
" (UsrCod,PendingPassword,DateAndTime)"
" VALUES"
" (%ld,'%s',NOW())",
Gbl.Usrs.Me.UsrDat.UsrCod,Gbl.Usrs.Me.PendingPassword);
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/************************ Check if a password is good ************************/
/*****************************************************************************/
bool Pwd_SlowCheckIfPasswordIsGood (const char *PlainPassword,
const char *EncryptedPassword,
long UsrCod)
{
2015-11-02 18:55:24 +01:00
extern const char *Txt_The_password_is_too_trivial_;
2014-12-01 23:55:08 +01:00
/***** Check if password seems good by making fast checks *****/
if (!Pwd_FastCheckIfPasswordSeemsGood (PlainPassword))
return false;
/***** Check if password is found in user's ID, first name or surnames of anybody *****/
if (Pwd_CheckIfPasswdIsUsrIDorName (PlainPassword)) // PlainPassword is a user's ID, name or surname
{
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_is_too_trivial_);
2014-12-01 23:55:08 +01:00
return false;
}
/***** Check if password is used by too many other users *****/
if (Pwd_GetNumOtherUsrsWhoUseThisPassword (EncryptedPassword,UsrCod) >
Pwd_MAX_OTHER_USERS_USING_THE_SAME_PASSWORD)
{
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_is_too_trivial_);
2014-12-01 23:55:08 +01:00
return false;
}
return true;
}
/*****************************************************************************/
/***** Check if a password is a user's ID, a first name or a surname *********/
/*****************************************************************************/
static bool Pwd_CheckIfPasswdIsUsrIDorName (const char *PlainPassword)
{
bool Found;
/***** Get if password is found in user's ID from database *****/
2018-11-03 20:52:00 +01:00
Found = (DB_QueryCOUNT ("can not check if a password matches a user's ID",
2018-11-04 20:51:38 +01:00
"SELECT COUNT(*) FROM usr_IDs"
" WHERE UsrID='%s'",
2018-11-03 20:52:00 +01:00
PlainPassword) != 0);
2014-12-01 23:55:08 +01:00
/***** Get if password is found in first name or surnames of anybody, from database *****/
if (!Found)
2018-11-03 20:52:00 +01:00
Found = (DB_QueryCOUNT ("can not check if a password matches"
" a first name or a surname",
"SELECT COUNT(*) FROM usr_data"
2018-11-04 20:51:38 +01:00
" WHERE FirstName='%s'"
" OR Surname1='%s'"
" OR Surname2='%s'",
2018-11-03 20:52:00 +01:00
PlainPassword,PlainPassword,PlainPassword) != 0);
2014-12-01 23:55:08 +01:00
return Found;
}
/*****************************************************************************/
/************** Get the number of users who use yet a password ***************/
/*****************************************************************************/
static unsigned Pwd_GetNumOtherUsrsWhoUseThisPassword (const char *EncryptedPassword,long UsrCod)
{
2018-11-03 20:52:00 +01:00
unsigned NumUsrs;
char *SubQuery;
/***** Build subquery *****/
2014-12-01 23:55:08 +01:00
if (UsrCod > 0)
2018-11-03 20:52:00 +01:00
{
if (asprintf (&SubQuery," AND UsrCod<>%ld",UsrCod) < 0)
Lay_NotEnoughMemoryExit ();
}
2014-12-01 23:55:08 +01:00
else
2018-11-03 20:52:00 +01:00
SubQuery = "";
/***** Get number of other users who use a password from database *****/
NumUsrs =
(unsigned) DB_QueryCOUNT ("can not check if a password is trivial",
"SELECT COUNT(*) FROM usr_data"
" WHERE Password='%s'%s",
EncryptedPassword,SubQuery);
/***** Free subquery *****/
if (UsrCod > 0)
free ((void *) SubQuery);
return NumUsrs;
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/********************** Check if a password seems good ***********************/
/*****************************************************************************/
bool Pwd_FastCheckIfPasswordSeemsGood (const char *PlainPassword)
{
2015-11-02 18:55:24 +01:00
extern const char *Txt_The_password_must_be_at_least_X_characters;
extern const char *Txt_The_password_can_not_contain_spaces;
extern const char *Txt_The_password_can_not_consist_only_of_digits;
2014-12-01 23:55:08 +01:00
unsigned LengthPassword = strlen (PlainPassword),i;
bool ItsANumber;
/***** Check length of password *****/
2017-03-07 01:56:41 +01:00
if (LengthPassword < Pwd_MIN_BYTES_PLAIN_PASSWORD) // PlainPassword too short
2014-12-01 23:55:08 +01:00
{
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_must_be_at_least_X_characters,
Pwd_MIN_CHARS_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
return false;
}
/***** Check spaces in password *****/
if (strchr (PlainPassword,(int) ' ') != NULL) // PlainPassword with spaces
{
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_can_not_contain_spaces);
2014-12-01 23:55:08 +01:00
return false;
}
/***** Check if password is a number *****/
for (i = 0, ItsANumber = true;
i < LengthPassword && ItsANumber;
i++)
if (PlainPassword[i] < '0' || PlainPassword[i] > '9')
ItsANumber = false;
if (ItsANumber)
{
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,Pwd_PASSWORD_SECTION_ID,
Txt_The_password_can_not_consist_only_of_digits);
2014-12-01 23:55:08 +01:00
return false;
}
return true;
}
/*****************************************************************************/
/********************** Show form for changing my password *******************/
/*****************************************************************************/
2018-10-09 20:32:01 +02:00
void Pwd_ShowFormChgMyPwd (void)
2014-12-01 23:55:08 +01:00
{
2016-11-13 11:46:04 +01:00
extern const char *Hlp_PROFILE_Password;
2019-02-22 21:47:50 +01:00
extern const char *The_ClassFormInBox[The_NUM_THEMES];
2014-12-01 23:55:08 +01:00
extern const char *Txt_Before_going_to_any_other_option_you_must_create_your_password;
extern const char *Txt_Your_password_is_not_secure_enough;
extern const char *Txt_Your_password_must_be_at_least_X_characters_and_can_not_contain_spaces_;
extern const char *Txt_Password;
extern const char *Txt_Current_password;
2015-04-03 01:19:45 +02:00
extern const char *Txt_Change_password;
extern const char *Txt_Set_password;
2018-10-15 14:07:12 +02:00
char StrRecordWidth[10 + 1];
2014-12-01 23:55:08 +01:00
bool IHaveAPasswordInDB = (bool) Gbl.Usrs.Me.UsrDat.Password[0];
2018-10-09 20:32:01 +02:00
/***** Start section *****/
Lay_StartSection (Pwd_PASSWORD_SECTION_ID);
2014-12-01 23:55:08 +01:00
2018-10-15 14:07:12 +02:00
/***** Start form *****/
2019-02-15 21:09:18 +01:00
Frm_StartFormAnchor (ActChgMyPwd,Pwd_PASSWORD_SECTION_ID);
2014-12-01 23:55:08 +01:00
2018-10-15 14:07:12 +02:00
/***** Start box *****/
2018-10-18 02:02:32 +02:00
snprintf (StrRecordWidth,sizeof (StrRecordWidth),
"%upx",
Rec_RECORD_WIDTH);
2018-10-15 14:07:12 +02:00
Box_StartBox (StrRecordWidth,Txt_Password,NULL,
Hlp_PROFILE_Password,Box_NOT_CLOSABLE);
2019-03-09 20:12:44 +01:00
/***** Show possible alerts *****/
Ale_ShowAlerts (Pwd_PASSWORD_SECTION_ID);
2018-10-15 14:07:12 +02:00
/***** Help message *****/
if (!IHaveAPasswordInDB) // If I don't have a password in database...
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_WARNING,Txt_Before_going_to_any_other_option_you_must_create_your_password);
2018-10-15 14:07:12 +02:00
else if (Gbl.Usrs.Me.LoginPlainPassword[0])
2018-10-09 20:32:01 +02:00
{
2018-10-15 14:07:12 +02:00
if (!Pwd_FastCheckIfPasswordSeemsGood (Gbl.Usrs.Me.LoginPlainPassword))
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_WARNING,Txt_Your_password_is_not_secure_enough);
2018-10-15 14:07:12 +02:00
}
/***** Start table *****/
Tbl_StartTableWide (2);
2014-12-01 23:55:08 +01:00
2018-10-15 14:07:12 +02:00
/***** Current password *****/
if (IHaveAPasswordInDB) // If I have a password in database...
2014-12-01 23:55:08 +01:00
fprintf (Gbl.F.Out,"<tr>"
2018-10-15 14:07:12 +02:00
"<td class=\"REC_C1_BOT RIGHT_MIDDLE\">"
"<label for=\"UsrPwd\" class=\"%s\">%s:</label>"
"</td>"
"<td class=\"REC_C2_BOT LEFT_MIDDLE\">"
"<input type=\"password\""
" id=\"UsrPwd\" name=\"UsrPwd\""
" size=\"18\" maxlength=\"%u\""
" autocomplete=\"off\" required=\"required\" />"
"</td>"
"</tr>",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2018-10-15 14:07:12 +02:00
Txt_Current_password,
Pwd_MAX_CHARS_PLAIN_PASSWORD);
/***** Help message *****/
fprintf (Gbl.F.Out,"<tr>"
"<td colspan=\"2\">");
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_INFO,Txt_Your_password_must_be_at_least_X_characters_and_can_not_contain_spaces_,
Pwd_MIN_CHARS_PLAIN_PASSWORD);
2018-10-15 14:07:12 +02:00
fprintf (Gbl.F.Out,"</td>"
"</tr>");
/***** New password *****/
Pwd_PutFormToGetNewPasswordTwice ();
/***** End table, send button and end box *****/
Box_EndBoxTableWithButton (Btn_CONFIRM_BUTTON,
IHaveAPasswordInDB ? Txt_Change_password :
Txt_Set_password);
/***** End form *****/
2018-11-09 20:47:39 +01:00
Frm_EndForm ();
2015-04-11 23:46:21 +02:00
2018-10-09 20:32:01 +02:00
/***** End section *****/
Lay_EndSection ();
2014-12-01 23:55:08 +01:00
}
2015-11-20 21:11:33 +01:00
/*****************************************************************************/
/**************** Put form to request the new password once ******************/
/*****************************************************************************/
void Pwd_PutFormToGetNewPasswordOnce (void)
{
2019-02-22 21:47:50 +01:00
extern const char *The_ClassFormInBox[The_NUM_THEMES];
2015-11-20 21:11:33 +01:00
extern const char *Txt_Password;
extern const char *Txt_HELP_password;
2019-02-15 21:09:18 +01:00
/***** Start form element ****/
2015-11-20 21:11:33 +01:00
fprintf (Gbl.F.Out,"<tr>"
2016-12-20 20:48:53 +01:00
"<td class=\"RIGHT_MIDDLE\">"
"<label for=\"Passwd\" class=\"%s\">%s:</label>"
2015-11-20 21:11:33 +01:00
"</td>"
"<td class=\"LEFT_MIDDLE\">"
2016-12-20 20:48:53 +01:00
"<input type=\"password\" id=\"Passwd\" name=\"Paswd\""
2019-02-15 21:09:18 +01:00
" size=\"18\" maxlength=\"%u\" placeholder=\"",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2015-11-20 21:11:33 +01:00
Txt_Password,
2019-02-15 21:09:18 +01:00
Pwd_MAX_CHARS_PLAIN_PASSWORD);
/***** Placeholder *****/
fprintf (Gbl.F.Out,Txt_HELP_password,
Pwd_MIN_CHARS_PLAIN_PASSWORD);
/***** End form element ****/
fprintf (Gbl.F.Out,"\""
" required=\"required\" />"
"</td>"
"</tr>");
2015-11-20 21:11:33 +01:00
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/**************** Put form to request the new password twice *****************/
/*****************************************************************************/
void Pwd_PutFormToGetNewPasswordTwice (void)
{
2019-02-22 21:47:50 +01:00
extern const char *The_ClassFormInBox[The_NUM_THEMES];
2014-12-01 23:55:08 +01:00
extern const char *Txt_New_password;
2015-03-27 20:52:14 +01:00
extern const char *Txt_HELP_password;
2014-12-01 23:55:08 +01:00
extern const char *Txt_Retype_new_password;
2019-02-15 21:09:18 +01:00
/***** 1st password *****/
/* Start form element */
2014-12-01 23:55:08 +01:00
fprintf (Gbl.F.Out,"<tr>"
2018-10-15 14:07:12 +02:00
"<td class=\"REC_C1_BOT RIGHT_MIDDLE\">"
2016-12-24 14:33:27 +01:00
"<label for=\"Paswd1\" class=\"%s\">%s:</label>"
2014-12-23 22:47:09 +01:00
"</td>"
2018-10-15 14:07:12 +02:00
"<td class=\"REC_C2_BOT LEFT_MIDDLE\">"
2016-12-24 14:33:27 +01:00
"<input type=\"password\" id=\"Paswd1\" name=\"Paswd1\""
" size=\"18\" maxlength=\"%u\""
2019-02-15 21:09:18 +01:00
" placeholder=\"",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2019-02-15 21:09:18 +01:00
Txt_New_password,
Pwd_MAX_CHARS_PLAIN_PASSWORD);
/* Placeholder */
fprintf (Gbl.F.Out,Txt_HELP_password,
Pwd_MIN_CHARS_PLAIN_PASSWORD);
/* End form element */
fprintf (Gbl.F.Out,"\" required=\"required\" />"
2014-12-01 23:55:08 +01:00
"</td>"
2019-02-15 21:09:18 +01:00
"</tr>");
/***** 2nd password *****/
/* Start form element */
fprintf (Gbl.F.Out,"<tr>"
2018-10-15 14:07:12 +02:00
"<td class=\"REC_C1_BOT RIGHT_MIDDLE\">"
2016-12-24 14:33:27 +01:00
"<label for=\"Paswd2\" class=\"%s\">%s:</label>"
2014-12-23 22:47:09 +01:00
"</td>"
2018-10-15 14:07:12 +02:00
"<td class=\"REC_C2_BOT LEFT_MIDDLE\">"
2016-12-24 14:33:27 +01:00
"<input type=\"password\" id=\"Paswd2\" name=\"Paswd2\""
" size=\"18\" maxlength=\"%u\""
2019-02-15 21:09:18 +01:00
" placeholder=\"",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2015-11-20 21:11:33 +01:00
Txt_Retype_new_password,
2019-02-15 21:09:18 +01:00
Pwd_MAX_CHARS_PLAIN_PASSWORD);
/* Placeholder */
fprintf (Gbl.F.Out,Txt_HELP_password,
Pwd_MIN_CHARS_PLAIN_PASSWORD);
/* End form element */
fprintf (Gbl.F.Out,"\" required=\"required\" />"
"</td>"
"</tr>");
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/********** Show form to the change of password of another user **************/
/*****************************************************************************/
2018-10-16 01:36:13 +02:00
void Pwd_ShowFormChgOtherUsrPwd (void)
2014-12-01 23:55:08 +01:00
{
2015-09-18 18:08:43 +02:00
extern const char *Txt_Password;
extern const char *Txt_Change_password;
2017-05-22 14:52:11 +02:00
Act_Action_t NextAction;
2014-12-01 23:55:08 +01:00
2018-10-16 08:48:51 +02:00
/***** Start box *****/
Box_StartBox (NULL,Txt_Password,NULL,
NULL,Box_NOT_CLOSABLE);
2018-10-15 14:07:12 +02:00
2018-10-16 08:48:51 +02:00
/***** Start section *****/
Lay_StartSection (Pwd_PASSWORD_SECTION_ID);
2018-10-15 14:07:12 +02:00
2019-03-09 20:12:44 +01:00
/***** Show possible alerts *****/
Ale_ShowAlerts (Pwd_PASSWORD_SECTION_ID);
2018-10-15 14:07:12 +02:00
2018-10-16 08:48:51 +02:00
/***** Form to change password *****/
/* Start form */
switch (Gbl.Usrs.Other.UsrDat.Roles.InCurrentCrs.Role)
{
case Rol_STD:
NextAction = ActChgPwdStd;
break;
case Rol_NET:
case Rol_TCH:
NextAction = ActChgPwdTch;
break;
default: // Guest, user or admin
NextAction = ActChgPwdOth;
break;
}
2018-11-09 20:47:39 +01:00
Frm_StartFormAnchor (NextAction,Pwd_PASSWORD_SECTION_ID);
2018-10-16 08:48:51 +02:00
Usr_PutParamOtherUsrCodEncrypted ();
2014-12-01 23:55:08 +01:00
2018-10-16 08:48:51 +02:00
/* New password */
Tbl_StartTableWide (2);
Pwd_PutFormToGetNewPasswordTwice ();
Tbl_EndTable ();
2014-12-31 02:46:20 +01:00
2018-10-16 08:48:51 +02:00
/* End form */
Btn_PutConfirmButton (Txt_Change_password);
2018-11-09 20:47:39 +01:00
Frm_EndForm ();
2014-12-01 23:55:08 +01:00
2018-10-16 08:48:51 +02:00
/***** End box *****/
Box_EndBox ();
2015-09-18 02:15:35 +02:00
2018-10-16 08:48:51 +02:00
/***** End section *****/
Lay_EndSection ();
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/***************** Ask for confirmation on dangerous actions *****************/
/*****************************************************************************/
void Pwd_AskForConfirmationOnDangerousAction (void)
{
2019-02-22 21:47:50 +01:00
extern const char *The_ClassFormInBox[The_NUM_THEMES];
2015-11-20 14:44:54 +01:00
extern const char *Txt_I_understand_that_this_action_can_not_be_undone;
2014-12-01 23:55:08 +01:00
extern const char *Txt_For_security_enter_your_password;
2015-09-28 18:28:29 +02:00
fprintf (Gbl.F.Out,"<div class=\"CENTER_MIDDLE\" style=\"margin:12px;\">"
2016-12-21 01:51:30 +01:00
"<label class=\"%s\">"
2014-12-01 23:55:08 +01:00
"<input type=\"checkbox\" name=\"Consent\" value=\"Y\" />"
2016-12-21 01:51:30 +01:00
"%s"
"</label>"
"<br />"
"<label class=\"%s\">"
"%s:&nbsp;"
2014-12-01 23:55:08 +01:00
"<input type=\"password\" name=\"OthUsrPwd\""
2016-12-26 16:58:49 +01:00
" size=\"16\" maxlength=\"%u\""
" autocomplete=\"off\" required=\"required\" />"
2016-12-21 01:51:30 +01:00
"</label>"
2014-12-01 23:55:08 +01:00
"</div>",
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2015-11-20 14:44:54 +01:00
Txt_I_understand_that_this_action_can_not_be_undone,
2019-02-22 21:47:50 +01:00
The_ClassFormInBox[Gbl.Prefs.Theme],
2014-12-01 23:55:08 +01:00
Txt_For_security_enter_your_password,
2017-03-07 01:56:41 +01:00
Pwd_MAX_CHARS_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/******************** Get confirmation on dangerous actions ******************/
/*****************************************************************************/
// Returns true if consent have been checked and my password is correct
bool Pwd_GetConfirmationOnDangerousAction (void)
{
extern const char *Txt_You_have_not_confirmed_the_action;
extern const char *Txt_You_have_not_entered_your_password_correctly;
2017-03-07 01:56:41 +01:00
char PlainPassword[Pwd_MAX_BYTES_PLAIN_PASSWORD + 1];
2017-03-13 14:22:36 +01:00
char EncryptedPassword[Pwd_BYTES_ENCRYPTED_PASSWORD + 1];
2014-12-01 23:55:08 +01:00
/***** Get if consent has been done *****/
2017-01-30 19:12:35 +01:00
if (!Par_GetParToBool ("Consent"))
2014-12-01 23:55:08 +01:00
{
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_WARNING,Txt_You_have_not_confirmed_the_action);
2014-12-01 23:55:08 +01:00
return false;
}
/***** Get my password *****/
/* Get plain password from form */
2017-03-07 01:56:41 +01:00
Par_GetParToText ("OthUsrPwd",PlainPassword,Pwd_MAX_BYTES_PLAIN_PASSWORD);
2014-12-01 23:55:08 +01:00
/* Encrypt password */
Cry_EncryptSHA512Base64 (PlainPassword,EncryptedPassword);
/* Compare passwords */
if (strcmp (Gbl.Usrs.Me.LoginEncryptedPassword,EncryptedPassword))
{
2019-02-16 18:11:52 +01:00
Ale_ShowAlert (Ale_WARNING,Txt_You_have_not_entered_your_password_correctly);
2014-12-01 23:55:08 +01:00
return false;
}
return true;
}