Вот уже больше двух лет прошло с того момента, как я затрагивал в последний раз работу с POP3 в Synapse. Сам-то я к теме работы с почтой отношение имею очень отдаленное и разбирался с TPOP3Send исключительно ради интереса. Однако, тема эта в блоге в последнее время набрала несколько бОльшую популярность нежели я ожидал и даже появились просьбы читателей и посетителей блога о продолжении темы — рассмотреть работу в Synapse с вложениями в письмах. Что ж, спустя два года, возвращаемся к теме работы с почтой в Synapse и посмотрим на работу с аттачментами (attachments) в письмах- посмотрим как определять наличие вложений, их тип, как сочранять их и т.д.
Для того, чтобы лишний раз не повторяться, приведу ссылки на предыдущие посты по работе с TPOP3Send, их всего два: «Synapse. TPOP3Send. Подготовительные работы» и «Synapse TPOP3Send. Обработка писем«.
Введение
Помня о предыдущих дискуссиях относительно работы с почтой вообще, перед началом работы позволю себе напомнить Вам один момент, о котором не стоит забывать, когда Вы начнете сами работать с почтой в Synapse:
Почему я обращаю ваше внимание на этот момент? Все довольно просто. Каждый почтовый сервер может быть настроен по-своему — один работает через защищенные соединения, другой нет, один сервер принимает zip-архивы, второй — нет, один ограничивает объем вложений в письме, второй — нет и т.д. и т.п. Исходя из этого, я не ставлю перед собой такой цели как написать в одной статье почтовую программу по-круче The Bat — только показать подход к работе с почтой и все. А для этого работы с одним популярным почтовым сервером вполне достаточно.
Теперь, покончив с этой маленькой формальностью, приступим к работе. Для работы с почтовыми сообщениями нам потребуется три класса:
- TPOP3Send — для отправки запросов и получения ответов от сервера
- TMimeMess — для работы с почтовым сообщением
- TMimePart — для работы с частями почтового сообщения.
Соответственно, в работе буду использоваться три модуля: pop3send.pas, mimemess.pas и mimepart.pas. Напишем небольшое приложение, которое будет выполнять следующие функции:
- Авторизовываться и получать количество писем на сервере
- Загружать с сервера письмо и получать информацию по нему (количество частей, заголовки и т.д.)
- Читать список вложений и сохранять их на диск
Пройдемся по всем пунктам функций нашей будущей программы по порядку.
1. Авторизация на сервере и получение информации о письмах
Этот пункт достаточно подробно рассматривался в первой публикации на тему TPOP3Send, поэтому здесь я приведу только готовый код (с небольшими комментариями) и двинемся дальше.
Вот так у меня сейчас выглядит главная форма приложения:
Клик по кнопке «Login» приводит к выполнению следующего кода:
type TForm9 = class(TForm) [...] procedure btnLoginClick(Sender: TObject); private Pop3: TPOP3Send; public { Public declarations } end; var Form9: TForm9; implementation {$R *.dfm} procedure TForm9.btnLoginClick(Sender: TObject); begin Pop3.TargetHost:=edHost.Text; Pop3.UserName:=edLogin.Text; Pop3.Password:=edPassword.Text; if Pop3.Login then begin if Pop3.Stat then lbMailCount.Caption:=IntToStr(Pop3.StatCount) else raise Exception.Create('Не удалось получить ответ сервера на команду STAT'); end else raise Exception.Create('Соединение не удалось. Проверьте логин/пароль/порт.'); end;
Т.е., если авторизовались на сервере, то сразу запросили информацию по письмам, иначе — сообщаем об ошибке. Переходим к следующему шагу.
2. Загрузка сообщения и его анализ
Эта часть работы будет для нас, видимо, самой важной и интересной. Итак, мы получили количество писем в ящике. Что дальше? Дальше мы должны, в самом простом случае, действовать следующим образом:
- Загрузить сообщение с сервера
- Декодировать сообщение, например, как это делалось для простых писем, содержащих только текст
- Проверить наличие частей сообщения (вложений, текста и т.д.)
- Обработать каждую из частей сообщения и получить необходимую нам информацию
- Если часть является вложением (файлом) — декодировать вложение и сохранить на диск.
Пройдемся по всему списку задач по порядку.
2.1. Загрузка сообщения с сервера
Объявим в секции private главной формы следующие переменные:
MailMessage: TMimeMess;
Эта переменная служит для хранения информации о почтовом сообщении. Пока не забыли, в OnCreate и OnDestroy формы пишем:
procedure TForm9.FormCreate(Sender: TObject); begin Pop3:=TPOP3Send.Create; MailMessage:=TMimeMess.Create; end; procedure TForm9.FormDestroy(Sender: TObject); begin MailMessage.Free; Pop3.Free; end;
Теперь, чтобы получить с сервера любое сообщение нам необходимо вызвать метод Retr объекта Pop3:TPop3Send, который получит всё содержимое сообщения и сохранит его в своем свойстве FullResult. Для выполнения Retr необходимо указать индекс сообщения в списке. Индексы писем на сервере мы можем получить, проанализировав все тот же FullResult после выполнения команды LIST с параметром 0. Как это делается — см. всё тот же первый пост по TPOP3Send.
Добавляем на главную форму следующие компоненты:
В ListBox, для наглядности, пока будем выводить все содержимое сообщения. Теперь пишем обработчик кнопки:
procedure TForm9.btnRetrClick(Sender: TObject); begin ListBox1.Items.Clear; if Pop3.Retr(StrToInt(edMailIndex.Text)) then begin MailMessage.Clear; MailMessage.Lines.Assign(Pop3.FullResult); ListBox1.Items.Assign(Pop3.FullResult); end; end;
Здесь мы сделали следующее: если письмо с заданным индексом было успешно загружено с сервера, то мы передаем все полученные строки из Pop3.FullResult в нашу переменную для последующей обработки. Как это будет выглядеть в работающей программе посмотрим на примере.
2.2. Анализ почтовых сообщений
Себе на ящик я отправил письмо со следующим содержимым:
При этом самый первый рисунок, прикрепленный к сообщению — это фон письма, а второй и третий файл — это уже нормальные вложения, которые я сам себе решил послать — файл Execl и картинка.
Посмотрим, что вернет нам сервер, когда мы попытаемся сказать это сообщение:
Return-path: Authentication-Results: mxs.mail.ru; spf=pass (mx132.mail.ru: domain of gmail.com designates 209.85.217.175 as permitted sender) smtp.mailfrom=any@gmail.com smtp.helo=mail-lb0-f175.google.com; dkim=pass header.i=gmail.com Received-SPF: pass (mx132.mail.ru: domain of gmail.com designates 209.85.217.175 as permitted sender) client-ip=209.85.217.175; envelope-from=any@gmail.com; helo=mail-lb0-f175.google.com; Received: from [209.85.217.175] (port=37259 helo=mail-lb0-f175.google.com) by mx132.mail.ru with esmtp (envelope-from ) id 1TeAfU-0000xh-TI for any@mail.ru; Fri, 30 Nov 2012 00:25:09 +0400 X-Mru-BL: 0:0:1119 X-Mru-PTR: off X-Mru-NR: 1 X-Mru-OF: unknown ((Google 2)) X-Mru-RC: US Received: by mail-lb0-f175.google.com with SMTP id gg13so9218558lbb.34 for ; Thu, 29 Nov 2012 12:25:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:to:subject:references:mime-version :content-type; bh=yJ2c4ft9WbyhvYpYgpEKFZ386vDz4LVBeWzZ99Nrvag=; b=fUnFNbGp0r/+piP8XxO2YwF2RYTZ FIC0t79Boeg/IlTELK+0C2pnFzkEIEg1IfZTpm8D/TGOWQC+YyRichxI77WDAMrboqJj Kr3MmXd7Rl/Z5b+Yq9x0ZF4VqjB1B/Po2wc+mxv+pC6d+5YbnJEjEGxVBBHziLma15uT +2fQ== Received: by 10.112.36.137 with SMTP id q9mr10266416lbj.42.1354220708288; Thu, 29 Nov 2012 12:25:08 -0800 (PST) Received: from Vlad-PK ([23.23.23.23]) by mx.google.com with ESMTPS id sj3sm1126331lab.2.2012.11.29.12.24.58 (version=SSLv3 cipher=OTHER); Thu, 29 Nov 2012 12:25:04 -0800 (PST) Message-ID: Date: Fri, 30 Nov 2012 03:24:57 +0700 From: "Vlad" To: =?utf-8?B?ItCS0LvQsNC00LjRgdC70LDQsiDQktC40LrRgtC+0YDQvtCy0LjRhyDQkdCw0LbQtdC90L7QsiI=?= Subject: =?utf-8?B?0J/QuNGB0YzQvNC+INGBINCy0LvQvtC20LXQvdC40Y/QvNC4?= References: EDSK2012113003232600000057 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=NextPart_2012113003232600000058" X-Spam: Not detected X-Mras: Ok X-Mru-Authenticated-Sender: any@gmail.com --=NextPart_2012113003232600000058 MIME-Version: 1.0 Content-Type: multipart/related; type="multipart/alternative"; boundary="----=_NextPart_000_0000_38A0777C.7CA6C970" ------=_NextPart_000_0000_38A0777C.7CA6C970 Content-Type: multipart/alternative; boundary="----=_NextPart_001_0000_46AC7537.378F2A18" ------=_NextPart_001_0000_46AC7537.378F2A18 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 0K3RgtC+INC/0LjRgdGM0LzQviDRgdC+0LTQtdGA0LbQuNGCINCy0LvQvtC20LXQvdC40Y86INGE 0LDQudC7IEV4ZWNsINC4INC60LDRgNGC0LjQvdC60YM= ------=_NextPart_001_0000_46AC7537.378F2A18 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable [много текста+HTML] ------=_NextPart_001_0000_46AC7537.378F2A18-- ------=_NextPart_000_0000_38A0777C.7CA6C970 Content-Type: image/jpeg Content-Transfer-Encoding: base64 Content-ID: /9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/b AIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERER [много-много букв] FBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU FBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgCWAMgAwERAAIRAQMRAf/EAF8AAQEBAQEAAAAAAAAAAAAA AAABAgMIAQEBAQEBAAAAAAAAAAAAAAAAAQIEAxABAQEBAAMBAAMBAAAAAAAAAAERAiExEkFRYXGR gAAAAAAAqKAogCooAAACKGgaIaCaBoGgaKaAqIAAAAAACgCoIAAAaCaCWqiaqpaIzaqpaIzaDN6U S9KM6DNqolqjOgn0CaBqiaBoGgaBoGgAaC6BoKABoLqDWoLKKaDWsjUoLKC6CxFaBqXEF0GpUF2I qjJoqCioIICygagKIABoAGgaAACqAP/Z ------=_NextPart_000_0000_38A0777C.7CA6C970-- --=NextPart_2012113003232600000058 Content-Type: application/vnd.ms-excel; name==?utf-8?B?0L/RgNC40LzQtdGAICgyKS54bHM=?= Content-Disposition: attachment; filename==?utf-8?B?0L/RgNC40LzQtdGAICgyKS54bHM=?= Content-Transfer-Encoding: base64 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAACAAAA6wAAAAAAAAAA IHJlcXVpcmVkIHRvIHVzZSB0aGUgcHJvZ3JhbS4OAABzdHJOZXdEb2N1bWVudAwAAE5ldyBEb2N1 IABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW [много-много букв] AAAAAAAAAAAAAAAATbEBAAAAAAAFAFMAdQBtAG0AYQByAHkASQBuAGYAbwByAG0AYQB0AGkAbwBu AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAACAQEAAAADAAAA/////wAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAANkAAAAAEAAAAAAAAAUARABvAGMAdQBtAGUAbgB0AFMAdQBt AG0AYQByAHkASQBuAGYAbwByAG0AYQB0AGkAbwBuAAAAAAAAAAAAAAA4AAIB//////////////// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QAAAAAQAAAAAAAA --=NextPart_2012113003232600000058 Content-Type: image/png; name="with_error (2).PNG" Content-Disposition: attachment; filename="with_error (2).PNG" Content-Transfer-Encoding: base64 iVBORw0KGgoAAAANSUhEUgAAAocAAAIECAYAAABrMISUAAAAAXNSR0IArs4c6QAAAARnQU1BAACx jwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAP+lSURBVHhe7P096HVbdt6JVuDAQQcVOKigAwsc [много-много букв] SlERpvSBUiNnyDtVQwojR/5Sq9GoHAKkodpHf2dfRPCDvzFETHmyHeKuvFfZFhekTmUwz7PyBffY g5xCqY7WMdzOmoBgnFuPZ0jzgPHdCmUb74rQrfkjc/wR8JyDC9WoDAAzMvIYfsQ50DjGI3c8Q7Jw Xhzxy7DuoVDkbuUSoxYqqJ0ZQAEOQsfKHXpPRd0jpxzsUz6/DiHPY+7ze7VHoAe2a+37aD/ifIci iM8n21H7cbKn0Kc+gsPLk8KOa0Jbw9jKItiwEOVQcvDE4zgbIPJzeWiYq6ChURfaHXAG0ESQGZ+z --=NextPart_2012113003232600000058--
Что из себя представляет формате multipart/form-data мы уже в знаем ещё с момента работы с THTTPSend. Теперь, достаточно взглянуть на код почтового сообщения выше и увидеть очень много похожих вещей. В сообщении определены границы (boundary), которыми отделяется каждая часть сообщения. Для каждой части совершенно однозначно определен тип контента и способ его кодировки и при этом используются все те же заголовки, которые мы активно использовали при работе с HTTP. Посмотрев на сообщение ещё внимательнее, можно сказать, что
Например, часть с текстовым содержимым содержит в себе и картинку (фон).
Как разобрать письмо по частям и проанализировать каждую? Вариантов опять же масса, но рассмотрим мы такой — воспользуемся событиями у TMimePart, а точнее одним событием
property OnWalkPart: THookWalkPart read FOnWalkPart write FOnWalkPart;
Это событие срабатывает каждый раз, когда мы начинаем «гулять» по какой-либо части сообщения. THookWalkPart имеет следующее описание:
THookWalkPart = procedure(const Sender: TMimePart) of object;
Для наглядности положим на форму ещё один ListBox и TreeView, чтобы форма стала выглядеть вот так:
И напишем такой обработчик события OnWalkPart:
procedure TForm9.Walk(const Sender: TMimePart); function GetLastNodeOnLevel(ALevel: integer): TTreeNode; var I: integer; begin with MessageTree do for I := 0 to Items.Count - 1 do begin if Items[I].Level = ALevel then if (pos('MessagePart Level#', Items[I].Text) > 0) or (pos('Root', Items[I].Text) > 0) then Result := Items[I].Parent.GetLastChild; end; end; procedure ProcessPart(Node: TTreeNode; Part: TMimePart); begin with MessageTree do begin Items.AddChild(Node, 'SubLevel: ' + IntToStr(Part.SubLevel)); Items.AddChild(Node, 'Encoding: ' + Part.Encoding); Items.AddChild(Node, 'Charset: ' + Part.Charset); Items.AddChild(Node, 'Primaty: ' + Part.Primary); Items.AddChild(Node, 'Secondary: ' + Part.Secondary); Items.AddChild(Node, 'Disposition: ' + Part.Disposition); Items.AddChild(Node, 'ContentID: ' + Part.ContentID); Items.AddChild(Node, 'Boundary: ' + Part.Boundary); Items.AddChild(Node, 'FileName: ' + Part.FileName); Items.AddChild(Node, 'SubParts: ' + IntToStr(Part.GetSubPartCount)); end; end; begin Sender.DecodePart; if Sender.SubLevel = 0 then MessageTree.Items.AddChildFirst(MessageTree.Items[0], 'Root Part') else ProcessPart(MessageTree.Items.AddChild(GetLastNodeOnLevel(Sender.SubLevel), 'MessagePart Level#' + IntToStr(Sender.SubLevel)), Sender); WalkList.Items.Add(IntToStr(Sender.SubLevel)); end;
Рассмотрим этот обработчик подробнее.
Вначале мы разбираем (декодируем) полученную часть, для чего вызываем метод DecodePart.
Sender.DecodePart;
Далее идёт проверка значения свойства SubLevel. SubLevel — это уровень вложенности части сообщения. Как я уже говорил ранее — любая часть сообщения может иметь различно количество подчастей с различным уровнем вложенности. У сообщения всегда есть одна часть с SubLevel = 0 и может быть неограниченное количество частей с SubLevel = 1,2,3 и т.д. Вот мы и проверяем — если SubLevel = 0, то мы находимся в корне сообщения и соответственно, добавляем в дерево узел с текстом «Root Part», если нет, то выполняем следующие действия:
Ищем самый последний узел в дереве с уровнем вложенности равным SubLevel:
GetLastNodeOnLevel(Sender.SubLevel)
Далее добавляем новый дочерний узел к найденному, указав при этом SubLevel части в названии узла:
MessageTree.Items.AddChild(GetLastNodeOnLevel(Sender.SubLevel),'MessagePart Level#' + IntToStr(Sender.SubLevel))
Заполняем данные по полученной части:
ProcessPart(MessageTree.Items.AddChild(GetLastNodeOnLevel(Sender.SubLevel), 'MessagePart Level#' + IntToStr(Sender.SubLevel)), Sender);
Методы GetLastNodeOnLevel и ProcessNode описаны выше и подробно рассматривать мы их не будем. В заключении мы выводим во второй ListBox значение SubLevel части с которой работали, чтобы наглядно увидеть как происходит обход дерева сообщения.
Теперь, когда у нас есть обработчик «прогулки» по сообщению, можно дописать обработчик кнопки получения сообщения. Весь код обработчика будет таким:
procedure TForm9.btnRetrClick(Sender: TObject); begin ListBox1.Items.Clear; WalkList.Items.Clear; if Pop3.Retr(StrToInt(edMailIndex.Text)) then begin ListBox1.Items.Assign(Pop3.FullResult); MailMessage.Clear;//чистим от предыдущей информации MailMessage.Lines.Assign(Pop3.FullResult);//записываем сообщение MailMessage.DecodeMessage;//декодируем MailMessage.MessagePart.OnWalkPart := Walk;//назначаем обработчик MailMessage.MessagePart.WalkPart;//"гуляем" :) end; end;
Здесь мы стартуем, как и необходимо, с самой первой части сообщения (с корня). Вот, например, какой результат мы получим, если попробуем обойти части сообщения код которого я приводил выше:
А казалось бы — отправил всего лишь пару строк текста с фоновой картинкой и два файла. А получил (вместе с корнем) восемь частей сообщения.
Теперь посмотрим, что мы получили в итоге после прохождения по всем частям сообщения.
Encoding — тип кодирования части. Часть сообщения может кодироваться как Base64, Quoted-Printable и т.д. Это свойство записывается на основании значения в свойстве EncodingCode, которое может принимать одно из следующих значений:
TMimeEncoding = (ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE, ME_BASE64, ME_UU, ME_XX)
Charset — кодировка текста (UTF-8, WINDOWS-1251 и т.д.).
Primary — Mime-тип части. Записывается на основании значения в свойстве PrimaryCode, которое может принимать следующие значения:
TMimePrimary = (MP_TEXT, MP_MULTIPART, MP_MESSAGE, MP_BINARY);
Secondary — значение Type из заголовка Content-Type части. Например, в моем письме была часть с таким заголовком:
Content-Type: multipart/related; type="multipart/alternative";
В этом случае Primary=»multipart», а Secondary = «alternative».
Disposition — расположение части в сообщении. Может принимать значения «ATTACHMENT», «INLINE» или содержать пустую строку.
ContentID — идентификатор части. Может отсутствовать в заголовках.
Boundary — разделитель, который используется в части для разделения своих подчастей. Соответственно, если подчастей нет, то это свойство содержит пустую строку.
FileName — содержит имя файла, если часть является вложением
GetSubPartCount — метод который получает количество подчастей для части.
Вот теперь, имя в руках эту информацию мы уже можем сказать о том, как определить, что письмо содержит вложения.
Во-первых, вложения всегда имеют в свойстве Disposition значение ATTACHMENT.
Во-вторых, вложение всегда имеет свое названия (имя файла).
Теперь разберемся как эти вложения можно сохранять на диск.
2.3. Сохранение вложений
У нас уже есть обработчик для OnWalkPart — им мы и воспользуемся для сохранения вложений. Чтобы правильно сохранить вложение нам необходимо выполнить как минимум одно условие: правильно декодировать вложение.
Допишем нашу процедуру Walk следующим образом:
uses SynaCode; [...] var Stream: TStringStream; begin [...] WalkList.Items.Add(IntToStr(Sender.SubLevel)); if SameText(Sender.Disposition,'attachment') then begin Stream:=TStringStream.Create; try Stream.WriteString(DecodeBase64(Sender.PartBody.Text)); Stream.SaveToFile(Sender.FileName); finally Stream.Free; end; end; end;
Здесь мы воспользовались методом DecodeBase64 из модуля SynaCode, записали декодированную строку в поток, а поток сохранили в файл. В итоге на диске рядом с exe-файлом у меня оказалось два файла (а не 3 как показал mail.ru), т.к. всего две части сообщения были отмечены как attachment. Оба файла прекрасно открылись и прочитались.
Вот и все, что хотелось рассказать про вложения в письмах. Остается только поделиться исходниками:
и сказать — До новых встреч :)
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Новый дизайн? Мне нравится :)
Александр, да. Долго думал, собирался…а потом взял да и сменил всё :)
[…] Vladislav Bajenov writes about how to work with email attachements in Delphi […]
А можно ли как то читать заголовки писем без их скачивания?
Т.е. чтоб найти письмо по заголовку (+ отправителю)?
Evgeny Popov, для этого вроде бы IMAP есть.
Скажите пожалуйста, а как определить что хост доступен, если я днлаю коннект через Soks proxy ?
Т.е. вылезает ошибка Host not found но непонятно, то ли прокси дохлый, то ли до сервера не достучаться ?
Konstantin, вначале проверить доступ к прокси, потом, если доступ есть — проверить доступ к хосту…логично? Вообще вопрос работы с прокси рассматривался тут
А в самом компоненте FTPSend или POPSend можно это как то получить, чтобы не делать два коннекта подряд ?
нет
Vlad, доброго времени суток. Во первых БОЛЬШУЩЕЕ спасибо за статью про SMTP! Заработал DSN! Но теперь встала еще одна гадкая проблема. У нас почтарь Exchange и ему надо pop3 на 110 порту + starttls + аутентификация по NTLM. И тут-то я и приплыл… Подскажите плиз компонент (желательно фришный) который бы умел рор3 с такими извращениями… Или я плохо смотрел и synapse и это умеет?
sergej, то, что Synapse, в частности TPOP3Send без проблем понимает starttls — это точно (по-моему свойство AutoTLS надо в True включить). Порты, соответственно, меняются тоже легко. А вот по NTLM-аутентификации, к сожалению, помочь не могу. Я с ней в живую никогда не сталкивался — может быть в Synapse она и есть…Кстати, по поводу Exchange у меня тоже сейчас имеется целый ворох проблем в частности каким макаром можно гарантированно импортировать с Exchange-сервера (любого) все контакты, календари и почту пользователя…Там настроек всяких на этом сервере чёрт ногу сломит..
Влад, доброго времени суток. Рыская по инету в поисках решения случайно наткнулся… http://cryptocode.ru/synapse/NTLMAuthDemoApps_Synapse38b2.zip Счас вот сижу пытаюсь попробовать. О рез-ах отпишусь, если разбирусь… :)
Отвечаю.. Все работает! Правда, не понятно почему только при автоирзации pop3authlogin, хотя на сервере exchange стоит ntlm… Но разбираться я не стал, главное, что все что мне требвалось от synapse заработало. Влад, еще раз огромное вам спасибо за ценные материалы!
всегда пожалуйста. Как говориться, приходите к нам ещё :)
Подскажите пожалуйста в чем может быть проблема: Скачал ваш проект, пытаюсь открыть его в D7 вышла ошибка vcl.forms.dcu not found
Поможете разобраться?
vcl.forms.dcu not found
Перевод:
«vcl.forms.dcu не найден»
Все правильно, в Delphi 7 такого файла нет и быть никак не могло. А вот просто forms.dcu по-моему был. Следовательно — надо просто найти где в uses есть ссылка на модуль vcl.forms и изменить её просто на forms.
После того как удалил VCL. появились еще ошибки…. помогите пожалуйста их решить.
[Warning] main.pas(112): Return value of function ‘GetLastNodeOnLevel’ might be undefined
[Error] main.pas(143): Not enough actual parameters
[Error] main.pas(146): Undeclared identifier: ‘SaveToFile’
[Error] main.pas(155): Undeclared identifier: ‘ReportMemoryLeaksOnShutdown’
[Hint] main.pas(39): Private symbol ‘mimepart’ declared but never used
[Fatal Error] pop3.dpr(5): Could not compile used unit ‘main.pas’
[Warning] main.pas(112): Return value of function ‘GetLastNodeOnLevel’ might be undefined
[Error] main.pas(143): Not enough actual parameters ->ошибка в строке Stream:=TStringStream.Create;
[Error] main.pas(146): Undeclared identifier: ‘SaveToFile’ -> ошибка в строке Stream.SaveToFile(Sender.FileName);
[Error] main.pas(155): Undeclared identifier: ‘ReportMemoryLeaksOnShutdown’ -> ошибка в строке ReportMemoryLeaksOnShutdown := True;
[Hint] main.pas(39): Private symbol ‘mimepart’ declared but never used
[Fatal Error] pop3.dpr(5): Could not compile used unit ‘main.pas’
Andrew, различий между Delphi 7 и Delphi XE1-5 намного больше, чем может показаться на первый взгляд и ошибки об этом ясно говорят. У меня нет под рукой Delphi 7 вообще, поэтому могу кое в чем ошибиться, но: [Error] main.pas(143): Not enough actual parameters ->ошибка в строке Stream:=TStringStream.Create; на сколько я помню, в конструкторе TStringStream в Delphi 7 надо было указывать строку, т.е. делать так: Stream:=TStringStream.Create(''); далее [Error] main.pas(155): Undeclared identifier: ‘ReportMemoryLeaksOnShutdown’ В Delphi 7 FastMM набо ббыло ставить отдельно, следовательно, эту строку надо просто убрать, раз ваша 7-ка на эту строку ругается. [Hint] main.pas(39): Private symbol ‘mimepart’ declared but never… Подробнее »
Можно не использовать TStringStream, а заменить его на TMemoryStream вот так:
MyStream:= TMemoryStream.Create;
try MyStream.Write(PChar(DecodeBase64(Sender.PartBody.Text))^,Length(DecodeBase64(Sender.PartBody.Text)));
MyStream.SaveToFile(Sender.FileName);
finally
MyStream.Free;
end;
С свойством Disposition не верно подмечено автором:
«Во-первых, вложения всегда имеют в свойстве Disposition значение ATTACHMENT.»
Про Disposition немного описано здесь. У меня был случай, когда свойство Disposition для вложения (excel файлы) имели значение INLINE.
Спасибо за замечание. Учту на будущее
Кто подскажет, как с помощью Synapse разобрать Winmail.dat (TNEF)?