swad-core/swad_mail.c

1870 lines
69 KiB
C

// swad_mail.c: all the stuff related to email
/*
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.
Copyright (C) 1999-2017 Antonio Cañas Vargas
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 ***********************************/
/*****************************************************************************/
#include <linux/stddef.h> // For NULL
#include <stdlib.h> // For calloc
#include <string.h> // For string functions
#include <sys/wait.h> // For the macro WEXITSTATUS
#include <unistd.h> // For access, lstat, getpid, chdir, symlink, unlink
#include "swad_account.h"
#include "swad_database.h"
#include "swad_global.h"
#include "swad_mail.h"
#include "swad_parameter.h"
#include "swad_QR.h"
#include "swad_tab.h"
#include "swad_text.h"
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/***************************** Private constants *****************************/
/*****************************************************************************/
#define Mai_LENGTH_EMAIL_CONFIRM_KEY Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64
/*****************************************************************************/
/******************************* Private types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private variables *****************************/
/*****************************************************************************/
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static void Mai_GetParamMaiOrder (void);
static void Mai_PutIconToEditMailDomains (void);
static void Mai_GetListMailDomainsAllowedForNotif (void);
static void Mai_GetMailDomain (const char *Email,char MailDomain[Cns_MAX_BYTES_EMAIL_ADDRESS + 1]);
static bool Mai_CheckIfMailDomainIsAllowedForNotif (const char MailDomain[Cns_MAX_BYTES_EMAIL_ADDRESS + 1]);
static void Mai_ListMailDomainsForEdition (void);
static void Mai_PutParamMaiCod (long MaiCod);
static void Mai_RenameMailDomain (Cns_ShrtOrFullName_t ShrtOrFullName);
static bool Mai_CheckIfMailDomainNameExists (const char *FieldName,const char *Name,long MaiCod);
static void Mai_UpdateMailDomainNameDB (long MaiCod,const char *FieldName,const char *NewMaiName);
static void Mai_PutFormToCreateMailDomain (void);
static void Mai_PutHeadMailDomains (void);
static void Mai_CreateMailDomain (struct Mail *Mai);
static void Mai_RemoveEmail (struct UsrData *UsrDat);
static void Mai_RemoveEmailFromDB (long UsrCod,const char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1]);
static void Mai_NewUsrEmail (struct UsrData *UsrDat,bool ItsMe);
static void Mai_InsertMailKey (const char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1],
const char MailKey[Mai_LENGTH_EMAIL_CONFIRM_KEY + 1]);
/*****************************************************************************/
/************************* List all the mail domains *************************/
/*****************************************************************************/
void Mai_SeeMailDomains (void)
{
extern const char *Hlp_MESSAGES_Domains;
extern const char *Txt_Email_domains_allowed_for_notifications;
extern const char *Txt_EMAIL_DOMAIN_HELP_ORDER[3];
extern const char *Txt_EMAIL_DOMAIN_ORDER[3];
Mai_DomainsOrder_t Order;
unsigned NumMai;
/***** Get parameter with the type of order in the list of mail domains *****/
Mai_GetParamMaiOrder ();
/***** Get list of mail domains *****/
Mai_GetListMailDomainsAllowedForNotif ();
/***** Table head *****/
Lay_StartRoundFrameTable (NULL,Txt_Email_domains_allowed_for_notifications,
Gbl.Usrs.Me.LoggedRole == Rol_SYS_ADM ? Mai_PutIconToEditMailDomains :
NULL,
Hlp_MESSAGES_Domains,2);
fprintf (Gbl.F.Out,"<tr>");
for (Order = Mai_ORDER_BY_DOMAIN;
Order <= Mai_ORDER_BY_USERS;
Order++)
{
fprintf (Gbl.F.Out,"<th class=\"LEFT_MIDDLE\">");
Act_FormStart (ActSeeMai);
Par_PutHiddenParamUnsigned ("Order",(unsigned) Order);
Act_LinkFormSubmit (Txt_EMAIL_DOMAIN_HELP_ORDER[Order],"TIT_TBL",NULL);
if (Order == Gbl.Mails.SelectedOrder)
fprintf (Gbl.F.Out,"<u>");
fprintf (Gbl.F.Out,"%s",Txt_EMAIL_DOMAIN_ORDER[Order]);
if (Order == Gbl.Mails.SelectedOrder)
fprintf (Gbl.F.Out,"</u>");
fprintf (Gbl.F.Out,"</a>");
Act_FormEnd ();
fprintf (Gbl.F.Out,"</th>");
}
fprintf (Gbl.F.Out,"</tr>");
/***** Write all the mail domains *****/
for (NumMai = 0;
NumMai < Gbl.Mails.Num;
NumMai++)
/* Write data of this mail domain */
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"DAT LEFT_TOP\">"
"%s"
"</td>"
"<td class=\"DAT LEFT_TOP\">"
"%s"
"</td>"
"<td class=\"DAT RIGHT_TOP\">"
"%u"
"</td>"
"</tr>",
Gbl.Mails.Lst[NumMai].Domain,
Gbl.Mails.Lst[NumMai].Info,
Gbl.Mails.Lst[NumMai].NumUsrs);
/***** End table *****/
Lay_EndRoundFrameTable ();
/***** Free list of mail domains *****/
Mai_FreeListMailDomains ();
}
/*****************************************************************************/
/******* Get parameter with the type or order in list of mail domains ********/
/*****************************************************************************/
static void Mai_GetParamMaiOrder (void)
{
Gbl.Mails.SelectedOrder = (Mai_DomainsOrder_t)
Par_GetParToUnsignedLong ("Order",
0,
Mai_NUM_ORDERS - 1,
(unsigned long) Mai_ORDER_DEFAULT);
}
/*****************************************************************************/
/************************ Put icon to edit mail domains **********************/
/*****************************************************************************/
static void Mai_PutIconToEditMailDomains (void)
{
Lay_PutContextualIconToEdit (ActEdiMai,NULL);
}
/*****************************************************************************/
/*********************** Put forms to edit mail domains **********************/
/*****************************************************************************/
void Mai_EditMailDomains (void)
{
/***** Get list of mail domains *****/
Mai_GetListMailDomainsAllowedForNotif ();
/***** Put a form to create a new mail *****/
Mai_PutFormToCreateMailDomain ();
/***** Forms to edit current mail domains *****/
if (Gbl.Mails.Num)
Mai_ListMailDomainsForEdition ();
/***** Free list of mail domains *****/
Mai_FreeListMailDomains ();
}
/*****************************************************************************/
/************************* List all the mail domains *************************/
/*****************************************************************************/
static void Mai_GetListMailDomainsAllowedForNotif (void)
{
char OrderBySubQuery[256];
char Query[1024];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
unsigned NumMai;
struct Mail *Mai;
// Query uses temporary tables for speed
// Query uses two identical temporary tables...
// ...because a unique temporary table can not be used twice in the same query
/***** Create temporary table with all the mail domains present in users' emails table *****/
sprintf (Query,"DROP TEMPORARY TABLE IF EXISTS T1,T2");
if (mysql_query (&Gbl.mysql,Query))
DB_ExitOnMySQLError ("can not remove temporary tables");
sprintf (Query,"CREATE TEMPORARY TABLE T1 ENGINE=MEMORY"
" SELECT SUBSTRING_INDEX(E_mail,'@',-1) AS Domain,COUNT(*) as N"
" FROM usr_emails GROUP BY Domain");
if (mysql_query (&Gbl.mysql,Query))
DB_ExitOnMySQLError ("can not create temporary table");
sprintf (Query,"CREATE TEMPORARY TABLE T2 ENGINE=MEMORY SELECT * FROM T1");
if (mysql_query (&Gbl.mysql,Query))
DB_ExitOnMySQLError ("can not create temporary table");
/***** Get mail domains from database *****/
switch (Gbl.Mails.SelectedOrder)
{
case Mai_ORDER_BY_DOMAIN:
sprintf (OrderBySubQuery,"Domain,Info,N DESC");
break;
case Mai_ORDER_BY_INFO:
sprintf (OrderBySubQuery,"Info,Domain,N DESC");
break;
case Mai_ORDER_BY_USERS:
sprintf (OrderBySubQuery,"N DESC,Info,Domain");
break;
}
sprintf (Query,"(SELECT mail_domains.MaiCod,mail_domains.Domain AS Domain,mail_domains.Info AS Info,T1.N AS N"
" FROM mail_domains,T1"
" WHERE mail_domains.Domain=T1.Domain COLLATE 'latin1_bin')"
" UNION "
"(SELECT MaiCod,Domain,Info,0 AS N"
" FROM mail_domains"
" WHERE Domain NOT IN (SELECT Domain COLLATE 'latin1_bin' FROM T2))"
" ORDER BY %s", // COLLATE necessary to avoid error in comparisons
OrderBySubQuery);
if ((NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get mail domains"))) // Mail domains found...
{
Gbl.Mails.Num = (unsigned) NumRows;
/***** Create list with places *****/
if ((Gbl.Mails.Lst = (struct Mail *) calloc (NumRows,sizeof (struct Mail))) == NULL)
Lay_ShowErrorAndExit ("Not enough memory to store mail domains.");
/***** Get the mail domains *****/
for (NumMai = 0;
NumMai < Gbl.Mails.Num;
NumMai++)
{
Mai = &(Gbl.Mails.Lst[NumMai]);
/* Get next mail */
row = mysql_fetch_row (mysql_res);
/* Get mail code (row[0]) */
if ((Mai->MaiCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
Lay_ShowErrorAndExit ("Wrong code of mail domain.");
/* Get the mail domain (row[1]) */
Str_Copy (Mai->Domain,row[1],
Cns_MAX_BYTES_EMAIL_ADDRESS);
/* Get the mail domain info (row[2]) */
Str_Copy (Mai->Info,row[2],
Mai_MAX_BYTES_MAIL_INFO);
/* Get number of users (row[3]) */
if (sscanf (row[3],"%u",&(Mai->NumUsrs)) != 1)
Mai->NumUsrs = 0;
}
}
else
Gbl.Mails.Num = 0;
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
/***** Drop temporary table *****/
sprintf (Query,"DROP TEMPORARY TABLE IF EXISTS T1,T2");
if (mysql_query (&Gbl.mysql,Query))
DB_ExitOnMySQLError ("can not remove temporary tables");
}
/*****************************************************************************/
/************ Check if user can receive notifications via email **************/
/*****************************************************************************/
bool Mai_CheckIfUsrCanReceiveEmailNotif (const struct UsrData *UsrDat)
{
char MailDomain[Cns_MAX_BYTES_EMAIL_ADDRESS + 1];
/***** Check #1: is my email address confirmed? *****/
if (!UsrDat->EmailConfirmed)
return false;
/***** Check #2: if my mail domain allowed? *****/
Mai_GetMailDomain (UsrDat->Email,MailDomain);
return Mai_CheckIfMailDomainIsAllowedForNotif (MailDomain);
}
/*****************************************************************************/
/********************** Get mailbox from email address ***********************/
/*****************************************************************************/
static void Mai_GetMailDomain (const char *Email,char MailDomain[Cns_MAX_BYTES_EMAIL_ADDRESS + 1])
{
const char *Ptr;
MailDomain[0] = '\0'; // Return empty mailbox on error
if ((Ptr = strchr (Email,(int) '@'))) // Find first '@' in address
if (Ptr != Email) // '@' is not the first character in Email
{
Ptr++; // Skip '@'
if (strchr (Ptr,(int) '@') == NULL) // No more '@' found
Str_Copy (MailDomain,Ptr,
Cns_MAX_BYTES_EMAIL_ADDRESS);
}
}
/*****************************************************************************/
/************ Check if a mail domain is allowed for notifications ************/
/*****************************************************************************/
static bool Mai_CheckIfMailDomainIsAllowedForNotif (const char MailDomain[Cns_MAX_BYTES_EMAIL_ADDRESS + 1])
{
char Query[128 + Cns_MAX_BYTES_EMAIL_ADDRESS];
/***** Get number of mail_domains with a name from database *****/
sprintf (Query,"SELECT COUNT(*) FROM mail_domains WHERE Domain='%s'",
MailDomain);
return (DB_QueryCOUNT (Query,"can not check if a mail domain is allowed for notifications") != 0);
}
/*****************************************************************************/
/***************** Show warning about notifications via email ****************/
/*****************************************************************************/
void Mai_WriteWarningEmailNotifications (void)
{
extern struct Act_Actions Act_Actions[Act_NUM_ACTIONS];
extern const char *Txt_You_can_only_receive_email_notifications_if_;
extern const char *Txt_TABS_TXT[Tab_NUM_TABS];
extern const char *Txt_MENU_TITLE[Tab_NUM_TABS][Act_MAX_OPTIONS_IN_MENU_PER_TAB];
extern const char *Txt_Domains;
Act_Action_t SuperActionMyAccount = Act_Actions[ActFrmMyAcc].SuperAction;
Act_Action_t SuperActionMailDomains = Act_Actions[ActSeeMai ].SuperAction;
Tab_Tab_t TabMyAccount = Act_Actions[SuperActionMyAccount ].Tab;
Tab_Tab_t TabMailDomains = Act_Actions[SuperActionMailDomains].Tab;
sprintf (Gbl.Alert.Txt,Txt_You_can_only_receive_email_notifications_if_,
Txt_TABS_TXT[TabMyAccount ],
Txt_MENU_TITLE[TabMyAccount ][Act_Actions[SuperActionMyAccount ].IndexInMenu],
Txt_TABS_TXT[TabMailDomains],
Txt_MENU_TITLE[TabMailDomains][Act_Actions[SuperActionMailDomains].IndexInMenu],
Txt_Domains);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
/*****************************************************************************/
/**************************** Get mail domain data ***************************/
/*****************************************************************************/
void Mai_GetDataOfMailDomainByCod (struct Mail *Mai)
{
char Query[128];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
/***** Clear data *****/
Mai->Domain[0] = Mai->Info[0] = '\0';
/***** Check if mail code is correct *****/
if (Mai->MaiCod > 0)
{
/***** Get data of a mail domain from database *****/
sprintf (Query,"SELECT Domain,Info FROM mail_domains WHERE MaiCod=%ld",
Mai->MaiCod);
NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get data of a mail domain");
if (NumRows) // Mail found...
{
/* Get row */
row = mysql_fetch_row (mysql_res);
/* Get the short name of the mail (row[0]) */
Str_Copy (Mai->Domain,row[0],
Cns_MAX_BYTES_EMAIL_ADDRESS);
/* Get the full name of the mail (row[1]) */
Str_Copy (Mai->Info,row[1],
Mai_MAX_BYTES_MAIL_INFO);
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
}
/*****************************************************************************/
/************************** Free list of mail domains ************************/
/*****************************************************************************/
void Mai_FreeListMailDomains (void)
{
if (Gbl.Mails.Lst)
{
/***** Free memory used by the list of mail domains *****/
free ((void *) Gbl.Mails.Lst);
Gbl.Mails.Lst = NULL;
Gbl.Mails.Num = 0;
}
}
/*****************************************************************************/
/************************ List all the mail domains **************************/
/*****************************************************************************/
static void Mai_ListMailDomainsForEdition (void)
{
extern const char *Hlp_MESSAGES_Domains_edit;
extern const char *Txt_Email_domains_allowed_for_notifications;
unsigned NumMai;
struct Mail *Mai;
Lay_StartRoundFrameTable (NULL,Txt_Email_domains_allowed_for_notifications,
NULL,Hlp_MESSAGES_Domains_edit,2);
/***** Table head *****/
Mai_PutHeadMailDomains ();
/***** Write all the mail domains *****/
for (NumMai = 0;
NumMai < Gbl.Mails.Num;
NumMai++)
{
Mai = &Gbl.Mails.Lst[NumMai];
/* Put icon to remove mail */
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"BM\">");
Act_FormStart (ActRemMai);
Mai_PutParamMaiCod (Mai->MaiCod);
Lay_PutIconRemove ();
Act_FormEnd ();
fprintf (Gbl.F.Out,"</td>");
/* Mail code */
fprintf (Gbl.F.Out,"<td class=\"DAT RIGHT_MIDDLE\">"
"%ld"
"</td>",
Mai->MaiCod);
/* Mail domain */
fprintf (Gbl.F.Out,"<td class=\"CENTER_MIDDLE\">");
Act_FormStart (ActRenMaiSho);
Mai_PutParamMaiCod (Mai->MaiCod);
fprintf (Gbl.F.Out,"<input type=\"text\" name=\"Domain\""
" size=\"15\" maxlength=\"%u\" value=\"%s\""
" onchange=\"document.getElementById('%s').submit();\" />",
Cns_MAX_CHARS_EMAIL_ADDRESS,Mai->Domain,
Gbl.Form.Id);
Act_FormEnd ();
fprintf (Gbl.F.Out,"</td>");
/* Mail domain info */
fprintf (Gbl.F.Out,"<td class=\"CENTER_MIDDLE\">");
Act_FormStart (ActRenMaiFul);
Mai_PutParamMaiCod (Mai->MaiCod);
fprintf (Gbl.F.Out,"<input type=\"text\" name=\"Info\""
" size=\"40\" maxlength=\"%u\" value=\"%s\""
" onchange=\"document.getElementById('%s').submit();\" />",
Mai_MAX_CHARS_MAIL_INFO,Mai->Info,
Gbl.Form.Id);
Act_FormEnd ();
fprintf (Gbl.F.Out,"</td>");
/* Number of users */
fprintf (Gbl.F.Out,"<td class=\"DAT RIGHT_MIDDLE\">"
"%u"
"</td>"
"</tr>",
Mai->NumUsrs);
}
Lay_EndRoundFrameTable ();
}
/*****************************************************************************/
/******************** Write parameter with code of mail **********************/
/*****************************************************************************/
static void Mai_PutParamMaiCod (long MaiCod)
{
Par_PutHiddenParamLong ("MaiCod",MaiCod);
}
/*****************************************************************************/
/*********************** Get parameter with code of mail *********************/
/*****************************************************************************/
long Mai_GetParamMaiCod (void)
{
/***** Get code of mail *****/
return Par_GetParToLong ("MaiCod");
}
/*****************************************************************************/
/******************************* Remove a mail *******************************/
/*****************************************************************************/
void Mai_RemoveMailDomain (void)
{
extern const char *Txt_Email_domain_X_removed;
char Query[128];
struct Mail Mai;
/***** Get mail code *****/
if ((Mai.MaiCod = Mai_GetParamMaiCod ()) == -1L)
Lay_ShowErrorAndExit ("Code of mail domain is missing.");
/***** Get data of the mail from database *****/
Mai_GetDataOfMailDomainByCod (&Mai);
/***** Remove mail *****/
sprintf (Query,"DELETE FROM mail_domains WHERE MaiCod=%ld",
Mai.MaiCod);
DB_QueryDELETE (Query,"can not remove a mail domain");
/***** Write message to show the change made *****/
sprintf (Gbl.Alert.Txt,Txt_Email_domain_X_removed,
Mai.Domain);
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
/***** Show the form again *****/
Mai_EditMailDomains ();
}
/*****************************************************************************/
/********************* Change the short name of a mail ***********************/
/*****************************************************************************/
void Mai_RenameMailDomainShort (void)
{
Mai_RenameMailDomain (Cns_SHRT_NAME);
}
/*****************************************************************************/
/********************* Change the full name of a mail ************************/
/*****************************************************************************/
void Mai_RenameMailDomainFull (void)
{
Mai_RenameMailDomain (Cns_FULL_NAME);
}
/*****************************************************************************/
/************************* Change the name of a mail *************************/
/*****************************************************************************/
static void Mai_RenameMailDomain (Cns_ShrtOrFullName_t ShrtOrFullName)
{
extern const char *Txt_You_can_not_leave_the_name_of_the_email_domain_X_empty;
extern const char *Txt_The_email_domain_X_already_exists;
extern const char *Txt_The_email_domain_X_has_been_renamed_as_Y;
extern const char *Txt_The_email_domain_X_has_not_changed;
struct Mail *Mai;
const char *ParamName = NULL; // Initialized to avoid warning
const char *FieldName = NULL; // Initialized to avoid warning
unsigned MaxBytes = 0; // Initialized to avoid warning
char *CurrentMaiName = NULL; // Initialized to avoid warning
char NewMaiName[Mai_MAX_BYTES_MAIL_INFO + 1];
Mai = &Gbl.Mails.EditingMai;
switch (ShrtOrFullName)
{
case Cns_SHRT_NAME:
ParamName = "Domain";
FieldName = "Domain";
MaxBytes = Cns_MAX_BYTES_EMAIL_ADDRESS;
CurrentMaiName = Mai->Domain;
break;
case Cns_FULL_NAME:
ParamName = "Info";
FieldName = "Info";
MaxBytes = Mai_MAX_BYTES_MAIL_INFO;
CurrentMaiName = Mai->Info;
break;
}
/***** Get parameters from form *****/
/* Get the code of the mail */
if ((Mai->MaiCod = Mai_GetParamMaiCod ()) == -1L)
Lay_ShowErrorAndExit ("Code of mail domain is missing.");
/* Get the new name for the mail */
Par_GetParToText (ParamName,NewMaiName,MaxBytes);
/***** Get from the database the old names of the mail *****/
Mai_GetDataOfMailDomainByCod (Mai);
/***** Check if new name is empty *****/
if (!NewMaiName[0])
{
sprintf (Gbl.Alert.Txt,Txt_You_can_not_leave_the_name_of_the_email_domain_X_empty,
CurrentMaiName);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
else
{
/***** Check if old and new names are the same (this happens when user press enter with no changes in the form) *****/
if (strcmp (CurrentMaiName,NewMaiName)) // Different names
{
/***** If mail was in database... *****/
if (Mai_CheckIfMailDomainNameExists (ParamName,NewMaiName,Mai->MaiCod))
{
sprintf (Gbl.Alert.Txt,Txt_The_email_domain_X_already_exists,
NewMaiName);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
else
{
/* Update the table changing old name by new name */
Mai_UpdateMailDomainNameDB (Mai->MaiCod,FieldName,NewMaiName);
/* Write message to show the change made */
sprintf (Gbl.Alert.Txt,Txt_The_email_domain_X_has_been_renamed_as_Y,
CurrentMaiName,NewMaiName);
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
}
}
else // The same name
{
sprintf (Gbl.Alert.Txt,Txt_The_email_domain_X_has_not_changed,
CurrentMaiName);
Ale_ShowAlert (Ale_INFO,Gbl.Alert.Txt);
}
}
/***** Show the form again *****/
Str_Copy (CurrentMaiName,NewMaiName,
MaxBytes);
Mai_EditMailDomains ();
}
/*****************************************************************************/
/********************** Check if the name of mail exists *********************/
/*****************************************************************************/
static bool Mai_CheckIfMailDomainNameExists (const char *FieldName,const char *Name,long MaiCod)
{
char Query[256 + Mai_MAX_BYTES_MAIL_INFO];
/***** Get number of mail_domains with a name from database *****/
sprintf (Query,"SELECT COUNT(*) FROM mail_domains"
" WHERE %s='%s' AND MaiCod<>%ld",
FieldName,Name,MaiCod);
return (DB_QueryCOUNT (Query,"can not check if the name of a mail domain already existed") != 0);
}
/*****************************************************************************/
/****************** Update name in table of mail domains *********************/
/*****************************************************************************/
static void Mai_UpdateMailDomainNameDB (long MaiCod,const char *FieldName,const char *NewMaiName)
{
char Query[128 + Mai_MAX_BYTES_MAIL_INFO];
/***** Update mail domain changing old name by new name */
sprintf (Query,"UPDATE mail_domains SET %s='%s' WHERE MaiCod=%ld",
FieldName,NewMaiName,MaiCod);
DB_QueryUPDATE (Query,"can not update the name of a mail domain");
}
/*****************************************************************************/
/*********************** Put a form to create a new mail *********************/
/*****************************************************************************/
static void Mai_PutFormToCreateMailDomain (void)
{
extern const char *Hlp_MESSAGES_Domains_edit;
extern const char *Txt_New_email_domain;
extern const char *Txt_EMAIL_DOMAIN_ORDER[3];
extern const char *Txt_Create_email_domain;
struct Mail *Mai;
Mai = &Gbl.Mails.EditingMai;
/***** Start form *****/
Act_FormStart (ActNewMai);
/***** Start of frame *****/
Lay_StartRoundFrameTable (NULL,Txt_New_email_domain,
NULL,Hlp_MESSAGES_Domains_edit,2);
/***** Write heading *****/
fprintf (Gbl.F.Out,"<tr>"
"<th class=\"LEFT_MIDDLE\">"
"%s"
"</th>"
"<th class=\"LEFT_MIDDLE\">"
"%s"
"</th>"
"</tr>",
Txt_EMAIL_DOMAIN_ORDER[Mai_ORDER_BY_DOMAIN],
Txt_EMAIL_DOMAIN_ORDER[Mai_ORDER_BY_INFO ]);
/***** Mail domain *****/
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"CENTER_MIDDLE\">"
"<input type=\"text\" name=\"Domain\""
" size=\"15\" maxlength=\"%u\" value=\"%s\""
" required=\"required\" />"
"</td>",
Cns_MAX_CHARS_EMAIL_ADDRESS,Mai->Domain);
/***** Mail domain info *****/
fprintf (Gbl.F.Out,"<td class=\"CENTER_MIDDLE\">"
"<input type=\"text\" name=\"Info\""
" size=\"40\" maxlength=\"%u\" value=\"%s\""
" required=\"required\" />"
"</td>"
"<td></td>"
"</tr>",
Mai_MAX_CHARS_MAIL_INFO,Mai->Info);
/***** Send button and end frame *****/
Lay_EndRoundFrameTableWithButton (Lay_CREATE_BUTTON,Txt_Create_email_domain);
/***** End of form *****/
Act_FormEnd ();
}
/*****************************************************************************/
/********************* Write header with fields of a mail ********************/
/*****************************************************************************/
static void Mai_PutHeadMailDomains (void)
{
extern const char *Txt_Code;
extern const char *Txt_EMAIL_DOMAIN_ORDER[3];
fprintf (Gbl.F.Out,"<tr>"
"<th class=\"BM\"></th>"
"<th class=\"RIGHT_MIDDLE\">"
"%s"
"</th>"
"<th class=\"LEFT_MIDDLE\">"
"%s"
"</th>"
"<th class=\"LEFT_MIDDLE\">"
"%s"
"</th>"
"<th class=\"RIGHT_MIDDLE\">"
"%s"
"</th>"
"</tr>",
Txt_Code,
Txt_EMAIL_DOMAIN_ORDER[Mai_ORDER_BY_DOMAIN],
Txt_EMAIL_DOMAIN_ORDER[Mai_ORDER_BY_INFO ],
Txt_EMAIL_DOMAIN_ORDER[Mai_ORDER_BY_USERS ]);
}
/*****************************************************************************/
/******************* Receive form to create a new mail ***********************/
/*****************************************************************************/
void Mai_RecFormNewMailDomain (void)
{
extern const char *Txt_The_email_domain_X_already_exists;
extern const char *Txt_You_must_specify_the_short_name_and_the_full_name_of_the_new_email_domain;
struct Mail *Mai;
Mai = &Gbl.Mails.EditingMai;
/***** Get parameters from form *****/
/* Get mail short name */
Par_GetParToText ("Domain",Mai->Domain,Cns_MAX_BYTES_EMAIL_ADDRESS);
/* Get mail full name */
Par_GetParToText ("Info",Mai->Info,Mai_MAX_BYTES_MAIL_INFO);
if (Mai->Domain[0] && Mai->Info[0]) // If there's a mail name
{
/***** If name of mail was in database... *****/
if (Mai_CheckIfMailDomainNameExists ("Domain",Mai->Domain,-1L))
{
sprintf (Gbl.Alert.Txt,Txt_The_email_domain_X_already_exists,
Mai->Domain);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
else if (Mai_CheckIfMailDomainNameExists ("Info",Mai->Info,-1L))
{
sprintf (Gbl.Alert.Txt,Txt_The_email_domain_X_already_exists,
Mai->Info);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
else // Add new mail to database
Mai_CreateMailDomain (Mai);
}
else // If there is not a mail name
Ale_ShowAlert (Ale_WARNING,Txt_You_must_specify_the_short_name_and_the_full_name_of_the_new_email_domain);
/***** Show the form again *****/
Mai_EditMailDomains ();
}
/*****************************************************************************/
/************************** Create a new mail domain *************************/
/*****************************************************************************/
static void Mai_CreateMailDomain (struct Mail *Mai)
{
extern const char *Txt_Created_new_email_domain_X;
char Query[128 +
Cns_MAX_BYTES_EMAIL_ADDRESS +
Mai_MAX_BYTES_MAIL_INFO];
/***** Create a new mail *****/
sprintf (Query,"INSERT INTO mail_domains"
" (Domain,Info)"
" VALUES"
" ('%s','%s')",
Mai->Domain,Mai->Info);
DB_QueryINSERT (Query,"can not create mail domain");
/***** Write success message *****/
sprintf (Gbl.Alert.Txt,Txt_Created_new_email_domain_X,
Mai->Domain);
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
}
/*****************************************************************************/
/****** List the emails of all the students to creates an email message ******/
/*****************************************************************************/
#define Mai_MAX_BYTES_STR_ADDR (256 * 1024 - 1)
void Mai_ListEmails (void)
{
extern const char *Hlp_MESSAGES_Email;
extern const char *The_ClassFormBold[The_NUM_THEMES];
extern const char *Txt_Students_who_have_accepted_and_who_have_email;
extern const char *Txt_X_students_who_have_email;
extern const char *Txt_X_students_who_have_accepted_and_who_have_email;
extern const char *Txt_Create_email_message;
unsigned NumUsr;
unsigned NumStdsWithEmail;
unsigned NumAcceptedStdsWithEmail;
char StrAddresses[Mai_MAX_BYTES_STR_ADDR + 1]; // TODO: Use malloc depending on the number of students
unsigned int LengthStrAddr = 0;
struct UsrData UsrDat;
/***** Get groups to show ******/
Grp_GetParCodsSeveralGrpsToShowUsrs ();
/***** Get and order list of students in this course *****/
Usr_GetListUsrs (Rol_STUDENT,Sco_SCOPE_CRS);
/***** Start of the frame used to list the emails *****/
Lay_StartRoundFrame (NULL,
Txt_Students_who_have_accepted_and_who_have_email,
NULL,Hlp_MESSAGES_Email);
/***** Form to select groups *****/
Grp_ShowFormToSelectSeveralGroups (ActMaiStd);
if (Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs)
{
if (Usr_GetIfShowBigList (Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs,NULL))
{
/***** Initialize structure with user's data *****/
Usr_UsrDataConstructor (&UsrDat);
/***** List the students' email addresses *****/
fprintf (Gbl.F.Out,"<div class=\"DAT_SMALL LEFT_MIDDLE\">");
for (NumUsr = 0, NumStdsWithEmail = 0, NumAcceptedStdsWithEmail = 0,
StrAddresses[0] = '\0';
NumUsr < Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs;
NumUsr++)
{
/* Copy user's basic data from list */
Usr_CopyBasicUsrDataFromList (&UsrDat,&Gbl.Usrs.LstUsrs[Rol_STUDENT].Lst[NumUsr]);
/* Get user's email */
Mai_GetEmailFromUsrCod (&UsrDat);
if (UsrDat.Email[0])
{
NumStdsWithEmail++;
if (UsrDat.Accepted) // If student has email and has accepted
{
if (NumAcceptedStdsWithEmail > 0)
{
fprintf (Gbl.F.Out,", ");
LengthStrAddr ++;
if (LengthStrAddr > Mai_MAX_BYTES_STR_ADDR)
Lay_ShowErrorAndExit ("The space allocated to store email addresses is full.");
Str_Concat (StrAddresses,",",
Mai_MAX_BYTES_STR_ADDR);
}
LengthStrAddr += strlen (UsrDat.Email);
if (LengthStrAddr > Mai_MAX_BYTES_STR_ADDR)
Lay_ShowErrorAndExit ("The space allocated to store email addresses is full.");
Str_Concat (StrAddresses,UsrDat.Email,
Mai_MAX_BYTES_STR_ADDR);
fprintf (Gbl.F.Out,"<a href=\"mailto:%s?subject=%s\">%s</a>",
UsrDat.Email,Gbl.CurrentCrs.Crs.FullName,UsrDat.Email);
NumAcceptedStdsWithEmail++;
}
}
}
fprintf (Gbl.F.Out,"</div>");
/***** Free memory used for user's data *****/
Usr_UsrDataDestructor (&UsrDat);
/***** Show a message with the number of students with email ****/
fprintf (Gbl.F.Out,"<div class=\"DAT CENTER_MIDDLE\">");
fprintf (Gbl.F.Out,Txt_X_students_who_have_email,
NumStdsWithEmail,
((float) NumStdsWithEmail /
(float) Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs) * 100.0,
Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs);
fprintf (Gbl.F.Out,"</div>");
/***** Show a message with the number of students who have accepted and have email ****/
fprintf (Gbl.F.Out,"<div class=\"DAT CENTER_MIDDLE\">");
fprintf (Gbl.F.Out,Txt_X_students_who_have_accepted_and_who_have_email,
NumAcceptedStdsWithEmail,
((float) NumAcceptedStdsWithEmail /
(float) Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs) * 100.0,
Gbl.Usrs.LstUsrs[Rol_STUDENT].NumUsrs);
fprintf (Gbl.F.Out,"</div>");
/***** Icon to open the client email program *****/
fprintf (Gbl.F.Out,"<div class=\"CONTEXT_MENU\">"
"<a href=\"mailto:%s?subject=%s&cc=%s&bcc=%s\""
" title=\"%s\" class=\"%s\">",
Gbl.Usrs.Me.UsrDat.Email,
Gbl.CurrentCrs.Crs.FullName,
Gbl.Usrs.Me.UsrDat.Email,
StrAddresses,
Txt_Create_email_message,
The_ClassFormBold[Gbl.Prefs.Theme]);
Lay_PutIconWithText ("editnewmsg16x16.gif",
Txt_Create_email_message,
Txt_Create_email_message);
fprintf (Gbl.F.Out,"</a>"
"</div>");
}
}
else
Usr_ShowWarningNoUsersFound (Rol_STUDENT);
/***** End of the frame used to list the emails *****/
Lay_EndRoundFrame ();
/***** Free memory for students list *****/
Usr_FreeUsrsList (Rol_STUDENT);
/***** Free memory for list of selected groups *****/
Grp_FreeListCodSelectedGrps ();
}
/*****************************************************************************/
/**************** Check whether an email address if valid ********************/
/*****************************************************************************/
bool Mai_CheckIfEmailIsValid (const char *Email)
{
unsigned Length = strlen (Email);
unsigned LastPosArroba = Length - 4;
const char *Ptr;
unsigned Pos;
bool ArrobaFound = false;
/***** An email address must have a number of characters
5 <= Length <= Cns_MAX_BYTES_EMAIL_ADDRESS *****/
if (Length < 5 ||
Length > Cns_MAX_BYTES_EMAIL_ADDRESS)
return false;
/***** An email address can have digits, letters, '.', '-' and '_';
and must have one and only '@' (not in any position) *****/
for (Ptr = Email, Pos = 0;
*Ptr;
Ptr++)
if ((*Ptr >= 'a' && *Ptr <= 'z') ||
(*Ptr >= 'A' && *Ptr <= 'Z') ||
(*Ptr >= '0' && *Ptr <= '9') ||
*Ptr == '.' ||
*Ptr == '-' ||
*Ptr == '_')
Pos++;
else if (*Ptr == '@')
{
if (ArrobaFound)
return false;
/* Example: a@b.c
01234
Length = 5
LastPosArroba = 5 - 4 = 1 */
if (Pos == 0 || Pos > LastPosArroba)
return false;
ArrobaFound = true;
}
else
return false;
return ArrobaFound;
}
/*****************************************************************************/
/********** Get email address of a user from his/her user's code *************/
/*****************************************************************************/
bool Mai_GetEmailFromUsrCod (struct UsrData *UsrDat)
{
char Query[256];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned long NumRows;
bool Found;
/***** Get current (last updated) user's nickname from database *****/
sprintf (Query,"SELECT E_mail,Confirmed FROM usr_emails"
" WHERE UsrCod=%ld ORDER BY CreatTime DESC LIMIT 1",
UsrDat->UsrCod);
NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get email address");
if (NumRows == 0)
{
UsrDat->Email[0] = '\0';
UsrDat->EmailConfirmed = false;
Found = false;
}
else
{
/* Get email */
row = mysql_fetch_row (mysql_res);
Str_Copy (UsrDat->Email,row[0],
Cns_MAX_BYTES_EMAIL_ADDRESS);
UsrDat->EmailConfirmed = (row[1][0] == 'Y');
Found = true;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
return Found;
}
/*****************************************************************************/
/************* Get user's code of a user from his/her email ******************/
/*****************************************************************************/
// Returns -1L if email not found
long Mai_GetUsrCodFromEmail (const char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1])
{
char Query[512 + Cns_MAX_BYTES_EMAIL_ADDRESS];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumUsrs;
long UsrCod = -1L;
if (Email)
if (Email[0])
{
/***** Get user's code from database *****/
/* Check if user code from table usr_emails is also in table usr_data */
sprintf (Query,"SELECT usr_emails.UsrCod FROM usr_emails,usr_data"
" WHERE usr_emails.E_mail='%s'"
" AND usr_emails.UsrCod=usr_data.UsrCod",
Email);
NumUsrs = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get user's code");
if (NumUsrs == 0)
/* User not found for this email ==> set user's code to void */
UsrCod = -1L;
else if (NumUsrs == 1) // One user found
{
/* Get row */
row = mysql_fetch_row (mysql_res);
/* Get user's code */
UsrCod = Str_ConvertStrCodToLongCod (row[0]);
}
else // NumRows > 1 ==> impossible, an email can not be reapeated
Lay_ShowErrorAndExit ("Internal error: email is repeated in database.");
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
return UsrCod;
}
/*****************************************************************************/
/********** Put a link to the action used to change user's emails ************/
/*****************************************************************************/
void Mai_PutLinkToChangeOtherUsrEmails (void)
{
extern const char *Txt_Change_email;
/***** Link for changing the password *****/
if (Gbl.Usrs.Other.UsrDat.UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod) // It's me
Lay_PutContextualLink (ActFrmMyAcc,NULL,NULL,
"msg64x64.gif",
Txt_Change_email,Txt_Change_email,
NULL);
else // Not me
Lay_PutContextualLink ( Gbl.Usrs.Other.UsrDat.RoleInCurrentCrsDB == Rol_STUDENT ? ActFrmMaiStd :
(Gbl.Usrs.Other.UsrDat.RoleInCurrentCrsDB == Rol_TEACHER ? ActFrmMaiTch :
ActFrmMaiOth),
NULL,Usr_PutParamOtherUsrCodEncrypted,
"msg64x64.gif",
Txt_Change_email,Txt_Change_email,
NULL);
}
/*****************************************************************************/
/*********** Show form to the change the email of another user ***************/
/*****************************************************************************/
void Mai_ShowFormOthEmail (void)
{
extern const char *Txt_Email;
extern const char *Txt_User_not_found_or_you_do_not_have_permission_;
/***** Get user whose password must be changed *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ())
{
if (Usr_ICanEditOtherUsr (&Gbl.Usrs.Other.UsrDat))
{
/***** Start frame *****/
Lay_StartRoundFrame (NULL,Txt_Email,NULL,NULL);
/***** Show user's record *****/
Rec_ShowSharedUsrRecord (Rec_SHA_RECORD_LIST,
&Gbl.Usrs.Other.UsrDat,NULL);
/***** Form with the user's email *****/
Lay_StartTableCenter (2);
Mai_ShowFormChangeUsrEmail (&Gbl.Usrs.Other.UsrDat,
(Gbl.Usrs.Other.UsrDat.UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod)); // It's me?
Lay_EndTable ();
/***** End frame *****/
Lay_EndRoundFrame ();
}
else
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
else // User not found
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
/*****************************************************************************/
/*********************** Show form to change my email ************************/
/*****************************************************************************/
void Mai_ShowFormChangeUsrEmail (const struct UsrData *UsrDat,bool ItsMe)
{
extern const char *The_ClassForm[The_NUM_THEMES];
extern const char *Txt_Current_email;
extern const char *Txt_Other_emails;
extern const char *Txt_Email_X_confirmed;
extern const char *Txt_Confirm_email;
extern const char *Txt_Use_this_email;
extern const char *Txt_New_email;
extern const char *Txt_Email;
extern const char *Txt_Change_email;
extern const char *Txt_Save;
char Query[256];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
unsigned NumEmails;
unsigned NumEmail;
bool Confirmed;
/***** Get my emails *****/
sprintf (Query,"SELECT E_mail,Confirmed FROM usr_emails"
" WHERE UsrCod=%ld"
" ORDER BY CreatTime DESC",
UsrDat->UsrCod);
NumEmails = (unsigned) DB_QuerySELECT (Query,&mysql_res,"can not get old email addresses of a user");
/***** List my emails *****/
for (NumEmail = 1;
NumEmail <= NumEmails;
NumEmail++)
{
/* Get email */
row = mysql_fetch_row (mysql_res);
Confirmed = (row[1][0] == 'Y');
if (NumEmail == 1)
/* The first mail is the current one */
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"%s RIGHT_MIDDLE\">"
"%s:"
"</td>"
"<td class=\"LEFT_MIDDLE\">"
"<div class=\"FORM_ACCOUNT\">",
The_ClassForm[Gbl.Prefs.Theme],Txt_Current_email);
else // NumEmail >= 2
{
fprintf (Gbl.F.Out,"<tr>");
if (NumEmail == 2)
fprintf (Gbl.F.Out,"<td rowspan=\"%u\" class=\"%s RIGHT_TOP\">"
"%s:",
NumEmails - 1,
The_ClassForm[Gbl.Prefs.Theme],
Txt_Other_emails);
fprintf (Gbl.F.Out,"</td>"
"<td class=\"LEFT_TOP\">"
"<div class=\"FORM_ACCOUNT\">");
}
/* Form to remove email */
if (ItsMe)
Act_FormStart (ActRemMaiMe);
else
{
Act_FormStart ( UsrDat->RoleInCurrentCrsDB == Rol_STUDENT ? ActRemMaiStd :
(UsrDat->RoleInCurrentCrsDB == Rol_TEACHER ? ActRemMaiTch :
ActRemMaiOth)); // Guest, visitor or admin
Usr_PutParamUsrCodEncrypted (UsrDat->EncryptedUsrCod);
}
fprintf (Gbl.F.Out,"<input type=\"hidden\" name=\"Email\" value=\"%s\" />",
row[0]);
Lay_PutIconRemove ();
Act_FormEnd ();
/* Email */
fprintf (Gbl.F.Out,"<span class=\"%s\">%s</span>",
NumEmail == 1 ? "USR_ID" :
"DAT",
row[0]);
/* Email confirmed? */
if (Confirmed)
{
sprintf (Gbl.Title,Txt_Email_X_confirmed,row[0]);
fprintf (Gbl.F.Out,"<img src=\"%s/ok_green16x16.gif\""
" alt=\"%s\" title=\"%s\""
" class=\"ICO20x20\" />",
Gbl.Prefs.IconsURL,
Gbl.Title,Gbl.Title);
}
fprintf (Gbl.F.Out,"</div>");
/* Form to change user's email */
if (NumEmail > 1 || (ItsMe && !Confirmed))
{
if (ItsMe)
Act_FormStart (ActNewMaiMe);
else
{
Act_FormStart ( UsrDat->RoleInCurrentCrsDB == Rol_STUDENT ? ActNewMaiStd :
(UsrDat->RoleInCurrentCrsDB == Rol_TEACHER ? ActNewMaiTch :
ActNewMaiOth)); // Guest, visitor or admin
Usr_PutParamUsrCodEncrypted (UsrDat->EncryptedUsrCod);
}
fprintf (Gbl.F.Out,"<input type=\"hidden\" name=\"NewEmail\" value=\"%s\" />",
row[0]); // Email
Lay_PutConfirmButtonInline ((ItsMe && NumEmail == 1) ? Txt_Confirm_email :
Txt_Use_this_email);
Act_FormEnd ();
fprintf (Gbl.F.Out,"</td>");
}
fprintf (Gbl.F.Out,"</td>"
"</tr>");
}
/***** Form to enter new email *****/
fprintf (Gbl.F.Out,"<tr>"
"<td class=\"RIGHT_MIDDLE\">"
"<label for=\"NewEmail\" class=\"%s\">%s:</label>"
"</td>"
"<td class=\"LEFT_MIDDLE\">",
The_ClassForm[Gbl.Prefs.Theme],
NumEmails ? Txt_New_email : // A new email
Txt_Email); // The first email
if (ItsMe)
Act_FormStart (ActNewMaiMe);
else
{
Act_FormStart ( UsrDat->RoleInCurrentCrsDB == Rol_STUDENT ? ActNewMaiStd :
(UsrDat->RoleInCurrentCrsDB == Rol_TEACHER ? ActNewMaiTch :
ActNewMaiOth)); // Guest, visitor or admin
Usr_PutParamUsrCodEncrypted (UsrDat->EncryptedUsrCod);
}
fprintf (Gbl.F.Out,"<div class=\"FORM_ACCOUNT\">"
"<input type=\"email\" id=\"NewEmail\" name=\"NewEmail\""
" size=\"18\" maxlength=\"%u\" value=\"%s\" />"
"</div>",
Cns_MAX_CHARS_EMAIL_ADDRESS,
Gbl.Usrs.Me.UsrDat.Email);
Lay_PutCreateButtonInline (NumEmails ? Txt_Change_email : // User already has an email address
Txt_Save); // User has no email address yet
Act_FormEnd ();
fprintf (Gbl.F.Out,"</td>"
"</tr>");
}
/*****************************************************************************/
/******************** Remove one of my user's emails *************************/
/*****************************************************************************/
void Mai_RemoveMyUsrEmail (void)
{
/***** Remove user's email *****/
Mai_RemoveEmail (&Gbl.Usrs.Me.UsrDat);
/***** Show my account again *****/
Acc_ShowFormChangeMyAccount ();
}
/*****************************************************************************/
/**************** Remove one of the user's IDs of another user ***************/
/*****************************************************************************/
void Mai_RemoveOtherUsrEmail (void)
{
extern const char *Txt_User_not_found_or_you_do_not_have_permission_;
/***** Get other user's code from form and get user's data *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ())
{
/***** Remove user's email *****/
Mai_RemoveEmail (&Gbl.Usrs.Other.UsrDat);
/***** Show user's record *****/
Rec_ShowSharedUsrRecord (Rec_SHA_RECORD_LIST,
&Gbl.Usrs.Other.UsrDat,NULL);
}
else // User not found
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
/*****************************************************************************/
/************************** Remove email address *****************************/
/*****************************************************************************/
static void Mai_RemoveEmail (struct UsrData *UsrDat)
{
extern const char *Txt_Email_X_removed;
extern const char *Txt_User_not_found_or_you_do_not_have_permission_;
char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1];
if (Usr_ICanEditOtherUsr (UsrDat))
{
/***** Get new email from form *****/
Par_GetParToText ("Email",Email,Cns_MAX_BYTES_EMAIL_ADDRESS);
/***** Remove one of user's old email addresses *****/
Mai_RemoveEmailFromDB (UsrDat->UsrCod,Email);
/***** Show message *****/
sprintf (Gbl.Alert.Txt,Txt_Email_X_removed,Email);
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
/***** Update list of emails *****/
Mai_GetEmailFromUsrCod (UsrDat);
}
else
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
/*****************************************************************************/
/*************** Remove an old email address from database *******************/
/*****************************************************************************/
static void Mai_RemoveEmailFromDB (long UsrCod,const char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1])
{
char Query[256 + Cns_MAX_BYTES_EMAIL_ADDRESS];
/***** Remove an old email address *****/
sprintf (Query,"DELETE FROM usr_emails"
" WHERE UsrCod=%ld AND E_mail='%s'",
UsrCod,Email);
DB_QueryREPLACE (Query,"can not remove an old email address");
}
/*****************************************************************************/
/************************* New user's email for me ***************************/
/*****************************************************************************/
void May_NewMyUsrEmail (void)
{
/***** Remove user's email *****/
Mai_NewUsrEmail (&Gbl.Usrs.Me.UsrDat,true); // It's me
/***** Show my account again *****/
Acc_ShowFormChangeMyAccount ();
}
/*****************************************************************************/
/********************* New user's email for another user *********************/
/*****************************************************************************/
void Mai_NewOtherUsrEmail (void)
{
extern const char *Txt_User_not_found_or_you_do_not_have_permission_;
/***** Get other user's code from form and get user's data *****/
if (Usr_GetParamOtherUsrCodEncryptedAndGetUsrData ())
{
/***** New user's ID *****/
Mai_NewUsrEmail (&Gbl.Usrs.Other.UsrDat,
(Gbl.Usrs.Other.UsrDat.UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod)); // It's me?
/***** Show user's record *****/
Rec_ShowSharedUsrRecord (Rec_SHA_RECORD_LIST,
&Gbl.Usrs.Other.UsrDat,NULL);
}
else // User not found
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
/*****************************************************************************/
/************************* Update my email address ***************************/
/*****************************************************************************/
static void Mai_NewUsrEmail (struct UsrData *UsrDat,bool ItsMe)
{
extern const char *Txt_The_email_address_X_matches_one_previously_registered;
extern const char *Txt_The_email_address_X_has_been_registered_successfully;
extern const char *Txt_The_email_address_X_had_been_registered_by_another_user;
extern const char *Txt_The_email_address_entered_X_is_not_valid;
extern const char *Txt_User_not_found_or_you_do_not_have_permission_;
char NewEmail[Cns_MAX_BYTES_EMAIL_ADDRESS + 1];
if (Usr_ICanEditOtherUsr (UsrDat))
{
/***** Get new email from form *****/
Par_GetParToText ("NewEmail",NewEmail,Cns_MAX_BYTES_EMAIL_ADDRESS);
if (Mai_CheckIfEmailIsValid (NewEmail)) // New email is valid
{
/***** Check if new email exists in database *****/
if (UsrDat->EmailConfirmed &&
!strcmp (UsrDat->Email,NewEmail)) // User's current confirmed email match exactly the new email
{
sprintf (Gbl.Alert.Txt,Txt_The_email_address_X_matches_one_previously_registered,
NewEmail);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
else
{
if (Mai_UpdateEmailInDB (UsrDat,NewEmail))
{
/***** Email updated sucessfully *****/
sprintf (Gbl.Alert.Txt,Txt_The_email_address_X_has_been_registered_successfully,
NewEmail);
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
/***** Update list of emails *****/
Mai_GetEmailFromUsrCod (UsrDat);
/***** Send message via email
to confirm the new email address *****/
if (ItsMe)
Mai_SendMailMsgToConfirmEmail ();
}
else
{
sprintf (Gbl.Alert.Txt,Txt_The_email_address_X_had_been_registered_by_another_user,
NewEmail);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
}
}
else // New email is not valid
{
sprintf (Gbl.Alert.Txt,Txt_The_email_address_entered_X_is_not_valid,
NewEmail);
Ale_ShowAlert (Ale_WARNING,Gbl.Alert.Txt);
}
}
else
Ale_ShowAlert (Ale_WARNING,Txt_User_not_found_or_you_do_not_have_permission_);
}
/*****************************************************************************/
/************************ Update email in database ***************************/
/*****************************************************************************/
// Return true if email is successfully updated
// Return false if email can not be updated beacuse it is registered by another user
bool Mai_UpdateEmailInDB (const struct UsrData *UsrDat,const char NewEmail[Cns_MAX_BYTES_EMAIL_ADDRESS + 1])
{
char Query[256 + Cns_MAX_BYTES_EMAIL_ADDRESS];
/***** Check if the new email matches any of the confirmed emails of other users *****/
sprintf (Query,"SELECT COUNT(*) FROM usr_emails"
" WHERE E_mail='%s' AND Confirmed='Y'"
" AND UsrCod<>%ld",
NewEmail,UsrDat->UsrCod);
if (DB_QueryCOUNT (Query,"can not check if email already existed")) // An email of another user is the same that my email
return false; // Don't update
/***** Delete email (not confirmed) for other users *****/
sprintf (Query,"DELETE FROM pending_emails"
" WHERE E_mail='%s' AND UsrCod<>%ld",
NewEmail,UsrDat->UsrCod);
DB_QueryDELETE (Query,"can not remove pending email for other users");
sprintf (Query,"DELETE FROM usr_emails"
" WHERE E_mail='%s' AND Confirmed='N'"
" AND UsrCod<>%ld",
NewEmail,UsrDat->UsrCod);
DB_QueryDELETE (Query,"can not remove not confirmed email for other users");
/***** Update email in database *****/
sprintf (Query,"REPLACE INTO usr_emails"
" (UsrCod,E_mail,CreatTime)"
" VALUES"
" (%ld,'%s',NOW())",
UsrDat->UsrCod,NewEmail);
DB_QueryREPLACE (Query,"can not update email");
return true; // Successfully updated
}
/*****************************************************************************/
/********** Put button to go to check and confirm my email address ***********/
/*****************************************************************************/
void Mai_PutButtonToCheckEmailAddress (void)
{
extern const char *Hlp_PROFILE_Account_email;
extern const char *Txt_Email_unconfirmed;
extern const char *Txt_Please_check_and_confirm_your_email_address;
extern const char *Txt_Check;
/***** Start form *****/
Act_FormStart (ActFrmMyAcc);
/***** Frame with button to go to account *****/
Lay_StartRoundFrame (NULL,Txt_Email_unconfirmed,
NULL,Hlp_PROFILE_Account_email);
Ale_ShowAlert (Ale_WARNING,Txt_Please_check_and_confirm_your_email_address);
Lay_EndRoundFrameWithButton (Lay_CONFIRM_BUTTON,Txt_Check);
/***** End form *****/
Act_FormEnd ();
}
/*****************************************************************************/
/************** Send mail message to confirm my email address ****************/
/*****************************************************************************/
// Return true on success
// Return false on error
bool Mai_SendMailMsgToConfirmEmail (void)
{
extern struct Act_Actions Act_Actions[Act_NUM_ACTIONS];
extern const char *Txt_If_you_just_request_from_X_the_confirmation_of_your_email_Y_NO_HTML;
extern const char *Txt_Confirmation_of_your_email_NO_HTML;
extern const char *Txt_There_was_a_problem_sending_an_email_automatically;
char Command[2048]; // Command to execute for sending an email
int ReturnCode;
/***** Create temporary file for mail content *****/
Mai_CreateFileNameMail ();
/***** Write mail content into file and close file *****/
/* Welcome note */
Mai_WriteWelcomeNoteEMail (&Gbl.Usrs.Me.UsrDat);
/* Store encrypted key in database */
Mai_InsertMailKey (Gbl.Usrs.Me.UsrDat.Email,Gbl.UniqueNameEncrypted);
/* Message body */
fprintf (Gbl.Msg.FileMail,
Txt_If_you_just_request_from_X_the_confirmation_of_your_email_Y_NO_HTML,
Cfg_URL_SWAD_CGI,Gbl.Usrs.Me.UsrDat.Email,
Cfg_URL_SWAD_CGI,Act_Actions[ActCnfMai].ActCod,Gbl.UniqueNameEncrypted,
Cfg_URL_SWAD_CGI);
/* Footer note */
Mai_WriteFootNoteEMail (Gbl.Prefs.Language);
fclose (Gbl.Msg.FileMail);
/***** Call the script to send an email *****/
sprintf (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_Confirmation_of_your_email_NO_HTML,
Gbl.Msg.FileNameMail);
ReturnCode = system (Command);
if (ReturnCode == -1)
Lay_ShowErrorAndExit ("Error when running script to send email.");
/***** Remove temporary file *****/
unlink (Gbl.Msg.FileNameMail);
/***** Write message depending on return code *****/
ReturnCode = WEXITSTATUS(ReturnCode);
switch (ReturnCode)
{
case 0: // Message sent successfully
Gbl.Usrs.Me.ConfirmEmailJustSent = true;
return true;
case 1:
Ale_ShowAlert (Ale_WARNING,Txt_There_was_a_problem_sending_an_email_automatically);
return false;
default:
sprintf (Gbl.Alert.Txt,"Internal error: an email message has not been sent successfully."
" Error code returned by the script: %d",
ReturnCode);
Ale_ShowAlert (Ale_ERROR,Gbl.Alert.Txt);
return false;
}
}
/*****************************************************************************/
/******* Show alert to report that confirmation email has been sent **********/
/*****************************************************************************/
void Mai_ShowMsgConfirmEmailHasBeenSent (void)
{
extern const char *Txt_A_message_has_been_sent_to_email_address_X_to_confirm_that_address;
sprintf (Gbl.Alert.Txt,Txt_A_message_has_been_sent_to_email_address_X_to_confirm_that_address,
Gbl.Usrs.Me.UsrDat.Email);
Ale_ShowAlert (Ale_INFO,Gbl.Alert.Txt);
}
/*****************************************************************************/
/************************* Insert mail hey in database ***********************/
/*****************************************************************************/
static void Mai_InsertMailKey (const char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1],
const char MailKey[Mai_LENGTH_EMAIL_CONFIRM_KEY + 1])
{
char Query[256 +
Cns_MAX_BYTES_EMAIL_ADDRESS +
Mai_LENGTH_EMAIL_CONFIRM_KEY];
/***** Remove expired pending emails from database *****/
sprintf (Query,"DELETE FROM pending_emails"
" WHERE DateAndTime<FROM_UNIXTIME(UNIX_TIMESTAMP()-'%lu')",
Cfg_TIME_TO_DELETE_OLD_PENDING_EMAILS);
DB_QueryDELETE (Query,"can not remove old pending mail keys");
/***** Insert mail key in database *****/
sprintf (Query,"INSERT INTO pending_emails"
" (UsrCod,E_mail,MailKey,DateAndTime)"
" VALUES"
" (%ld,'%s','%s',NOW())",
Gbl.Usrs.Me.UsrDat.UsrCod,
Email,
MailKey);
DB_QueryREPLACE (Query,"can not create pending password");
}
/*****************************************************************************/
/************************* Confirm my email address **************************/
/*****************************************************************************/
void Mai_ConfirmEmail (void)
{
extern const char *Txt_Email_X_has_already_been_confirmed_before;
extern const char *Txt_The_email_X_has_been_confirmed;
extern const char *Txt_The_email_address_has_not_been_confirmed;
extern const char *Txt_Failed_email_confirmation_key;
char Query[256 +
Cns_MAX_BYTES_EMAIL_ADDRESS +
Mai_LENGTH_EMAIL_CONFIRM_KEY];
MYSQL_RES *mysql_res;
MYSQL_ROW row;
char MailKey[Mai_LENGTH_EMAIL_CONFIRM_KEY + 1];
long UsrCod;
char Email[Cns_MAX_BYTES_EMAIL_ADDRESS + 1];
bool KeyIsCorrect = false;
bool Confirmed;
/***** Get parameter Key *****/
Par_GetParToText ("key",MailKey,Mai_LENGTH_EMAIL_CONFIRM_KEY);
/***** Get user's code and email from key *****/
sprintf (Query,"SELECT UsrCod,E_mail FROM pending_emails"
" WHERE MailKey='%s'",
MailKey);
if (DB_QuerySELECT (Query,&mysql_res,"can not get user's code and email from key"))
{
row = mysql_fetch_row (mysql_res);
/* Get user's code */
UsrCod = Str_ConvertStrCodToLongCod (row[0]);
/* Get user's email */
Str_Copy (Email,row[1],
Cns_MAX_BYTES_EMAIL_ADDRESS);
KeyIsCorrect = true;
}
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
if (KeyIsCorrect)
{
/***** Delete this key *****/
sprintf (Query,"DELETE FROM pending_emails WHERE MailKey='%s'",
MailKey);
DB_QueryDELETE (Query,"can not remove an email key");
/***** Check user's code and email
and get if email is already confirmed *****/
sprintf (Query,"SELECT Confirmed FROM usr_emails"
" WHERE UsrCod=%ld AND E_mail='%s'",
UsrCod,Email);
if (DB_QuerySELECT (Query,&mysql_res,"can not get user's code and email"))
{
Confirmed = (row[0][0] == 'Y');
/***** Confirm email *****/
if (Confirmed)
sprintf (Gbl.Alert.Txt,Txt_Email_X_has_already_been_confirmed_before,
Email);
else
{
sprintf (Query,"UPDATE usr_emails SET Confirmed='Y'"
" WHERE usr_emails.UsrCod=%ld"
" AND usr_emails.E_mail='%s'",
UsrCod,Email);
DB_QueryUPDATE (Query,"can not confirm email");
sprintf (Gbl.Alert.Txt,Txt_The_email_X_has_been_confirmed,Email);
}
Ale_ShowAlert (Ale_SUCCESS,Gbl.Alert.Txt);
}
else
Ale_ShowAlert (Ale_WARNING,Txt_The_email_address_has_not_been_confirmed);
/***** Free structure that stores the query result *****/
DB_FreeMySQLResult (&mysql_res);
}
else
Ale_ShowAlert (Ale_WARNING,Txt_Failed_email_confirmation_key);
/***** Form to log in *****/
Usr_WriteFormLogin (ActLogIn,NULL);
}
/*****************************************************************************/
/****************** Create temporary file for mail content *******************/
/*****************************************************************************/
void Mai_CreateFileNameMail (void)
{
sprintf (Gbl.Msg.FileNameMail,"%s/%s/%s_mail.txt",
Cfg_PATH_SWAD_PRIVATE,Cfg_FOLDER_OUT,Gbl.UniqueNameEncrypted);
if ((Gbl.Msg.FileMail = fopen (Gbl.Msg.FileNameMail,"wb")) == NULL)
Lay_ShowErrorAndExit ("Can not open file to send email.");
}
/*****************************************************************************/
/************ Write a welcome note heading the automatic email ***************/
/*****************************************************************************/
void Mai_WriteWelcomeNoteEMail (struct UsrData *UsrDat)
{
extern const char *Txt_Dear_NO_HTML[Usr_NUM_SEXS][1 + Txt_NUM_LANGUAGES];
extern const char *Txt_user_NO_HTML[Usr_NUM_SEXS][1 + Txt_NUM_LANGUAGES];
fprintf (Gbl.Msg.FileMail,"%s %s:\n",
Txt_Dear_NO_HTML[UsrDat->Sex][UsrDat->Prefs.Language],
UsrDat->FirstName[0] ? UsrDat->FirstName :
Txt_user_NO_HTML[UsrDat->Sex][UsrDat->Prefs.Language]);
}
/*****************************************************************************/
/****************** Write a foot note in the automatic email *****************/
/*****************************************************************************/
void Mai_WriteFootNoteEMail (Txt_Language_t Language)
{
extern const char *Txt_Please_do_not_reply_to_this_automatically_generated_email_NO_HTML[1 + Txt_NUM_LANGUAGES];
fprintf (Gbl.Msg.FileMail,"%s\n"
"%s\n"
"%s\n",
Txt_Please_do_not_reply_to_this_automatically_generated_email_NO_HTML[Language],
Cfg_PLATFORM_SHORT_NAME,
Cfg_URL_SWAD_CGI);
}
/*****************************************************************************/
/**************** Check if I can see another user's email ********************/
/*****************************************************************************/
bool Mai_ICanSeeOtherUsrEmail (const struct UsrData *UsrDat)
{
if (UsrDat->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod) // It's me
return true;
/* Check if I have permission to see another user's email */
switch (Gbl.Usrs.Me.LoggedRole)
{
case Rol_STUDENT:
/* If I am a student in the current course,
I can see the email of confirmed teachers */
return (UsrDat->RoleInCurrentCrsDB == Rol_TEACHER && // A teacher
UsrDat->Accepted); // who accepted registration
case Rol_TEACHER:
/* If I am a teacher in the current course,
I can see the email of confirmed students and teachers */
return (UsrDat->RoleInCurrentCrsDB == Rol_STUDENT || // A student
UsrDat->RoleInCurrentCrsDB == Rol_TEACHER) && // or a teacher
UsrDat->Accepted; // who accepted registration
case Rol_DEG_ADM:
/* If I am an administrator of current degree,
I only can see the user's email of users from current degree */
return Usr_CheckIfUsrBelongsToDeg (UsrDat->UsrCod,
Gbl.CurrentDeg.Deg.DegCod);
case Rol_CTR_ADM:
/* If I am an administrator of current centre,
I only can see the user's email of users from current centre */
return Usr_CheckIfUsrBelongsToCtr (UsrDat->UsrCod,
Gbl.CurrentCtr.Ctr.CtrCod);
case Rol_INS_ADM:
/* If I am an administrator of current institution,
I only can see the user's email of users from current institution */
return Usr_CheckIfUsrBelongsToIns (UsrDat->UsrCod,
Gbl.CurrentIns.Ins.InsCod);
case Rol_SYS_ADM:
return true;
default:
return false;
}
}