Получение новой почты с помощью IMAP PHP

Задача у нас такая. Необходимо подключаться к ящику на gmail.com заходить в папку входящие и если есть непрочитанные читать их, помечать прочитаннымми, а текст и вложения расшифровывать и сохранить к себе.

Для работы с почтой сначала необходимо подключить расширение imap для php, если оно не подключено.

В начале подключимся. Для этого используется команда imap_open, куда передаются адрес для подключения, пользователь и пароль.


$user  = 'example@gmail.com';

$pass = '123';

$connect = imap_open('{imap.gmail.com:993/imap/ssl}INBOX',$user, $pass);
if ($connect) echo 'Successful'; else {echo 'Failed'; die;}

В адресе мы передаем адрес сервера imap, порт подключения, тип соединения (также доступен /pop3) и показываем, что соединение будет зашифровано — так требует gmail. После фигурных скобок указываем папку к которой подключаемся, в данном случае к входящим.

Теперь запросим список непрочитанных писем. Это осуществляется командой imap_search, в которую передаем наше соединение и флаги.


$mails = imap_search($connect, 'UNSEEN');

Флаг ‘UNSEEN’, как вы наверное уже догадались обозначает непрочитанные сообщения. В $mails вернется массив с номерами сообщений. Теперь, пробежавшись по массиву, мы сможем получить каждое письмо. Я надеюсь, что все знают foreach, по этому предположим что в $mail  у нас будет хранится номер письма из массива $mails.

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


$structure = imap_fetchstructure($connect, $mail);
$boundary = '';

if ($structure->ifparameters) {
foreach ($structure->parameters as $param)
{
if (strtolower($param->attribute) == 'boundary')
$boundary = $param->value;
}
}

$parts = array();
// Get allparts to $parts
getParts($structure, $parts);

function getParts($object, & $parts)
{

// Object is multipart
if ($object->type == 1) {

foreach ($object->parts as $part)
{
getParts($part, $parts);
}
}
else
{
$p['type'] = $object->type;
$p['encode'] = $object->encoding;
$p['subtype'] = $object->subtype;
$p['bytes'] = $object->bytes;
if ($object->ifparameters == 1) {
foreach ($object->parameters as $param)
{
$p['params'][] = array('attr' => $param->attribute,
'val'  => $param->value);
}
}
if ($object->ifdparameters == 1) {
foreach ($object->dparameters as $param)
{
$p['dparams'][] = array('attr' => $param->attribute,
'val'  => $param->value);
}
}
$p['disp'] = null;
if ($object->ifdisposition == 1) {
$p['disp'] = $object->disposition;
}
$parts[] = $p;
}
}

В $boundary теперь хранится метка-разделитель, необходимая для разбора тела письма, а в $parts структура частей. Получение частей вынесено в отдельную функцию, которая вызывается рекурсивно. Если кому интересно, то информацию о частях, а также о самой функции можно прочитать здесь http://www.php.su/functions/?imap-fetchstructure.

Приступаем к разбору тела письма. Вкратце часть тела может содержать вложенные части или не содержать. Если содержит, то аттрибут type будет равен 1.


if ($structure->type == 1) {

$parts = array();
// Get allparts to $parts
getParts($structure, $parts);

$mail['body'] = imap_fetchbody($connect, $mail, '1');

$mail['body'] = imap_utf8((getPlain($mail['body'], $boundary)));

$mail['body'] = iconv('KOI8-R', 'utf-8', $mail['body']);

// Get attach

$i = 0;
foreach ($parts as $part)
{

// Not text or multipart
if ($part['type'] > 1) {

$file = imap_fetchbody($connect, $mail, $i);
$mail['files'][] = array('content'  => base64_decode($file),
'filename' => $part['params'][0]['val'],
'size'     => $part['bytes']);
}
$i++;
}
}
else
{
$mail['body'] = imap_body($connect, $mail);

$mail['body'] = imap_utf8((getPlain($mail['body'], $boundary)));

$mail['body'] = iconv('KOI8-R', 'utf-8', $mail['body']);
}

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

Стоит обратить внимание на функцию getPlain, которая осуществляет разбор письма, если он будет передан в неразобранном виде.


function getPlain($str, $boundary)
{
$lines = explode("\n", $str);

$plain = false;
$res = '';
$start = false;
foreach ($lines as $line) {

if (strpos($line, 'text/plain') !== false) $plain = true;

if (strlen($line) == 1 && $plain) {
$start = true;
$plain = false;
continue;
}

if ($start && strpos($line, 'Content-Type') !== false) $start = false;
if ($start)
$res .= $line;

}

$res = substr($res, 0, strpos($res, '--' . $boundary));

$res = base64_decode($res == '' ? $str : $res);

return $res;
}

После преобразований в $mail['body'] будет содержаться текст письма, а в $mail['files'] приаттаченные файлы. Осталось только разобрать заголовок и готово.


$mail['subject'] = imap_utf8($header->subject);

if (isset($header->to[0]->personal))
$mail['to']['personal'] = imap_utf8($header->to[0]->personal);
else
$mail['to']['personal'] = '';
$mail['to']['mailbox'] = imap_utf8($header->to[0]->mailbox);
$mail['to']['host'] = imap_utf8($header->to[0]->host);

if (isset($header->from[0]->personal))
$mail['from']['personal'] = imap_utf8($header->from[0]->personal);
else
$mail['from']['personal'] = '';
$mail['from']['mailbox'] = imap_utf8($header->from[0]->mailbox);
$mail['from']['host'] = imap_utf8($header->from[0]->host);

$mail['maildate'] = strtotime(imap_utf8($header->MailDate));
$mail['date'] = strtotime(imap_utf8($header->date));
$mail['udate'] = imap_utf8($header->udate);
$mail['size'] = imap_utf8($header->Size);
$mail['id'] = md5($header->message_id);

Теперь все готово.

Комментарии запрещены.