Скрипт форума на PHP своими руками. Часть 4
Итак, продолжаем наш разговор о пользователях сайта. Если пользователь забыл свой пароль, он может получить новый,
заполнив форму, которую формирует функция newPasswordForm():
<?php
// Если пользователь забыл свой пароль, он может получить новый,
// заполнив эту форму (свой логин и e-mail)
function newPasswordForm()
{
$html = '';
if ( isset( $_SESSION['newPasswordForm']['error'] ) ) {
$info = file_get_contents( './templates/infoMessage.html' );
$info = str_replace( '{infoMessage}', $_SESSION['newPasswordForm']['error'], $info );
$html = $html.$info."n";
unset( $_SESSION['newPasswordForm']['error'] );
}
$action = $_SERVER['PHP_SELF'].'?action=sendNewPassword';
$tpl = file_get_contents( './templates/newPasswordForm.html' );
$tpl = str_replace( '{action}', $action, $tpl );
$html = $html . $tpl;
return $html;
}
?>
Обработчик этой формы - функция sendNewPassword() высылает на e-mail пользователя новый пароль.
<?php
// Функция высылает на e-mail пользователя новый пароль
function sendNewPassword()
{
// Если не переданы методом POST логин и e-mail - перенаправляем пользователя
if ( !isset( $_POST['username'] ) or
!isset( $_POST['email'] )
)
{
header( 'Location: '.$_SERVER['PHP_SELF'] );
die();
}
// Обрезаем переменные до длины, указанной в параметре maxlength тега input
$name = substr( $_POST['username'], 0, 30 );
$email = substr( $_POST['email'], 0, 60 );
// Обрезаем лишние пробелы
$name = trim( $name );
$email = trim( $email );
// Проверяем, заполнены ли обязательные поля
$error = "";
if ( empty( $name ) ) $error = $error.'<li>не заполнено поле "Имя"</li>'."n";
if ( empty( $email ) ) $error = $error.'<li>не заполнено поле "Адрес e-mail"</li>'."n";
// Проверяем поля формы на недопустимые символы
if ( !empty( $name ) and !ereg( "[-_[:blank:]0-9a-zA-Zа-яА-Я]+", $name ) )
$error = $error.'<li>поле "Имя" содержит недопустимые символы</li>'."n";
// Проверяем корректность e-mail
if ( !empty( $email ) and !preg_match( "#^[0-9a-z_-.]+@[0-9a-z-.]+.[a-z]{2,6}$#i", $email ) )
$error = $error.'<li>поле "Адрес e-mail" должно соответствовать формату somebody@somewhere.ru</li>'."n";
// Проверять существование такого пользователя есть смысл только в том
// случае, если поля не пустые и не содержат недопустимых символов
if ( empty( $error ) ) {
$query = "SELECT id_author FROM ".TABLE_USERS."
WHERE name='".mysql_real_escape_string( $name )."'
AND email='".mysql_real_escape_string( $email )."'";
$res = mysql_query( $query );
if ( !$res ) {
$msg = 'Ошибка при создании нового пароля.';
$err = 'Ошибка при выполнении запроса: <br/>'.
$query.'<br/>'.mysql_errno().': '.mysql_error().'<br/>'.
'(Файл '. __FILE__ .', строка '. __LINE__ .')';
return showErrorMessage( $msg, $err, true, 'action=newPasswordForm' );
}
// Если пользователь с таким логином и e-mail существует
if ( mysql_num_rows( $res ) > 0 ) {
// Небольшой код, который читает содержимое директории activate
// и удаляет старые файлы для активации пароля (были созданы более суток назад)
if ( $dir = opendir( './activate' ) ) {
chdir( './activate' );
$tmp = 24*60*60;
while ( false !== ( $file = readdir($dir) ) ) {
if ( is_file( $file ) )
if ( ( time() - filemtime( $file ) ) > $tmp ) unlink( $file );
}
chdir( '..' );
closedir( $dir );
}
// Как происходит процедура восстановления пароля? Пользователь ввел свой логин
// и e-mail, мы проверяем существование такого пользователя в таблице БД. Потом
// генерируем с помощью функции getNewPassword() новый пароль, создаем файл с именем
// md5( $newPassword ) в директории activate. Файл содержит ID пользователя.
// В качестве кода активации выступает хэш пароля - md5( $newPassword ).
// Когда пользователь перейдет по ссылке в письме для активации своего нового пароля,
// мы проверяем наличие в директории activate файла с именем кода активации,
// и если он существует, активируем новый пароль.
$id = mysql_result( $res, 0, 0 );
$newPassword = getNewPassword();
$code = md5( $newPassword );
// file_put_contents( './activate/'.$code, $id );
$fp = fopen( './activate/'.$code, "w" );
fwrite($fp, $id);
fclose($fp);
// Посылаем письмо пользователю с просьбой активировать пароль
$headers = "From: ".$_SERVER['SERVER_NAME']." <".ADMIN_EMAIL.">n";
$headers = $headers."Content-type: text/html; charset="windows-1251"n";
$headers = $headers."Return-path: <".ADMIN_EMAIL.">n";
$message = '<p>Добрый день, '.$name.'!</p>'."n";
$message = $message.'<p>Вы получили это письмо потому, что вы (либо кто-то, выдающий себя
за вас) попросили выслать новый пароль к вашей учётной записи на форуме '.
$_SERVER['SERVER_NAME'].'. Если вы не просили выслать пароль, то не обращайте
внимания на это письмо, если же подобные письма будут продолжать приходить,
обратитесь к администратору форума</p>'."n";
$message = $message.'<p>Прежде чем использовать новый пароль, вы должны его активировать.
Для этого перейдите по ссылке:</p>'."n";
$link = '//'.$_SERVER['SERVER_NAME'].$_SERVER['PHP_SELF'].
'?action=activatePassword&code='.$code;
$message = $message.'<p><a href="'.$link.'">Активировать пароль</a></p>'."n";
$message = $message.'<p>В случае успешной активации вы сможете входить в систему, используя
следующий пароль: '.$newPassword.'</p>'."n";
$message = $message.'<p>Вы сможете сменить этот пароль на странице редактирования профиля.
Если у вас возникнут какие-то трудности, обратитесь к администратору форума.</p>'."n";
$subject = 'Активация пароля на форуме '.$_SERVER['SERVER_NAME'];
$subject = '=?koi8-r?B?'.base64_encode(convert_cyr_string($subject, "w","k")).'?=';
mail( $email, $subject, $message, $headers );
$msg = 'На ваш e-mail выслано письмо. Чтобы активировать новый пароль, зайдите
по адресу, указанному в письме.';
$html = file_get_contents( './templates/infoMessage.html' );
$html = str_replace( '{infoMessage}', $msg, $html );
return $html;
} else {
$error = $error.'<li>неправильный логин или e-mail</li>'."n";
}
}
// Если были допущены ошибки при заполнении формы - перенаправляем посетителя
if ( !empty( $error ) ) {
$_SESSION['newPasswordForm'] = array();
$_SESSION['newPasswordForm']['error'] = '<p class="errorMsg">При заполнениии формы были допущены
ошибки:</p>'."n".'<ul class="errorMsg">'."n".$error.'</ul>'."n";
header( 'Location: '.$_SERVER['PHP_SELF'].'?action=newPasswordForm' );
die();
}
}
?>
Как происходит процедура восстановления пароля? Пользователь вводит свой логин и e-mail, мы проверяем существование такого пользователя в таблице БД. Потом генерируем с помощью функции getNewPassword() новый пароль, создаем файл с именем md5( $newPassword ) в директории activate. Файл содержит ID пользователя. В качестве кода активации выступает хэш пароля - md5( $newPassword ). Когда пользователь перейдет по ссылке в письме для активации своего нового пароля, мы проверяем наличие в директории activate файла с именем кода активации, и если он существует, активируем новый пароль.
<?php
// Активация нового пароля
function activatePassword()
{
if ( !isset( $_GET['code'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'] );
die();
}
// Т.к. код активации создан с помощью md5, то он
// представляет собой 32-значное шестнадцатеричное число
$code = substr( $_GET['code'], 0, 32 );
$code = preg_replace( "#[^0-9a-f]#i", '', $code );
if ( is_file( './activate/'.$code ) and ( ( time() - filemtime('./activate/'.$code) ) < 24*60*60 ) ) {
$file = file( './activate/'.$code );
unlink( './activate/'.$code );
$id_user = (int)trim( $file[0] );
$query = "UPDATE ".TABLE_USERS."
SET passw='".mysql_real_escape_string( $code )."'
WHERE id_author=".$id_user;
$res = mysql_query( $query );
if ( !$res ) {
$msg = 'Ошибка при активации нового пароля. Обратитесь к администратору.';
$err = 'Ошибка при выполнении запроса: <br/>'.
$query.'<br/>'.mysql_errno().': '.mysql_error().'<br/>'.
'(Файл '. __FILE__ .', строка '. __LINE__ .')';
return showErrorMessage( $msg, $err, true, 'action=newPasswordForm' );
}
$message = 'Ваш новый пароль успешно активирован.';
} else {
$message = 'Ошибка при активации нового пароля. Обратитесь к администратору.';
}
$html = file_get_contents( './templates/infoMessage.html' );
$html = str_replace( '{infoMessage}', $message, $html );
return $html;
}
?>
Функция getNewPassword() возвращает случайно сгенерированный пароль:
<?php
// Функция возвращает случайно сгенерированный пароль
function getNewPassword()
{
$length = rand( 10, 30 );
$password = '';
for( $i = 0; $i < $length; $i++ ) {
$range = rand(1, 3);
switch( $range ) {
case 1: $password = $password.chr( rand(48, 57) ); break;
case 2: $password = $password.chr( rand(65, 90) ); break;
case 3: $password = $password.chr( rand(97, 122) ); break;
}
}
return $password;
}
?>
Пользователь должен иметь возможность изменить свои данные - за это отвечают функции getEditUserForm() и updateUser().
<?php
// Функция возвращает html формы для редактирования данных о пользователе
function getEditUserForm()
{
// Если информацию о пользователе пытается редактировать
// не зарегистрированный пользователь
if ( !isset( $_SESSION['user'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'] );
die();
}
$html = '';
// Если при заполнении формы были допущены ошибки
if ( isset( $_SESSION['editUserForm'] ) ) {
$info = file_get_contents( './templates/infoMessage.html' );
$info = str_replace( '{infoMessage}', $_SESSION['editUserForm']['error'], $info );
$html = $html.$info."n";
$email = htmlspecialchars( $_SESSION['editUserForm']['email'] );
$timezone = $_SESSION['editUserForm']['timezone'];
$icq = htmlspecialchars( $_SESSION['editUserForm']['icq'] );
$url = htmlspecialchars( $_SESSION['editUserForm']['url'] );
$about = htmlspecialchars( $_SESSION['editUserForm']['about'] );
$signature = htmlspecialchars( $_SESSION['editUserForm']['signature'] );
unset( $_SESSION['editUserForm'] );
} else {
$email = htmlspecialchars( $_SESSION['user']['email'] );
$timezone = 0;
$icq = htmlspecialchars( $_SESSION['user']['icq'] );
$url = htmlspecialchars( $_SESSION['user']['url'] );
$about = htmlspecialchars( $_SESSION['user']['about'] );
$signature = htmlspecialchars( $_SESSION['user']['signature'] );
}
$action = $_SERVER['PHP_SELF'].'?action=updateUser';
$tpl = file_get_contents( './templates/editUserForm.html' );
$tpl = str_replace( '{action}', $action, $tpl );
$tpl = str_replace( '{email}', htmlspecialchars( $email ), $tpl );
$tpl = str_replace( '{icq}', htmlspecialchars( $icq ), $tpl );
$tpl = str_replace( '{url}', htmlspecialchars( $url ), $tpl );
$tpl = str_replace( '{about}', htmlspecialchars( $about ), $tpl );
$tpl = str_replace( '{signature}', htmlspecialchars( $signature ), $tpl );
$options = '';
for ( $i = -12; $i <= 12; $i++ ) {
if ( $i < 1 )
$value = $i.' часов';
else
$value = '+'.$i.' часов';
if ( $i == $_SESSION['user']['timezone'] )
$options = $options . '<option value="'.$i.'" selected>'.$value.'</option>'."n";
else
$options = $options . '<option value="'.$i.'">'.$value.'</option>'."n";
}
$tpl = str_replace( '{options}', $options, $tpl);
$tpl = str_replace( '{servertime}', date( "d.m.Y H:i:s" ), $tpl );
// Если ранее был загружен файл - надо предоставить возможность удалить его
$unlinkfile = '';
if ( is_file( './photo/'.$_SESSION['user']['id_author'] ) ) {
$unlinkfile = '<br/><input type="checkbox" name="unlink" value="1" />
Удалить загруженный ранее файл'."n";
}
$tpl = str_replace( '{unlinkfile}', $unlinkfile, $tpl );
$html = $html.$tpl;
return $html;
}
?>
Обработчик формы:
<?php
// Функция обновляет данные пользователя (обновляет запись в таблице TABLE_USERS)
function updateUser()
{
// Если это не зарегистрированный пользователь - функция вызвана по ошибке
if ( !isset( $_SESSION['user'] ) ) {
header( 'Location: '.$_SERVER['PHP_SELF'] );
die();
}
// Если не переданы данные формы - функция вызвана по ошибке
if ( !isset( $_POST['timezone'] ) or
!isset( $_POST['icq'] ) or
!isset( $_POST['url'] ) or
!isset( $_POST['about'] ) or
!isset( $_POST['signature'] ) or
!isset( $_FILES['avatar'] )
)
{
header( 'Location: '.$_SERVER['PHP_SELF'] );
die();
}
// Обрезаем переменные до длины, указанной в параметре maxlength тега input
$password = substr( $_POST['password'], 0, 30 );
$newpassword = substr( $_POST['newpassword'], 0, 30 );
$confirm = substr( $_POST['confirm'], 0, 30 );
$email = substr( $_POST['email'], 0, 60 );
$icq = substr( $_POST['icq'], 0, 12 );
$url = substr( $_POST['url'], 0, 60 );
$about = substr( $_POST['about'], 0, 1000 );
$signature = substr( $_POST['signature'], 0, 500 );
// Обрезаем лишние пробелы
$password = trim( $password );
$newpassword = trim( $newpassword );
$confirm = trim( $confirm );
$email = trim( $email );
$icq = trim( $icq );
$url = trim( $url );
$about = trim( $about );
$signature = trim( $signature );
// Проверяем, заполнены ли обязательные поля
$error = '';
// Если заполнено поле "Текущий пароль" - значит пользователь
// хочет изменить его или поменять свой e-mail
$changePassword = false;
$changeEmail = false;
if ( !empty( $_POST['password'] ) ) {
if ( md5( $_POST['password'] ) != $_SESSION['user']['passw'] )
$error = $error.'<li>текущий пароль введен не верно</li>'."n";
// Надо выяснить, что хочет сделать пользователь:
// поменять свой e-mail, изменить пароль или и то и другое
if ( !empty( $newpassword ) ) { // хочет изменить пароль
$changePassword = true;
if ( empty( $confirm ) ) $error = $error.'<li>не заполнено поле "Подтвердите пароль"</li>'."n";
// Проверяем, не слишком ли короткий новый пароль
if ( strlen( $newpassword ) < MIN_PASSWORD_LENGTH )
$error = $error.'<li>длина пароля должна быть не меньше '.MIN_PASSWORD_LENGTH.' символов</li>'."n";
// Проверяем, совпадают ли пароли
if ( !empty( $confirm ) and $newpassword != $confirm )
$error = $error.'<li>не совпадают пароли</li>'."n";
// Проверяем поля формы на недопустимые символы
if ( !preg_match( "#^[-_0-9a-z]+$#i", $newpassword ) )
$error = $error.'<li>поле "Новый пароль" содержит недопустимые символы</li>'."n";
if ( !empty( $confirm ) and !preg_match( "#^[-_0-9a-z]+$#i", $confirm ) )
$error = $error.'<li>поле "Подтвердите пароль" содержит недопустимые символы</li>'."n";
}
if ( $email != $_SESSION['user']['email'] ) { // хочет изменить e-mail
$changeEmail = true;
if ( empty( $email ) ) $error = $error.'<li>не заполнено поле "Адрес e-mail"</li>'."n";
// Проверяем корректность e-mail
if ( !empty( $email ) and !preg_match( "#^[0-9a-z_-.]+@[0-9a-z-.]+.[a-z]{2,6}$#i", $email ) )
$error = $error.'<li>поле "Адрес e-mail" должно соответствовать формату
somebody@somewhere.ru</li>'."n";
}
}
// Проверяем поля формы на недопустимые символы
if ( !empty( $icq ) and !preg_match( "#^[0-9]+$#", $icq ) )
$error = $error.'<li>поле "ICQ" содержит недопустимые символы</li>'."n";
if ( !empty( $about ) and !preg_match( "#^[-[].;:,?!/)(_"s0-9а-яА-Яa-z]+$#i", $about ) )
$error = $error.'<li>поле "Интересы" содержит недопустимые символы</li>'."n";
if ( !empty( $signature ) and !preg_match( "#^[-[].;:,?!/)(_"s0-9а-яА-Яa-z]+$#i", $signature ) )
$error = $error.'<li>поле "Подпись" содержит недопустимые символы</li>'."n";
// Проверяем корректность URL домашней странички
if ( !empty( $url ) and !preg_match( "#^(//)?(www.)?[-0-9a-z]+.[a-z]{2,6}/?$#i", $url ) )
$error = $error.'<li>поле "Домашняя страничка" должно соответствовать формату //www.homepage.ru</li>'."n";
if ( !empty( $_FILES['avatar']['name'] ) ) {
$ext = strrchr( $_FILES['avatar']['name'], "." );
$extensions = array( ".jpg", ".gif", ".bmp", ".png" );
if ( !in_array( $ext, $extensions ) )
$error = $error.'<li>недопустимый формат файла аватара</li>'."n";
if ( $_FILES['avatar']['size'] > MAX_AVATAR_SIZE )
$error = $error.'<li>размер файла аватора больше '.(MAX_AVATAR_SIZE/1024).' Кб</li>'."n";
}
$timezone = (int)$_POST['timezone'];
if ( $timezone < -12 or $timezone > 12 ) $timezone = 0;
// Если были допущены ошибки при заполнении формы -
// перенаправляем посетителя на страницу редактирования
if ( !empty( $error ) ) {
$_SESSION['editUserForm'] = array();
$_SESSION['editUserForm']['error'] = '<p class="errorMsg">При заполнениии формы были допущены ошибки:</p>'.
"n".'<ul class="errorMsg">'."n".$error.'</ul>'."n";
$_SESSION['editUserForm']['email'] = $email;
$_SESSION['editUserForm']['timezone'] = $timezone;
$_SESSION['editUserForm']['icq'] = $icq;
$_SESSION['editUserForm']['url'] = $url;
$_SESSION['editUserForm']['about'] = $about;
$_SESSION['editUserForm']['signature'] = $signature;
header( 'Location: '.$_SERVER['PHP_SELF'].'?action=editUserForm' );
die();
}
// Если выставлен флажок "Удалить загруженный ранее файл"
if ( isset( $_POST['unlink'] ) and is_file( './photo/'.$_SESSION['user']['id_author'] ) ) {
unlink( './photo/'.$_SESSION['user']['id_author'] );
}
if ( !empty( $_FILES['avatar']['name'] ) and
move_uploaded_file ( $_FILES['avatar']['tmp_name'], './photo/'.$_SESSION['user']['id_author'] ) ) {
chmod( './photo/'.$_SESSION['user']['id_author'], 0644 );
}
// Все поля заполнены правильно - записываем изменения в БД
$tmp = '';
if ( $changePassword ) {
$tmp = $tmp."passw='".mysql_real_escape_string( md5( $newpassword ) )."', ";
$_SESSION['user']['passw'] = md5( $newpassword );
}
if ( $changeEmail ) {
$tmp = $tmp."email='".mysql_real_escape_string( $email )."', ";
$_SESSION['user']['email'] = $email;
}
$query = "UPDATE ".TABLE_USERS." SET ".$tmp."
timezone=".$timezone.",
url='".mysql_real_escape_string( $url )."',
icq='".$icq."',
about='".mysql_real_escape_string( $about )."',
signature='".mysql_real_escape_string( $signature )."'
WHERE id_author=".$_SESSION['user']['id_author'];
//echo $query;
//die();
$res = mysql_query( $query );
if ( !$res ) {
$msg = 'Ошибка при обновлении профиля';
$err = 'Ошибка при выполнении запроса: <br/>'.
$query.'<br/>'.mysql_errno().': '.mysql_error().'<br/>'.
'(Файл '. __FILE__ .', строка '. __LINE__ .')';
return showErrorMessage( $msg, $err, true, '' );
}
// Теперь надо обновить данные о пользователе в массиве $_SESSION['user']
if ( $changePassword ) $_SESSION['user']['passw'] = md5( $newpassword );
if ( $changeEmail ) $_SESSION['user']['email'] = $email;
$_SESSION['user']['timezone'] = $timezone;
$_SESSION['user']['url'] = $url;
$_SESSION['user']['icq'] = $icq;
$_SESSION['user']['about'] = $about;
$_SESSION['user']['signature'] = $signature;
// ... и в массиве $_COOKIES
if ( isset( $_COOKIE['autologin'] ) ) {
$tmppos = strrpos( $_SERVER['PHP_SELF'], '/' ) + 1;
$path = substr( $_SERVER['PHP_SELF'], 0, $tmppos );
setcookie( 'autologin', 'yes', time() + 3600*24*COOKIE_TIME, $path );
setcookie( 'username', $_SESSION['user']['name'], time() + 3600*24*COOKIE_TIME, $path );
setcookie( 'password', $_SESSION['user']['passw'], time() + 3600*24*COOKIE_TIME, $path );
}
return showInfoMessage( 'Ваш профиль был изменён', '' );
}
?>
Продолжение следует...
|