Содержание


Реальные веб-проекты на PHP и MySQL. Разработка почтовой службы с веб-интерфейсом

Часть 1.План построения почтовой службы и реализация базы данных проекта

Comments

Серия контента:

Этот контент является частью # из серии # статей: Реальные веб-проекты на PHP и MySQL. Разработка почтовой службы с веб-интерфейсом

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Реальные веб-проекты на PHP и MySQL. Разработка почтовой службы с веб-интерфейсом

Следите за выходом новых статей этой серии.

1. Введение

В предыдущих статьях многократно описывались вопросы настройки и запуска в эксплуатацию самых разнообразных почтовых служб, - таких как sendmail, postfix, exim, Communigate, qmail, использующих в качестве системной платформы самый широкий спектр современных Linux- и BSD-систем. В настоящее время, по мере развития web все чаще и чаще возникает вопрос об актуальности использования услуг электронной почты, основанной на веб. В этой серии статей будут рассмотрены вопросы, посвященные реализации веб-интерфейса к существующему почтовому серверу с использованием PHP-библиотек, поддерживающих IMAP.

С помощью такого интерфейса можно будет просматривать содержимое своего почтового ящика прямо на веб-странице. Если расширить проект и усилить его, то он легко может превратиться в реальную многопользовательскую почтовую систему, наподобие mail.ru.

В рамках этого проекта планируется разработать почтовый клиент под гипотетическим названием «Безопасная и быстрая почта», который может предложить пользователям следующие функции:

  1. подключение к своим учетным записям на почтовых серверах POP и IMAP;
  2. чтение приходящих почтовых сообщений;
  3. отправка своих сообщений электронной почты;
  4. отправка ответов на пришедшие сообщения;
  5. переадресация сообщений;
  6. удаление ненужных сообщений из своего почтового ящика.

Для реализации этого широкого спектра задач необходимо найти возможность для подключения пользователя к работающему почтовому серверу, кроме того, там же должен находиться и веб-сервер. В рамках общей задачи должно быть налажено взаимодействие с почтовым ящиком пользователя для индивидуальной обработки всех сообщений.

Для обеспечения возможности чтения сообщений на сервере должны функционировать протоколы POP и IMAP (см. RFC 1939). Желательна поддержка обоих протоколов, но более удобным и безопасным на сегодняшний день считается IMAP (Internet Message Access Protocol – протокол доступа к электронной почте). В общем случае POP3 предназначен для короткого взаимодействия с сервером в целях загрузки, чтения и/или удаления почтовых сообщений. Протокол IMAP больше ориентирован на использование в режиме постоянного подключения к сети и серверу, на котором развернута почтовая служба. Ни один из этих протоколов не предназначен для отправки почты - для этого служит протокол SMTP (см. RFC821).

Модули решения поставленной задачи

В рамках PHP реализована поддержка обоих протоколов, но она поставляется через библиотеку функций IMAP. Для нормальной работы всего излагаемого материала в системе должна быть установлена библиотека IMAP. Установлена ли она или нет в системе, покажет информация в выводе функции phpinfo(). Если библиотека не установлена, то получить ее можно здесь: ftp://ftp.cac.washington.edu/imap/.

В случае возникновения проблем при компиляции библиотеки в UNIX/Linux среде рекомендуется использовать ее более стабильную версию IMAP-2001. Затем желательно создать подкаталог в рабочем для веб-сервера каталоге, например с именем imap/lib и imap/include. Далее нужно скопировать все файлы *.h из места инсталляции библиотеки IMAP в подкаталог /imap/include. Затем, в случае успешной компиляции появится файлик c-client.a и его надо переименовать в libc-client.a и скопировать в imap/lib/.

После чего нужно запустить PHP-сценарий конфигурирования, добавив директиву –with-imap=dirname (здесь dirname – имя созданного каталога) к набору директив, используемых в системе и перекомпилировать PHP для поддержки IMAP.

В Windows для доступа к расширению IMAP нужно будет открыть файл php.ini и раскомментировать строку extension=php_imap.dll, после этого веб-сервер надо будет перезагрузить.

Проверить правильность установки расширения IMAP можно, выполнив функцию phpinfo(). В выводе этой функции должен быть раздел, посвященный IMAP.

Сама библиотека IMAP содержит очень много функций, но в данном цикле статей будут рассматриваться только следующие:

  1. imap_open();
  2. imap_close();
  3. imap_header();
  4. imap_headers();
  5. imap_fetchheader();
  6. imap_body();
  7. imap_delete();
  8. imap_expunge()

Для получения возможности работы с почтой необходимо иметь на сервере учетную запись для конкретного пользователя и обладать на ее паролем. На основе СУБД MySQL учетные записи пользователей и их пароли будут храниться в структурированном виде, а система будет получать к ним быстрый и избирательный доступ. Если принять во внимание тот факт, что пользователи любят иметь по несколько учетных записей - каждую для разных целей, то будет нужно обеспечить хранение в базе данных нескольких наборов сведений об учетных записях для каждого пользователя.

Каждый пользователь должен иметь возможность читать сообщения, отвечать на них, пересылать и удалять существующие, а также отправлять вновь созданные. Все функции по чтению надо будет возложить на протоколы IMAP и POP3, а операции по отправке сообщений – на протокол SMTP, который можно будет активировать функцией mail().

Общая схема решения

Первым шагом в работе системы должен быть вход пользователя с его последующей авторизацией, после чего ему может быть предоставлен список необходимых опций. В этом случае он может создать новую учетную запись или выбрать для использования одну из уже существующих. Кроме того, должны быть реализованы такие возможности как просмотр входящих почтовых сообщений, ответ на них, переадресации, удаление и отправка новых сообщений.

Большим плюсом будет наличие просмотра заголовков с дополнительной информацией, связанной с конкретными сообщениями. Именно просмотр заголовков дает возможность определить, откуда пришло это сообщение и понять, нужно ли его вам читать и тем более отвечать на него. Кроме этого, такая информация дает возможность просмотреть весь процесс путешествия сообщения от хоста к хосту.

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

Создание базы данных

В этом случае база данных под проект будет достаточно простой. Все что там должно быть – сведения о пользователях системы, причем для каждого из них нужно предусмотреть существование следующих полей:

  1. username – имя пользователя системы;
  2. password – пароль пользователя;
  3. address – указываемый пользователем адрес электронной почты, отображаемый в заголовке “from” сообщения;
  4. displayname – имя, которое будет отображаться в отправляемых сообщениях;

для каждой учетной записи, кроме того, нужно будет хранить следующее:

  1. username – имя пользователя приложения «Безопасная и быстрая почта», которому принадлежит учетная запись;
  2. server – машина, на которой размещена учетная запись пользователя;
  3. port – порт, к которому будет выполняться подключение от имени этой учетной записис. Как правило, для POP3 – это 110 порт, для IMAP 143.
  4. type – протокол, используемый для подключения к данному серверу (POP3 или IMAP).
  5. remoteuser – имя пользователя для подключения к почтовому серверу;
  6. accounted – уникальный идентификатор, ключ, служащий для отождествления учетных записей.

Для создания базы данных используется следующий SQL-код:

create database mail;

use mail;

create table users
(
  username char(16) not null primary key,
  password char(40) not null,
  address char(100) not null,
  displayname char(100) not null
);

create table accounts
(
  username char(16) not null,
  server char(100) not null,
  port int not null,
  type char(4) not null,
  remoteuser char(50) not null,
  remotepassword char(50) not null,
  accountid int unsigned not null auto_increment primary key
);

grant select, insert, update, delete
on mail.*
to mail@localhost identified by 'password';

его можно ввести в окне phpMyAdmin или в консоли сервера командой:

mysql –u root –p < create_database.sql

(скрипт, содержащий приведенный выше код).

Код, представленный ниже, позволяет завести пользователя user в систему с паролем password. Для добавления других пользователей нужно видоизменить этот код и добавить к нему все то, что нужно.

use mail;

insert into users values 
('user', sha1('password'), 'email@host.domain', 'Имя, фамилия');

insert into accounts values 
('user', 'localhost', '110', 'POP3', 'user', 'password', ''),
('user', 'localhost', '143', 'IMAP', 'user', 'password', '');

Основная библиотека mail-фукнций

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

<?php
function retrieve_message($auth_user, $accountid, $messageid, $fullheaders)
{
  $message = array();
  if(!($auth_user & $messageid & $accountid))
    return false;
  
  $imap = open_mailbox($auth_user, $accountid);
  if(!$imap) 
    return false;      
  
  $header = imap_header($imap, $messageid);
  if(!$header) 
    return false;
  
  $message['body'] = imap_body($imap, $messageid);
  if(!$message['body']) 
    $message['body'] = "[Отсутствует тело сообщения]\n\n\n\n\n\n";
  if($fullheaders)
    $message['fullheaders'] = imap_fetchheader($imap, $messageid);
  else
    $message['fullheaders'] = '';
  $message['subject'] = $header->subject;
  $message['fromaddress'] =   $header->fromaddress;
  $message['toaddress'] =   $header->toaddress;
  $message['ccaddress'] =   $header->ccaddress;
  $message['date'] =   $header->date;
    
    imap_close($imap); 
  return $message;
}

function account_exists($auth_user, $accountid)
{
  // Проверяет, имеет ли данный пользователь учетную запись с заданным 
  //идентификатором
  
  $query = "select count(*) from accounts where username = '$auth_user' and 
  accountid = $accountid";
  if($conn=db_connect())
  {
    $result = $conn->query($query);
    $row = $result->fetch_array();
    return $row[0];
  }
  return false;
}

function get_account_list($auth_user)
{
  // Получает массив идентификаторов учетных записей, которые относятся к 
  //данному пользователю

  $query = "select accountid from accounts where username = '$auth_user'";
  $list = array();
  
  if($conn=db_connect())
  {
    $result = $conn->query($query);
    $num = $result->num_rows;
    for($i = 0; $i<$num; $i++)
    {
      $row = $result->fetch_array();
      array_push($list, $row[0]);
    }
  }
  return $list;
}

function number_of_accounts($auth_user)
{
  // Получает количество учетных записей, которые относятся к данному 
  //пользователю
  
  $query = "select count(*) from accounts where username = '$auth_user'";

  if($conn=db_connect())
  {
    $result = $conn->query($query);
      if($result)
      {
        $row = $result->fetch_array();
        return $row[0];
      }
  }
  return 0;
}

function get_account_settings($auth_user, $accountid=0)
{
  // Получает массив параметров данной учетной записи
  $settings = array();
  if($conn=db_connect())
  {
    if($accountid>0 & $accountid!='')
    {
      $query = "select * from accounts where accountid=$accountid and username 
	  = '$auth_user'";
    }
    else if (number_of_accounts($auth_user)==1)
    {
      $query = "select * from accounts where username = '$auth_user'";
    }
    else
    {
      return false;
    }
    $result = $conn->query($query);
    if($result)
    {
      $settings = $result->fetch_assoc();
      return $settings;
    }
    else
      return false;
  }
  return false;
}

function get_accounts($auth_user)
{
  $list = array();
  if($conn=db_connect())
  {
    $query = "select * from accounts where username = '$auth_user'";
    $result = $conn->query($query);
    if($result)
    {
      while($settings = $result->fetch_assoc())
        array_push($list, $settings);
    }
    else
      return false;
  }
  return $list;
}


function store_account_settings($auth_user, $settings)
{
  if(!filled_out($settings))
  {
    echo 'Все поля должны быть заполнены.  Повторите попытку.<br /><br />';
    return false;
  }
  else
  {
    if($settings['account']>0)
      $query = "update accounts  set server = '$settings[server]',
                  port = $settings[port], type = '$settings[type]',
                  remoteuser = '$settings[remoteuser]', 
                  remotepassword = '$settings[remotepassword]' 
                where accountid = $settings[account] 
                  and username = '$auth_user'";
    else
      $query = "insert into accounts values ('$auth_user', 
                     '$settings[server]', $settings[port], 
                     '$settings[type]', '$settings[remoteuser]', 
                     '$settings[remotepassword]', NULL)";
    if($conn->db_connect())
    {
      $result = $conn->query($query);
      if ($query)
        return true;
      else
        return false;
    }
    else
    {
      echo 'Невозможно сохранить изменения.<br /><br 
	  /><br /><br /><br /><br />';
      return false;
    }
  }
}

function delete_message($auth_user, $accountid, $message_id)
{
  // Удаляет одно сообщение на сервере
  $imap = open_mailbox($auth_user, $accountid);
  if($imap)
  {
    imap_delete($imap, $message_id);
    imap_expunge($imap);
    imap_close($imap);
    return true;
  }
  return false;
}


function open_mailbox($auth_user, $accountid)
{
  
  // Выбрать почтовый ящик, если он единственный
  if(number_of_accounts($auth_user)==1)
  {
    $accounts = get_account_list($auth_user); 
    $_SESSION['selected_account'] = $accounts[0]; 
    $accountid = $accounts[0];
  }

  // Подключиться к POP3 или IMAP-серверу, выбранному пользователем
  $settings = get_account_settings($auth_user, $accountid);
  if(!sizeof($settings)) return 0;
  $mailbox = '{'.$settings[server];
  if($settings[type]=='POP3')
    $mailbox .= '/pop3';
  
  $mailbox .= ':'.$settings[port].'}INBOX';

  // Подавить вывод предупреждающих сообщений, 
  // проверить возвращаемое значение
@ $imap = imap_open($mailbox, $settings['remoteuser'], 
                    $settings['remotepassword']);

  return  $imap;
}

function get_list($imap)
{
  // Получает список сообщений из почтового ящика

  $headers = imap_headers($imap);
  $messages = sizeof($headers);
  for($i = 0; $i<$messages; $i++)
  {
    echo $headers[$i];
  }
  imap_close($imap); 
}

function send_message($to, $cc, $subject, $message)
{
  // Отправляет одно сообщение электронной почты с помощью PHP
  
  if (!$conn->db_connect())
  { 
    return false;
  }
  $query = 'select address from users where '
           .'username=\''.$_SESSION['auth_user']."'";
  
  $result = $conn->query($query);
  if (!$result)
  {
    return false;  
  }
  else if ($result->num_rows==0)
  {
    return false; 
  }
  else
  {
    $row = $result->fetch_object();
    $other = 'From: '.$row->address;
    if (!empty($cc))
      $other.="\r\nCc: $cc";
    if (mail($to, $subject, $message, $other))
    {
      return true;
    }
    else
    {
      return false;
    }
  }
}

function add_quoting($string, $pattern = '> ')
{
  // Добавляет символы цитирования в ответном сообщении
  return $pattern.str_replace("\n", "\n$pattern", $string);
}

Выводы

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


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=577042
ArticleTitle=Реальные веб-проекты на PHP и MySQL. Разработка почтовой службы с веб-интерфейсом: Часть 1.План построения почтовой службы и реализация базы данных проекта
publish-date=11092010