16+
ComputerPrice
НА ГЛАВНУЮ СТАТЬИ НОВОСТИ О НАС




Яндекс цитирования


Версия для печати

Модуль поиска не установлен.

Программирование электронной почты. Часть 2

28.01.2004

Павел Кондратьев

Намучившись с многочисленными версиями и интерфейсами от Microsoft, лично я выбрал принципиально другой вариант программирования почты. Я говорю о программировании winsock. В этом случае вы не зависите ни от системных требований, ни от почтовых агентов, ни от версий, но платите за это тем, что очень многое приходится реализовывать вручную. С другой стороны, вы не зависите от функциональных ограничений почтовой подсистемы и можете реализовывать именно то, что вам нужно. Компонент winsock можно использовать, программируя на Си, Visual Basic, Delphi и т. п.

Если не MAPI

Winsock представляет собой COM-компонент, позволяющий использующим его приложениям реализовать обмен данными через TCP/IP-канал. Разработчику остается лишь запрограммировать протокол самого верхнего уровня: SMTP, POP3 (E-Mail), FTP, HTTP (WWW) или NNTP (News). Windows Sockets или Winsock - технология, при которой приложение создает специально программное устройство сокет ("гнездо"), обеспечивающее прием и передачу данных, через определенный TCP/IP-порт, например, FTP-сервер должен использовать TCP/IP-порт 21, а Web-сервер - порт с номером 80. Для подобных, широко известных, сервисов Интернета существует "глобальная договоренность" о нумерации портов по умолчанию, для того чтобы многочисленные клиенты знали, куда именно адресовать свои запросы (см. табл.).

Обмен данными через winsock происходит с помощью методов SendData и GetData. Итак, предположим, мы подключили к нашей программе компонент winsock. Первым делом нужно установить соединение с сервером, с которым мы хотим обмениваться данными. Для этого вызываем метод connect и указываем IP-адрес сервера и номер порта для выбранного протокола (далее будет код на Visual Basic):

Winsock1.Connect "smtp.mymail.ru", 25

В ответ на данный запрос winsock генерирует событие Connect. Далее, в зависимости от успешности наших действий:

- либо сервер посылает приветственное сообщение с кодом отклика, сообщающим, что сервер готов к приему ваших инструкций;

- либо сервер посылает сообщение с кодом отклика, сообщающим, что сервер по каким-либо причинам не может поддерживать соединение и разрывает соединение.

В ответ на любой запрос сервер может посылать нам какие-либо данные, и об их прибытии Winsock Control извещает нас событием DataArrival. В теле обработки этого события можно поместить и код, извлекающий, при помощи метода GetData, данные из буфера Windows Sockets:

Dim strData As String
Winsock1.GetData strData

Последняя строка кода извлекает данные из буфера в переменную strData, и далее вы можете обрабатывать эти данные, как вам вздумается. Теперь рассмотрим, как таким образом реализовать SMTP-протокол для отправки письма. SMTP-протокол предполагает примерно следующий обмен (клиент - "К", сервер - "С"):

Первым делом называем себя:

К: HELO PAVEL_K
С: 250 mail.myfirm.ru Hello george.myfirm.ru [195.239.137.25] , pleased to meet you

Если все нормально, сервер возвращает 250 и далее приветствие (то, что идет после кода отклика, может различаться для различных серверов)

Указываем наш адрес:

К: MAIL FROM: pavel_k@myfirm.ru
С: 250 2.1.0 pasha@myfirm.ru ... Sender ok

Указываем получателя:

К: RCPT TO: masha@mail.ru
С: 250 2.1.5 masha@mail.ru ... Recipient ok

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

Далее указываем, что сейчас пойдет содержимое письма:

К: DATA
С: 354 Enter mail, and with "." On a line by it self

Сервер готов принимать данные.

В первой строке указываем тему письма после ключевого слова "Subject:" и далее вводим сам текст, после чего перевод строки + точка + перевод строки:

К: Subject: Hi
Privet! Kak dela ?
С: 250 2.0.0 hA273636A12088 Message accepted by delivery

Сообщение успешно отправлено.

Есть несколько основных правил для протоколов SMTP, POP3, FTP и т.п., которых вы обязаны придерживаться при разработке программ:

Команды могут быть набраны в любом регистре клавиатуры, то есть большими или маленькими буквами.

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

Командная строка (команда + параметр или просто одна команда) всегда должна завершаться символом новой строки и возврата каретки. В VB вы можете использовать специальную константу - vbCrLf.

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

Напишем вспомогательную процедуру, которая будет отсылать команды и возвращать отклик с сервера. Для этого создадим невидимую форму, на которой разместим компоненты winsock и таймер и объявим строковую переменную strResponse и переменную TimerCount. В обработчик события DataArrival напишем:

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Winsock1.GetData strResponse
End Sub:

В обработчик таймера пишем:

Private Sub Timer1_Timer()
TimerCount = TimerCount + 1
End Sub

И собственно код процедуры:

Public Function SendCommand(Command As String, Optional Response As String) As String
On Error Resume Next
Winsock1.SendData Command & vbCrLf
strResponse=""
TimerCount = 0
While strResponse="" And TimerCount < Max
DoEvents
Wend
Response = strResponse
If TimerCount < Max Then
If Len(Response) >= 3 Then SendCommand = Left(Response, 3)
End If
End Function
SendCommand через Winsock1.SendData отсылает команду серверу и ждет, пока не поступит отклик через событие Winsock1_DataArrival, либо пока не истечет время ожидания отклика (которое мы регулируем константой MAX и интервалом таймера), это будет означать, что сервер не отвечает из-за каких-либо технических сбоев.

Напишем также процедуру для установки соединения с нашим почтовым сервером. Она будет отличаться от SendCommand лишь одной строкой:

Public Function Logon (Server As String, Port as integer, Optional Response As String) As String
On Error Resume Next

Winsock1. Connect Server, Port
strResponse=""
TimerCount = 0
While strResponse="" And TimerCount < Max
DoEvents
Wend
Response = strResponse
If TimerCount < Max Then
If Len(Response) >= 3 Then Logon = Left(Response, 3)
End If
End Function

А теперь в процедуре Send просто реализуем протокол SMTP для отправки письма:

Private Sub Send(Server as string, Account as string, FromAddress as string, ToAddress as string, Subj as string , Text as string)

'для отправки используем порт 25
If Logon(Server, 25) <> "220" Then
MsgBox "Ошибка соединения с сервером "
Exit sub
End If
If SendCommand("HELO " & Trim$(Account))<>"250" then
MsgBox "Ошибка при авторизации"
Exit sub
End If
If SendCommand("MAIL FROM: " & Trim$(FromAddress))<>"250" then
MsgBox "Ошибка при выборе отправителя "
Exit sub
End If
If SendCommand("RCPT TO: " & Trim$(ToAddress))<>"250" then
MsgBox "Ошибка при выборе получателя "
Exit sub
End If
If SendCommand("DATA")<>"354" then
MsgBox "Ошибка данных "
Exit sub
End If
If SendCommand("Subject: " & Subj & vbCrLf & Text & vbCrLf & "." & vbCrLf )<>"250" then
MsgBox "Ошибка отправки"
Exit sub
End If
Winsock1.close
End Sub

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

Во-первых, в самом тексте письма не должно быть последовательности vbCrLf & "." & vbCrLf, иначе сервер воспримет ее как конец сообщения, и все, что в письме следовало дальше, отсечется. Поэтому перед отправкой в теле письма рекомендуется заменять все vbLf & "." на vbLf & "..", а также разбивать сообщение на отдельные строки (в бэйсике это можно сделать с помощью функции Split(strMessage, vbCrLf)) и отсылать через WSock.SendData отдельно каждую строку + vbCrLf.

Во-вторых, мы не рассмотрели случай, когда сервер требует авторизации. Иногда администраторы настраивают почтовый сервер так, что перед отправкой письма необходимо ввести пароль, дабы предотвратить несанкционированный доступ к адресу. В этом случае перед командой MAIL FROM: необходимо выполнить еще одну команду - AUTH из расширенного SMTP, в противном случае сервер в ответ на MAIL FROM: вернет отклик ошибки авторизации. К сожалению, с поддержкой авторизации дело обстоит не так просто, потому что разные почтовые сервера поддерживают разные типы авторизации. К счастью, узнать, какой вид авторизации требует от нас сервер, очень просто - по отклику сервера на команду приветствия EHLO (HELO в расширенном SMTP), например:

Клиент:
Ehlo PASHA
Сервер:
250-localhost.locdomain Hello pasha.myfirm.ru [128.1.6.39], pleased to meet you
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-SIZE
250-DSN
250-XUSR
250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN

В последней строке сервер просто перечисляет поддерживаемые типы авторизации. Чтобы пройти авторизацию, достаточно выполнить любую из перечисленных в отклике. Самые простые, с точки зрения реализации, - это LOGIN и PLAIN, однако они и самые незащищенные с точки зрения безопасности. Итак, мы проанализировали строку отклика (например, нашли подстроку "LOGIN" функцией InStr) и определили, что сервер поддерживает LOGIN. Вводим команду AUTH LOGIN и ждем отклика 334:

If SendCommand("AUTH LOGIN")<>"334" then
MsgBox "Авторизация LOGIN не поддерживается!"
Exit sub
End If

Далее, авторизация LOGIN требует имя учетной записи и пароль в кодировке base64. Тут ничего не поделаешь, придется отыскать описание этого алгоритма или готовую функцию перекодировки. Кодировка base64 активно используется в почтовых протоколах, и она нам все равно далее понадобится, даже если вам и не требуется авторизация. Суть ее в том, что все байты строки (в том числе и неотображаемые символы) преобразуются в последовательность ASCII-символов A-z, 0-9,+,/, которых всего 64, при этом один байт исходной строки кодируется в три из base64. Это негативно сказывается на размере сообщения, но позволяет передавать любые данные (в том числе бинарные файлы) в интернет-протоколах, поддерживающих лишь консольный ввод (ASCII-символы). Код для функции перекодировки ToBase64 приводить здесь, пожалуй, не стоит, при желании его легко можно найти в Интернете. Итак, в качестве логина отсылаем имя учетной записи:

If SendCommand(ToBase64(Account))<>"334" then
MsgBox "Доступ " & Account & "закрыт"
Exit sub
End If

И пароль для авторизации (обычно он совпадает с паролем для доступа к почтовому ящику):

If SendCommand(ToBase64(Password))<>"235" then
MsgBox "Неправильный пароль" & Account
Exit sub End If

Ну а далее снова идет команда MAIL FROM:.

PLAIN - авторизация отличается лишь тем, что и логин, и пароль отсылаются в одной строке, разделенные нулевым символом:

"AUTH PLAIN " & ToBase64 (vbNullChar & Trim$(Account) & vbNullChar & Password) & vbCrLf

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

Теперь хотелось бы научиться прикреплять к письму файлы. Содержимое прикрепляемых файлов необходимо как-то разместить в теле письма (тело письма - это все, что идет после SMTP-команды DATA), и еще нужно как-то указать названия этих файлов. Для этого, во-первых, необходимо перекодировать каждый байт прикрепляемого файла в набор ASCII-символов, иначе мы просто не сможем передать нетекстовые файлы. Для этого, как вы уже догадались, отлично подходит алгоритм base64. Во-вторых, отделить содержимое файлов друг от друга и от текста письма. Для этих целей были разработаны UU-кодирование и стандарт MIME. UU-кодирование появилось раньше и просто позволяет прикрепить к письму файлы, при этом используется не алгоритм base64, а алгоритм UU, отличающийся от base64 другим набором выходных символов. MIME - это более поздний стандарт, регламентирующий, как передавать в письме разнородные данные, например картинки, звуковые файлы, другие данные, и их интерпретировать. MIME расшифровывается как "Multipurpose Internet Mail Extensions" (Многоцелевые расширения почтового стандарта Internet). Конечно, лучше всего научиться работать с MIME. Но, на всякий случай, расскажу вкратце и о UUEncode. UU-кодирование выполняется так: после текста письма и перевода строки ставится маркер вида:

begin XXX filename, где XXX - трехзначное число (обычно 664), а filename задает имя файла. Далее идет, собственно, содержимое файла в UU-кодировке. В конце данных ставится перевод строки, символ ' , перевод строки и End. Вот и все. При чтении такого письма выполняется обратный разбор - все, что заключено между маркерами "Begin 664" и ' & vbLf & End, перекодируется и помещается в файл с названием, указанным после "Begin 664". Все современные почтовые агенты "понимают" письма с прикрепленными файлами в таком формате. Описание UU-алгоритма можно найти в Интернете.

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

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----------1121D2C9B0C39C1"
Вторая строчка указывает на тип письма, в данном случае - это multipart/mixed, то есть письмо, содержащее разнородные данные. Если же письмо состоит только из одного блока данных, то здесь будет указан тип этих данных, например: text/plain - для простого текста, image/jpeg - для рисунка в формате jpeg или application/octet-stream - для обычного прикрепленного файла. Параметр boundary содержит маркер-разделитель, который будет использован для отделения блоков данных. Вот пример MIME-письма:
From: "sva" <sva@balt.ru>
To: "KPE" <kpe@balt.ru>
Subject: Shema
Date: Wed, 19 Nov 2003 13:12:59 +0300
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="NextPart_000_004F_01C3AE9E.DACA9080"
This is a multi-part message in MIME format.
--NextPart_000_004F_01C3AE9E.DACA9080
Content-Type: text/plain; charset=Windows-1251
Content-Transfer-Encoding: 8bit
Привет! Вот схема проезда, которую ты просил.
--NextPart_000_004F_01C3AE9E.DACA9080
Content-Type: image/jpeg;
name="002.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="002.jpg"

/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4AIUFkb2JlAGTAAAAAAQMAEAMCAwYAAArD
AAAhfwAATWv2wCEABALCwsMCxAMDBAXDw
0PFxsUEBAUGx8XFxcXFx8eFxoaGhoXHh4
jJSclIx4vLzMzLy9AQEBAQEBAQEBAQEBAQEABE
Q8PERMRFRISFRQRFBEUGhQWFhQaJhoaHBoa
JjAjHh4eHiMwKy4nJycuKzU1MDA1NUBAP0BAQE
BAQEBAQEBAQP/CABEIASQBwgMBIgACEQEDEQH/xADJAAABBQEBAAAAAAAAAAAAAAADAQIEBQYABw
EBAQEBAAAAAAAAAAAAAAAAAAECAxAAAgICAQ
MCBAUEAgMBAQAAAgMBBAAFERITBiEUECAxIjBBMiMVQEI
zFlAkQzQlNQcRAAEEAAMFBQQIBQEFBwUBAAEAEQ
IDITESQVEiEwQQYXGBMpGhsUIgwVJiciMUBfDR4Y
IzJDDxQ1NjkqKyg6M0RMLS4nMVBhIAAQMEAQQDA
AAAAAAAAAAAAQARQRAhMQJAgFFhoTBwkf/aAAwDAQACEQMRAAAAj2AQNWKjWCIxAqjQ
kkjFDK3hysUIaOYL0THVveGSHqxaMeLIg7mOOaq
DWPaN5EHMVojUaKrOCjWrq1LS248LwGPc3hIx4pa
cJAA1FUh4lgqi4n83odXyYFXrhrDuZw4mb1
lOXQwYrkPEp6xJkKQ0opJdqpWksWEF7H088Q8Sn
DedyccwjBrHsEarRg3CoMnE6Mtcrp8cP2OF24SO
HNjeYtJElwC0YrCOLhkpRqPcF5P5nSw+Ae5muobCWwZVz
TL6Cfq0ZArc6bqtBOWVYVhInjisJ5qpS2JSSS1HBlBo0viOdYp
L6DKp6VWYNymAhp6S3yuNXq0PzvVSxo2rzpGq
hRLmxsc4Us9Dit8tW0aKsGTCLUfCAMVqSuGsqvAUm8
zqrlnw0WfR2Z1xm689BHgkNfGzV8W9jTyJbAU
IZIbFEWMTO1dm1dk4JvZGAszc2WbNLrB09rDByIxVx
7ivqbwIkecFa7WZ2qyOnW9z2gz8uchzIdy22q/TVorZ8nNxj1Zo2FKjJPYUSxWqNJHCecYDiZzOWg
c5rN3Rc+musZEQ26iSuBt5FKmomVM9oySRxX5Q8e5
HuMl6YoPMavN5cs6Ums9rcbay6eSNubdFzskldECX0My
V5eN8a5sEEh0dW0ioom1xcuNs7K30tcPmWtjljJax1Coht
VClAQ5zHhud0VLFbqdycSdFlLaNuN0SL1+bIc59DjVoz
Z8V/o+f8Ac1tp/mque/p/Lfd/JDx4XUub69nh3O
sO59OajzG5jHLPzWHvUt2ofBBvJHV7liuDLa/8Zoac3WGnUw1Nv2suPyfJF+0OhvXmUWzW3Pu9S//Z
--NextPart_000_004F_01C3AE9E.DACA9080--

В данном примере всего два блока: первый - это текст (Content-Type: text/plain) в кодировке Windows-1251 (charset=Windows-1251) и восьмибитном представлении (Content-Transfer-Encoding: 8bit), а второй блок - рисунок (Content-Type: image/jpeg) с названием name="002.jpg", закодированный для передачи с помощью алгоритма base64 (Content-Transfer-Encoding: base64). Данные, закодированные в base64, обычно делятся на строки по 76 символов (эту разбивку должен выполнять сам алгоритм перекодировки). Блоки данных разделяются маркером NextPart_000_004F_01C3AE9E.DACA9080, перед которым добавляются два символа "-", а в конце последнего маркера еще и два справа, для обозначения конца письма. Конечно, для полноценной реализации MIME необходимо изучить спецификацию RFC1521, однако для простой поддержки вложений достаточно сформировать блоки text/plain для простого текста и application/octet-stream для вложенных файлов. Итак, вот что должна сделать наша программа. Формируем заголовок и определяем маркер-разделитель:

MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="MyBoundary"

После двух переводов строки размещаем блок, содержащий текст письма (если он есть):

-- MyBoundary
Content-Type: text/plain; charset=Windows-1251
Content-Transfer-Encoding: 8bit
"Текст письма"

Далее вставляем блоки, соответствующие вложениям. Каждый блок начинаем с перевода строки и маркера:

-- MyBoundary
Content-Type: application/octet-stream; name="File1"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="File1"
АААААА (данные в base64)

Вместо File1, естественно, подставляете нужные названия. И далее перевод строки и, собственно, содержимое файла в base64. Потом следующий блок или же --MyBoundary-- (конец письма).

Сформировав такую строку, добавляем в ее начало subject: "ваша тема" + перевод строки и отправляем все это после SMTP-команды DATA. Такое письмо соответствует MIME-стандарту, и любой почтовый агент должен его правильно интерпретировать.

Если вы хотите запрограммировать чтение писем, придется выполнять синтаксический разбор полученного письма, выделять блоки по маркеру и перекодировать данные. Для работы с MIME-письмами очень удобно использовать классы - было бы вполне естественно определить класс entity, реализующий MIME-блок, свойствами которого будут такие параметры, как тип (Content-Type), кодировка (Content-Transfer-Encoding), набор символов (charset), имя вложения и т.п. (см. спецификацию MIME), а само письмо реализовывать коллекцией объектов такого типа и заголовком. Главный метод такого класса, по-видимому, должен будет восстанавливать свойства MIME-блока из соответствующего фрагмента письма, расположенного между маркерами.

Итак, о чтении писем.

Для чтения писем с почтового сервера используется протокол POP3. Как и в случае отправки, клиент отсылает серверу команды и анализирует коды отклика. Естественно, для программного выполнения такого обмена подойдут приведенные ранее процедуры.

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

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Winsock1.GetData strResponse
sData = sData & strResponse
End Sub

И определим функию GetData, собирающую блоки письма воедино:

Function GetData(Command As String, Response As String) As String
sData = ""
strReadResponse = ""
Winsock1.SendData Command & vbCrLf
TimerCount = 0
Response = ""
'Ждем, пока не придет последний блок, содержащий либо vbCrLf & ".", либо "-ERR"
While InStr(1, strResponse, vbCrLf & ".") = 0 _
And InStr(1, strResponse, vbCrLf & "-ERR") = 0 _
And TimerCount < MAX
DoEvents
Wend
If Left(strResponse, 4) = "-ERR" Then Exit Function
If TimerCount < MAX Then
If Len(sData) >= 3 Then GetData = Left(sData, 3)
Response = sData
End If
End Function

В модуле формы необходимо объявить переменную sData. SendCommand точно так же будет отсылать команды и возвращать код отклика, а Logon позволит установить соединение, только на этот раз через 110-й порт. Для POP3 успешный отклик сервера начинается с "+OK":

Public TimerCount As Integer
Public strResponse As String
Public sData As String
Private Sub Read(Server as string,Account as string, Pass as string, Del as boolean)
If Logon(Server, 110) <> "+OK" Then
MsgBox "Ошибка соединения с сервером "
Exit sub
End If

Далее сообщаем серверу имя учетной записи и пароль:

If SendCommand("USER:" & Trim$ (Account)) <>"+OK" then
MsgBox "Пользователь неизвестен"
Exit sub
End If
If SendCommand("PASS:" & Pass)<>"+OK" then
MsgBox "Ошибка авторизации"
Exit sub
End If
Dim strBuffer As String

А теперь получаем информацию о количестве новых писем в нашем ящике с помощью команды STAT:

If SendCommand("STAT" & Trim$(ToAddress), strBuffer)<>"+OK" then
MsgBox "Ошибка чтения"
Exit sub
End If

В strBuffer возвращаются данные о количестве писем и их общем объеме, к примеру "+OK 3 7564"

Вот так определим количество писем:

intMessages = CInt(Mid$( strBuffer, 5, InStr(5, strBuffer, " ") - 5))
If intMessages = 0 Then
Писем нет
Exit sub
Else
Dim intMessages As Integer
'Перебираем все полученные письма:
While intCurrentMessage < intMessages
'порядковый номер письма
IntCurrentMessage = intCurrentMessage + 1
'Получаем письмо: шлем RETR "номер письма", и в ответ на эту 'команду сервер возвращает все содержимое письма
If GetData("RETR " & intCurrentMessage, strBuffer) <>"+OK" then
MsgBox "Ошибка чтения"
Exit sub
End if
'В strBuffer наше письмо. Делаем с ним, что нужно
Debug.print strBuffer
If Del Then
'Удаляем прочитанное письмо с сервера, если Del=true
SendCommand("DELE " & CStr(intCurrentMessage))
End If
Wend
End If
End sub

Команда DELE "номер письма" удалит письмо с сервера и, если вы его нигде не сохраните, прочитать его уже будет невозможно.

Полученное содержимое письма (в строке strBuffer) хорошо бы как-то обработать, для того чтобы отделить служебные заголовки от текста письма. Кроме того, если в письме содержатся вложенные файлы, их необходимо отделить от сообщения и создать на локальном диске. Об обработке присоединенных файлов говорилось ранее, процедура сводится к определению формата письма (UU или MIME) и определению блоков, расположенных между маркерами-разделителями. Кроме того, нужен алгоритм перекодировки base64 и (или) UU. А дальше все зависит от ваших потребностей - будете ли вы определять тип вложений и как будете их интерпретировать, в какой кодировке будете выводить текст, будете ли использовать служебную информацию из заголовка письма и т.д. В заголовке письма содержатся основные параметры письма, такие как тип письма, отправитель, тема, прохождение письма, дата создания, дата получения, почтовый агент, в котором письмо создано и т.п. Все эти данные представлены в виде списка параметров и их значений:

Параметр1: значение1
Параметр2: значение2
и т.д.

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

Пример:

Return-Path: nobody@mail.mp3zzz.ru - путь следования письма
Received: from mail.mp3zzz.ru ([81.176.66.62]) - сервер отправителя
To: kpe@balt.ru - получатель
Subject: MP3ZZZ.RU: Registration complete! - тема
From:"nobody@mp3zzz.ru <nobody@ mp3zzz.ru> - отправитель
MIME-Version": "1.0 - тип письма
Content-Type: text/html; charset=\"windows-1251\" - тип данных
Content-Transfer-Encoding: 8bit - кодировка
Reply-To: nobody@mp3zzz.ru "<nobody@ mp3zzz.ru> - обратный адрес для ответа
X-Mailer: PHP/4.3.3 - почтовый агент отправителя (программа)
Message-Id: E1AODa7-000FJ5-5x@mail.mp3zzz.ru - идентификатор сообщения
Sender: Unprivileged user nobody@ mail.mp3zzz.ru - имя отправителя
Date: Mon, 24 Nov 2003 16:04:35 +0600- дата отправки
X-UIDL: fP["!KOT"!i;N!!5e##! - служебный параметр

Дополнительные параметры, не являющиеся стандартом, начинаются с префикса "X-". Их используют для своих целей почтовые агенты, следовательно, можете использовать и вы.

Программирование электронной почты. Часть 1
Программирование электронной почты. Часть 3



статьи
статьи
 / 
новости
новости
 / 
контакты
контакты