БлогNot. PHP: пишем почтовую форму "попроще"

PHP: пишем почтовую форму "попроще"

Код частично из архивов, но, вроде бы, ничего особо "страшного" в нём нет. Здесь я не отправлял письмо с вложением, а просто организовывал более-менее простую и безопасную форму для программной отправки электронной почты, то, что называют mailer. Только нужен был достаточно простой.

Применяя подобные вещи, следует всегда иметь в виду изобретательность спамеров, которые могут подделать заголовки HTTP и использовать вашу форму для своих целей.

С другой стороны, если код не выложен в открытый доступ или хотя бы содержит уникальные настройки, его можно использовать.

В отличие от множества других подобных скриптов, доступных в сети, этот скрипт состоит всего из одного файла, имеет простую текстовую капчу и загружает форму только после загрузки всей страницы, что "обрубает" большинство известных ботов. На отключённый Javascript выдаётся сообщение-предупреждение, а если в браузере клиента отключены Cookie, письмо также не получится отправить. Настройки URL скрипта и сайта также прописываются в коде "ручками", так и проще, и надёжней. Также в заголовок X-Mailer и в текст письма включается IP-адрес отправителя.

Через POST никаких кодов не передаётся, только сохраняется в сессии "сдвинутый" с помощью настройки SECRET и при этом зашифрованный код капчи. При показе пользователю капча "зашумляется" случайными тегами.

Исключив из начала файла вызов session_start (наверняка он у Вас делается где-то в другом месте) и настроив форму "под себя" Вы легко сможете встроить такой простой mailer в существующий проект.

Код не снабжён HTML-обрамлением, предполагается, что оно будет там, куда вставляется программа.

Можно снабдить такую форму javascript-активизацией кнопки "Отправить", которая срабатывает только тогда, когда пользователь заполнил нужные поля (так сделано у меня в блоге), но опыт показывает, что если заполняемых полей более двух, это только сбивает с толку.

Предполагается, что скрипт будет работать и отправлять письмо в кодировке Юникод (UTF-8). Вот код файла .htaccess, размещённого в папке с мейлером:

AddDefaultCharset utf-8
php_flag magic_quotes_gpc off
php_flag magic_quotes_runtime off
php_flag magic_quotes_sybase off

Вот код самого мейлера, на реальном хостинге сработал, и, вроде бы, "живёт" на паре сайтов:

<?php
 //Следующие 2 строки, возможно, будут Вами удалены
 error_reporting (E_ALL); //для отладки
 session_start (); //должно быть выполнено до выдачи любого контента в браузер

 //Настройки
 define ('SECRET','1234'); //уникальное целое число для дополнительной защиты, смените на своё
 define ('MAINURL','localhost');  // Главный URL сайта без черты в конце, смените на свой
 define ('MYURL','index.php'); //URL скрипта, смените на свой
 define ('MAXWORDLENGTH','32'); //Максимальная длина слова в письме
 define ('EOL',"\r\n"); //Разделитель строк, некоторые почтовые сервера требуют \n - подобрать опытным путём
 define ('SUBJECT','Сообщение от '); //Начало темы письма

 $fields = Array ( //поля формы, заполните по образцу
  //имя, тип, подпись, значение, длина
  Array ('name', 'text', 'Ваше имя', '', 32),
  Array ('email', 'text', 'Ваш адрес E-mail', '', 32),
  Array ('text', 'textarea', 'Сообщение', '', 1024),
  Array ('list', 'select', 'Адресат', 'default@mail.ru', Array('default@mail.ru'=>'Дирекция','buh@mail.ru'=>'Бухгалтерия')),
  Array ('code', 'captcha', 'Код подтверждения', '', 4)
 );
 $params = array ('name','email','text','list','code','action'); //разрешённые имена параметров POST

 //Код
 while (list($num,$var) = each($params)) { //приём параметров POST
  if (!empty($_POST[$var])) {
   $$var = trimall(htmlspecialchars($_POST[$var],ENT_COMPAT,'utf-8'));
   for ($i=0; $i<count($fields); $i++) {
    if ($fields[$i][0]==$var) {
     $type = $fields[$i][1];
     if ($type=='text' || $type=='textarea' || $type=='select') {
      $fields[$i][3]= $$var; //сохранить ранее введённое, кроме кода
     }
    }
   }
  }
  else $$var = ''; //если величина не передана - будет создана пустая
 }
 echo '<div id="sform"></div>'."\n"; //сюда будет вставлена форма после загрузки страницы
 $error = '';
 $valid='';
 if (isset($_SESSION['valid'])) $valid=$_SESSION['valid'];
 if (!empty($action)) {
  if (!empty($code)) {
   $code = strval(intval($code)-intval(SECRET));
   if (md5($code) == $valid) { 
    if (!empty($name) and !empty($email) and !empty($text) and !empty($list)) { //Поля заполнены
     $name_len = mb_strlen($name,'utf-8');
     $email_len = mb_strlen($email,'utf-8');
     $text_len = mb_strlen($text,'utf-8');
     if ($name_len > 3 and $email_len > 6 and $text_len > 6) {
      if ($name_len <= 32 and $email_len <= 32 and $text_len <= 1024) {
       $words = preg_split("/[\s]+/",$text);
       $goodwords = true;
       for ($i=0; $i<count($words); $i++) {
        if (mb_strlen($words[$i],'utf-8') > MAXWORDLENGTH) { $goodwords = false; break; }
       }
       if ($goodwords) {
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
         if ($_SERVER['SERVER_NAME'] == MAINURL) {
          $subj = SUBJECT.$name.' <'.$email.'>';
          $ip=get_ip();
          $headers = 
          'From: '.$email.EOL.
          'To: '.$list.EOL.
          'Reply-To: '.$email.EOL.
          'Subject: '.$subj.EOL.
          'Content-type: text/plain; charset=utf-8'.EOL.
          'X-Mailer: PHP/'.phpversion().' (IP='.$ip.')'.EOL.EOL;
          if (mail($email,$subj,$text.EOL.EOL.$name.' (E-mail: '.$email.', IP-адрес: '.$ip.')',$headers)) {
           echo '<p>Спасибо, Ваше сообщение успешно отправлено</p>';
          }
          else {
           $error.="<br>Ошибка при отправке почты функцией mail, обратитесь к администратору";
          }
         }
         else {
          $error.="<br>Форма заполнена на другом сайте, отправка почты недоступна";
         }
        }
        else {
         $error.="<br>Введённый адрес E-mail некорректен";
        }
       }
       else {
        $error.="<br>Обнаружено слишком длинное слово, пожалуйста, введите корректный текст письма";
       }
      }
      else {
       $error.="<br>Введённый в одно из полей текст - слишком длинный, пожалуйста, введите корректные данные";
      }
     }
     else {
      $error.="<br>Введённый в одно из полей текст - слишком короткий, пожалуйста, введите корректные данные";
     }
    }
    else {
     $error.="<br>Заполнены не все поля письма";
    }
   }
   else {
    $error.="<br>Введён неверный код подтверждения или отключён приём Cookie-файлов в браузере";
   }
  }
  else {
   $error.="<br>Не указан код сообщения или отключён приём Cookie-файлов в браузере";
  }
 }

 if (empty($action) or !empty($error)) {
  if (!empty($error)) {
   echo '<p>Извините, Ваше письмо не может быть отправлено. Причины:'.$error.'</p>'."\n";
  }
  echo form ($fields);
 }

 function form ($fields) {
  $form = '<form name="guestForm" action="'.MYURL.'" method="post"><table border="0" width="90%" align="center">'."\n";
  for ($i=0; $i<count($fields); $i++) {
   $form .= '<tr>'."\n";
   $field = $fields[$i]; 
   $name = $field[0]; $type = $field[1]; $msg = $field[2]; $value = $field[3]; $maxlen = $field[4];  
   $form .= '<td><p>'.$msg.':</p></td>'."\n".'<td>'."\n";
   switch ($type) {
    case 'text':
     $form .= '<input type="text" name="'.$name.'" maxlength="'.$maxlen.'" size="'.($maxlen+2).'" value="'.$value.'">'."\n";
    break;
    case 'textarea':
     $form .= '<textarea name="'.$name.'" maxlength="'.$maxlen.'" rows="12" cols="86">'.$value.'</textarea>'."\n";
    break;
    case 'select':
     $form .= '<select name="'.$name.'" size="1">'."\n";
     foreach ($maxlen as $key=>$val) {
      $form .= '<option value="'.$key.'"'.($key==$value?' selected':'').'>'.$val."\n";
     }
     $form .= '</select>'."\n";
    break;
    case 'captcha':
     $form .= '<input type="text" name="'.$name.'" maxlength="'.$maxlen.'" size="'.($maxlen+1).'" value="">'.
      ' ('.visible_code(generate_code()).')'."\n";
    break;
   }
   $form .= '</td></tr>'."\n";
  }
  $form .= '<tr><td>&nbsp;</td><td><input name="action" type="submit" value="Отправить"></td></tr></table></form>'."\n";
  $form = '<script type="text/javascript">
 window.addEventListener("load", function() { 
  document.getElementById("sform").innerHTML = \''.trimall($form).'\'; 
 });
 </script>
 <noscript>
  <form>
   <table border="0" width="90%" align="center">
    <tr>
     <td>
      <p>
       Извините, для отправки сообщений нужен включённый в Вашем браузере Javascript. 
      </p>
     </td>
    </tr>
   </table>
  </form>
 </noscript>'."\n";
  return $form;
 }

 function visible_code ($s) {
  $tags = Array (
   Array ( '<span>','</span>' ),
   Array ( '<b>','</b>' ),
   Array ( '<i>','</i>' ),
   Array ( '<u>','</u>' ),
   Array (  '<s>','</s>' ),
   Array ( '<sub>','</sub>' ),
   Array ( '<sup>','</sup>' ),
   Array ( '<big>','</big>' ),
   Array ( '<small>','</small>' )
  );
  $len = mb_strlen ($s,'utf-8');
  $res = '';
  for ($i = 0; $i < $len; $i++) {
   $c = substr ($s, $i, 1);
   $n = rand(0, count($tags)-1);
   $res .= $tags[$n][0].$c.$tags[$n][1];
  }
  return $res;
 }

 function generate_code() {
  srand (time());
  $valid = mt_rand(1000,9999);
  $_SESSION['valid'] = md5 ($valid - intval(SECRET));
  return $valid;
 }

 function trimall ($string) { 
  return preg_replace("/\s+/"," ",trim($string));
 }

 function get_ip () {
  if( getenv('HTTP_X_FORWARDED_FOR') != '' ) {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
   $entries = explode(',', getenv('HTTP_X_FORWARDED_FOR'));
   reset($entries);
   while (list(, $entry) = each($entries)) {
    $entry = trim($entry);
    if ( preg_match("/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/", $entry, $ip_list) ) {
     $private_ip = array('/^0\./', '/^127\.0\.0\.1/', '/^192\.168\..*/', '/^172\.((1[6-9])|(2[0-9])|(3[0-1]))\..*/', 
      '/^10\..*/', '/^224\..*/', '/^240\..*/');
     $found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);
     if ($client_ip != $found_ip) {
      $client_ip = $found_ip;
      break;
     }
    }
   }
  }
  else {
   $client_ip = ( !empty($_SERVER['REMOTE_ADDR']) ) ? $_SERVER['REMOTE_ADDR'] : 
    ( ( !empty($_ENV['REMOTE_ADDR']) ) ? $_ENV['REMOTE_ADDR'] : $REMOTE_ADDR );
  }
  return $client_ip;
 }
?>

Стили и прочее нетрудно дописать. Также на хостинге должна быть разрешена функция mail, чтобы работало.

12.05.2018, 13:19 [1905 просмотров]


теги: программирование php безопасность email

К этой статье пока нет комментариев, Ваш будет первым