Продолжается подписка на наши издания! Вы не забыли подписаться?

Почтовый робот для Baikonur SuperServer

Простой пример программирования

 Алексей Мельников

Цель статьи – показать возможность построения программ автоматического разбора приходящей почты для случая Baikonur SuperServer.

Если у вас уже был опыт работы с Baikonur Enterprise Web App Server (BEWAS), то вы уже знаете возможности сервера приложений и то, что основной функцией сервера является обслуживание запросов удаленных или локальносетевых пользователей, запуск задач (.exe-модулей) по запросам, приходящим по различным протоколам, и коммутация потоков информации между пользователями и запущенными приложениями. 

Baikonur SuperServer (BSS) в этом смысле не слишком отличается от BEWAS – он работает точно так же, просто в комплект поставки добавлены некоторые дополнительные возможности. 

На рисунке слева показаны основные отличия Baikonur SuperServer от Baikonur Enterprise Web App Server. Из рисунка видно, что в дополнение к уже существующим протоколам (http, ftp, finger, gopher) BSS поддерживает еще и почтовые протоколы SMTP, POP3, IMAP4. 
Работа с этими протоколами аналогична работе с протоколом HTTP. BSS можно использовать как штатный почтовый сервер, но самое полезное его качество – это возможность использования его как почтовый сервер приложений. 
Кроме стандартной обработки почтовых сообщений и доступа пользователей к своим почтовым ящикам для BSS можно конструировать специализированные почтовые обработчики (роботы), которые позволяют автоматически обрабатывать почтовые сообщения, выделять присоединенную информацию (attachements), проводить транзакции с серверами БД и генерировать рассылку почтовых сообщений по списку почтовых адресов. 
Давайте создадим простейшее приложение серверного слоя, которое, кроме стандартных функций SMTP сервера, позволяет удаленному пользователю запросить и получить содержимое произвольного файла с сервера по почте. 

Сценарий работы в нашем случае будет следующий: 

Чтобы проверить, как это будет работать наш пример, сделаем все необходимые настройки: 

 

А теперь обсудим особенности программирования. 

Откроем новый проект в Delphi 3. Если у Вас проинсталлирован package для работы с Baikonur Mail Server, то на странице "Baikonur Mail" Вы увидите три проинсталлированные компоненты TSmtpControl, Tpop3Control и TForwarder. 
В самом начале мы имеем пустую форму проекта. Положим на форму компоненты TPanel, TGroupBox и TMemo на TGroupBox.  

 

Теперь добавим функциональность SMTP сервера. Для этого положим компонент TSmtpControl на форму. 

Чтобы для каждого коннекта пользователя запускалась 1 копия программы, надо установить свойство ApplicationType компонента TSmtpControl равным atMultiuserRedirect. 

Как только на форму был положен компонент TSmtpControl, ваше приложение вместе с сервером Baikonur стало обладать функциональностью SMTP сервера.  
Для решения нашей задачи (пересылка указанного в письме файла по электронной почте) у компоненты TSmtpControl нужно определить обработчики событий.

Определим обработчик события OnWriteLog. Это событие отвечает за обработку информационных и отладочных сообщений. Оно происходит, когда появляется отладочная информация и обычно используется для формирования log-файла. Определим обработчик события так, чтобы отладочные сообщения не выводились.

Добавим модуль Dparsers в uses предложение Interface-ной части формы. 
Обработка приходящих писем производится при помощи механизма фильтров. Для нашего случая нам тоже потребуется создать такой фильтр. 
Нажмите в Object Inspector кнопку "…" в свойстве Filters для вызова его редактора. Перед вами высветится диалог редактора фильтров. В нем можно настроить порядок вызова фильтров, создать новые, уничтожить старые или настроить условия срабатывания фильтров.  
Фильтры могут каскадироваться, поэтому почтовые обработчики могут реализовывать довольно сложную функциональность.

 

Создадим новый фильтр. Для настройки условий его срабатывания надо нажать кнопку Edit. В этом диалоге можно указать такие условия, как максимальный и минимальный размер файла, с какого поля заголовка письмо может начинаться или что должно содержать письмо в своем теле ( некоторая строка) для того, чтобы сработал обработчик. 

   

Настроим фильтр, чтобы он срабатывал, если поле To начинается со строки send-mail. 

Теперь вернемся к редактору фильтров. Выберем только что созданный фильтр и страницу Events в Object Inspector. Два раза щелкнем мышкой на обработчике OnFilter. 
В обработчике можно написать следующий текст:

   procedure TForm1.SendViaMailFilter(Sender: TSmtpControl; Filter: TMailFilter; 
  Envelope: TSMTP_Transaction; MsgStream: TStream; 
  var Action: TFilterAction; var ContinueFiltering: Boolean); 
Var 
 Msg, OutMsg : TQueMessage;
 Line, Command : String; 
 FPos : Integer; 
 FileName : String; 
 Receip : TStringList; 
begin 
 // Выводим сообщение 
 SmtpControl.WriteLog (llMessage, 'GET filter started'); 
 Msg := NIL; 
 try 
  Msg := TQueMessage.Create (NIL); 
  try 
   Msg.Top (MsgStream,1);
// чтение тела сообщения из потока. Сообщение
// может быть размером в несколько мегабайт,
// поэтому нецелесообразно читать его
// полностью в память 
  except 
   On E : Exception do 
    begin 
     SmtpControl.WriteLog (llError, 'In TOP : '+E.Message); 
     Exit; 
    end; 
  end; 
  if Msg.Body.Count>0 then 
   begin 
    Line := Msg.Body[0]+#13; 
     SmtpControl.WriteLog (llMessage, 'Message line is : '+Line); 
// выделяем подстроку до символа ограничителя 
    FPos := 1; 
    Command := UpperCase (GetStringValueAt (Line, FPos, [' '], ''));  
    if Command='GET' then // запрос на файл 
     begin 
      SmtpControl.WriteLog (llMessage, 'Requesting file'); 
// создаем объект для хранения сообщения 
      OutMsg := TQueMessage.Create (NIL);  
      try 
       try 
        FileName := UpperCase (GetStringValueAt (Line, FPos, [' ',#13], '')); 
        if FileName = '' then 
         raise Exception.Create ('Required parameter (File name) for GET command is not defined' + CRLF + 
                'Use the following syntax : GET <file name>'); 
        // В данном примере не проверяется, где находится файл, так что  
        // сервер может прислать любой файл, на доступ к которому у него 
        // будут права 
        if not FileExists(FileName) then 
         raise Exception.Create ('Requested file ('+FileName+') not found'); 
        // формируем заголовок письма 
        OutMsg.ContentType := 'text/plain'; 
        OutMsg.Subject := 'Request processed'; 
        OutMsg.Recipients.AddAddress (Envelope.Sender,''); 
        OutMsg.Sender.Address := SendViaMail.HeaderData + Envelope.LocalHost; 
        // формируем тело письма 
        OutMsg.Body.Add ( 
         'Dear friend!' + CRLF + CRLF + 
         'You have requested the following file "' + FileName + '" from our server.' + CRLF + 
         'If you think that you got this message by mistake, please sent us message to '+ 
          'SUPPORT@' + Envelope.LocalHost + CRLF + // MUST EXIST!!! 
         'Thank you for using our service!' + CRLF); 
        OutMsg.Attachments.AddFile (FileName); // ??? 
        // формируем список получателей письма. Посылаем ответ 
        // отправителю, а не тому, кто указан в поле From 
        Receip := TStringList.Create; 
        Receip.Add (Envelope.Sender); 
        Try 
        // Посылка письма 
         SmtpControl.DirectSendMessage (OutMsg, 
                                        Envelope.LocalHost, 
                                        SendViaMail.HeaderData + Envelope.LocalHost, 
                                        Receip); 
        finally 
         Receip.Free; 
        end; 
       // Этот блок создает письмо, в случае неудачи при выполнении 
       // посылки письма (например требуемый файл не найден) 
       // Его можно опустить 
       except 
        on E : Exception do 
         begin 
          SmtpControl.WriteLog (llError, 'Post failed!!!'); 
          OutMsg.ContentType := 'text/plain'; 
          OutMsg.Subject := 'Request processing failed'; 
          OutMsg.Recipients.AddAddress (Envelope.Sender,''); 
          OutMsg.Sender.Address := SendViaMail.HeaderData + Envelope.LocalHost; 
          OutMsg.Body.Add ( 
           'Dear friend!' + CRLF + CRLF + 
           'You have requested the following file "' + FileName + '" from our server.' + CRLF + 
           'But server encounter the following error :' + E.Message + CRLF + 
           'If you think that you got this message by mistake, please sent us message to '+ 
           'SUPPORT@' + Envelope.LocalHost + CRLF + // MUST EXIST!!! 
           'Thank you for using our service!' + CRLF); 
          OutMsg.Attachments.AddFile (FileName); // ??? 
          Receip := TStringList.Create; 
          Receip.Add (Envelope.Sender); 
          try 
           SmtpControl.DirectSendMessage (OutMsg, 
                                          Envelope.LocalHost, 
                                          SendViaMail.HeaderData + Envelope.LocalHost, 
                                          Receip); 
          finally 
           Receip.Free; 
          end; 
         end; {On Exception} 
       end {try/except/end} 
      finally 
       OutMsg.Free; // Внимание! Уничтожаем объект для хранения письма 
      end; 
     end; {if 'GET'} 
   end; {Msg.Body.Count>0} 
 finally 
  Msg.Free; // Внимание! Уничтожаем объект для хранения письма 
  ContinueFiltering := False; // Запретить выполнять другие фильтры над данным письмом 
  Action := faDelete; // Удалить письмо 
 end; 
 SmtpControl.WriteLog (llMessage, 'Filter is OK'); 
end;

Как всегда, при любом программировании, большая часть кода ушла на обработку недопустимых или ошибочных ситуаций.  Заметим, что мы абсолютно свободны в выборе того, что должен делать обработчик, выполнять команду посылки файла или запрашивать информацию из базы данных.  Классическим случаем такого рода обработчиков являются почтовые роботы типа ftpmail, которые позволяют получать информацию с ftp-сервера через почту.  Мы могли убедиться в том, что для реализации подобного робота требуется сравнительно несложное программирование.  Более того, требуются совсем небольшие усилия, чтобы избавиться от назойливых мусорных сообщений (спам) или для того, чтобы сделать серьезную систему автоматического обмена информацией, основанную на почтовых службах. 


Copyright © 1994-2016 ООО "К-Пресс"