Обработка почтовых отчетов на Perl

И всё прочее, что касается HTML
Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-07-27 7:52:22

Есть задача распарсить отчет об ошибочной доставки письма на Perl и далее уже данные передать в БД. Сама загвоздка встала в разборке текста. Вариантов отчетов от почтовых серверов несколько, все выглядят по разному. Можно для упрощения начать с одного наиболее распространенного типа (пример его в низу).
Из отчета хотелось бы доставать email на который не дошло письмо и текст ошибки доставки.
Интересует алгоритм работы с текстами (доставание оттуда инфы, которая может размещаться в разных местах текста), реализация этого алгоритма на perl в частности. Можно просто ссылки на аналогичные скрипты, можно даже на других языках.

Пример самого распространенного типа отчета об ошибке:

Код: Выделить всё

This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  morosowwa.tat@yandex.ru
    SMTP error from remote mail server after RCPT TO:<morosowwa.tat@yandex.ru>:
    host mx.yandex.ru [87.250.250.89]: 550 5.7.1 No such user!

Код: Выделить всё

This message was created automatically by mail delivery software.
A message that you sent has not yet been delivered to one or more of its
recipients after more than 24 hours on the queue on mailer.smartresponder.ru.

The message identifier is:     1QlZXa-000E6I-Dd
The date of the message is:    Tue, 26 Jul 2011 08:46:45 +0400
The subject of the message is: =?windows-1251?B?T3JpY28uIMLg+CDv8Ojh++v87fvpIEFMUEhBLeHr7uMhIDIuMA==?=

The address to which the message has not yet been delivered is:

  orico@km.ru
    Delay reason: SMTP error from remote mail server after end of data:
    host m2.mx.km.ru [217.197.114.195]: 450 4.4.2 Bad connection

No action is required on your part. Delivery attempts will continue for
some time, and this warning may be repeated at intervals if the message
remains undelivered. Eventually the mail delivery software will give up,
and when that happens, the message will be returned to you.

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-07-27 10:43:58

email достать получилось, с текстом ошибки вопрос открытый.


Аватара пользователя
Alex Keda
стреляли...
Сообщения: 35454
Зарегистрирован: 2004-10-18 14:25:19
Откуда: Made in USSR
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение Alex Keda » 2011-07-28 21:23:20

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

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-07-31 20:34:35

Для доставания email применил модуль Perl Email-Find.
По тексту тупо условиями if else проверка текстов писем на наличие в них нужных фраз и в зависимости от типа письма выдергивание нужных абзацев или строк.
Сам скрипт корявый, но при необходимости могу приложить))

Аватара пользователя
Alex Keda
стреляли...
Сообщения: 35454
Зарегистрирован: 2004-10-18 14:25:19
Откуда: Made in USSR
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение Alex Keda » 2011-07-31 21:15:37

и тему закрыл...
наверное, чтобы не попросили приложить? =)
Убей их всех! Бог потом рассортирует...

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-01 8:18:36

Прикладываю свое корявое поделие :)

Код: Выделить всё

#!/usr/bin/perl -w

#use strict;
use Email::Find;
use DBI;

sub addTrashInfo {
my ($arg) = @_;

my $exim_body = Exim::expand_string('$message_body');

if ($exim_body =~ m/This\ message\ was\ created\ automatically\ by\ mail\ delivery\ software/i)
{
	my @emails = ();
	my $finder = Email::Find->new(sub {
								my($email, $orig_email) = @_;
								push (@emails, $email->format);
								return $orig_email;
							});
	$finder->find(\$exim_body);
	
	if ($exim_body =~ m/A\ message\ that\ you\ sent\ has\ not\ yet/i)
	{
		if ($exim_body =~ m/Delay\ reason/i)
		{
			($part1, $part2, $part3, $part4) = split /\n{2}/, $exim_body, 5;
			($part31, $part32) = split /\n/, $part4, 2;
				
			for ($part32) {
				s/^\s+Delay.*\n//;
			}
		}
		else
		{
			($part1, $part2) = split /\n{2}/, $exim_body, 3;
			($part31, $part32) = split /\n/, $part1, 2;
		}
	}
	else
	{
		($part1, $part2, $part3) = split /\n{2}/, $exim_body, 4;
		($part31, $part32) = split /\n/, $part3, 2;
			
		for ($part32) {
			s/^\s+SMTP.*\n//;
		}
	}
	
	for ($part32)
	{
		s/^\s+//;
		s/\n+//;
		s/\s+/ /g;
	}
	
	open(EXILOG,">> /var/log/exim_perl.log");
	
	my $dbh = DBI->connect("DBI:mysql:database=h11036_exim;host=schtep.ru","h11036_exim", "secret") || die $DBI::errstr;
	my $result = $dbh->prepare("INSERT INTO emails (email, error, body) VALUES (\'@emails['0']\', \'$part32\', \'$exim_body\');");
	$result->execute();
	
	print (EXILOG @emails['0']."\n");
	print (EXILOG $part32."\n");
	print (EXILOG "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
	print (EXILOG "\n");
	close(EXILOG);
}
else
{
	my $err_pattern = "BAD!";
	open(EXILOG,">> /var/log/exim_perl.log");
	print (EXILOG "$err_pattern\n");
	#print (EXILOG "$exim_body");
	print (EXILOG "\n");
	print (EXILOG "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
	print (EXILOG "\n");
	close(EXILOG);
	
}
#Exim::log_write("Perl test. Body is $exim_body");

return "Perl: trash info has been added.";
}

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-01 8:21:49

Есть дальнейшая задумка вынести все это в отдельный сервис (daemon) и передавать на него письма через LMTP. Как чето в этом направлении прояснится, то покажу результат.
Если у кого есть наработки в этой области (создание демона на perl и работа с lmtp) буду рад посмотреть на них. В инете достаточно мало примеров. Тему соответственно открываю.

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-01 21:25:08

https://metacpan.org/module/Net::Server::Mail::LMTP решение всех ваших проблем
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-01 22:27:24

thefree писал(а):https://metacpan.org/module/Net::Server::Mail::LMTP решение всех ваших проблем
Да, про него уже читал и тоже думаю что он мне подойдет. Тут я пока только не понимаю смогу ли использовать unix сокет? Ну и пока не вкуриваю как в итоге мне достать тело письма пришедшее по lmtp? :oops:

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-02 11:34:55

так там же callback есть, по ним и разбирайте письмо
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-02 14:39:17

thefree писал(а):так там же callback есть, по ним и разбирайте письмо
да, чувствую нужно просто брать и пробовать... дойду до истины опытным путем. :roll:
===
а про unix сокет вопрос открытый... можно его задействовать в этом модуле?

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-02 18:39:42

В общем пока имею вот что:
1. lmtp сервер (perl скрипт), слушающий unix сокет
2. роутер и транспорт в exim для редиректа писем ошибок на него
3. в скрипте достаем тело (data) письма и сохраняем в лог.
4. смотрим на бегущие строки в логе ;-)

Если кому интересно, могу показать реализацию. Но она очень примитивна и сделана ради проверки работы связки. Основная логика обработки ошибок в ней отсутствует.

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-02 20:59:42

показывай (:
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 7:39:12

lmtp.pl

Код: Выделить всё

 use IO::Socket::UNIX;
 use Net::Server::Mail::LMTP;
 use Data::Dumper;

my @local_domains = qw(example.com example.org);
my $server = new IO::Socket::UNIX Listen =>1, Type => SOCK_STREAM, Local => '/tmp/lmtp.sock';

my $conn;
while($conn = $server->accept)
{
	my $esmtp = new Net::Server::Mail::LMTP socket => $conn;
	# adding some handlers
	$esmtp->set_callback(RCPT => \&validate_recipient);
	$esmtp->set_callback(DATA => \&queue_message);
	$esmtp->process();
	$conn->close()
}

sub validate_recipient
{
	my($session, $recipient) = @_;

	my $domain;
	if($recipient =~ /@(.*)>\s*$/)
	{
		$domain = $1;
	}

	if(not defined $domain)
	{
		return(0, 513, 'Syntax error.');
	}
	elsif(not(grep $domain eq $_, @local_domains))
	{
		return(0, 554, "$recipient: Recipient address rejected: Relay access denied");
	}

	return(1);
}

sub queue_message
{
	my($session, $data) = @_;

	my $sender = $session->get_sender();
	my @recipients = $session->get_recipients();

	return(0, 554, 'Error: no valid recipients')
		unless(@recipients);

	#my $msgid = add_queue($sender, \@recipients, $data)
	#  or return(0);

	#return(1, 250, "message queued $msgid");
	
	my $msg = Dumper($data);
	
	open(EXILOG,">> /var/log/exim_perl.log");
	print (EXILOG $msg);
	print (EXILOG "=-=-=-=-=-=-=");
	print (EXILOG "\n");
	close(EXILOG);
	
	return(1, 250, "message queued");
}
exim/configure

Код: Выделить всё

trash:
           driver          = accept
           condition       = "${if  match{$h_To:}{trash@example.com} {1}{0}}"
           transport       = lmtp
...
lmtp:
            driver = lmtp
            socket = /tmp/lmtp.sock
            batch_max = 2
            user = mailnull

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 7:44:20

Тут возникла некоторая проблемка... данные в переменной $data в lmtp.pl хранятся в виде ссылки что ли. Т.е. при попытке вывести их непосредственно через print получается что-то типа этого SCALAR(8x007895). Поэтому пока извращаюсь через dumper. Может подскажет кто как их оттуда достать по человечески.
Ну и после создания сокета приходится менять его владельца на mailnull иначе exim на него не может ничего передать. Как понимаю при создании сокета этот параметр не задается? Менять его нужно после создания уже самостоятельно?

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-03 13:28:06

1. Покажи вывод Data::Dumper полностью.
2. сверху бы стоило добавить

Код: Выделить всё

use Proc::PID::File;
die "Already running!" if Proc::PID::File->running();
У вас же это демон.
3. В лес идти не будем

Код: Выделить всё

chown('mailnull', 'wheel', '/tmp/lmtp.sock') if -f "/tmp/lmtp.sock";
p.s. use strick & use warnings или use common::sense; Лучше сразу правильно кодить.
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-03 13:33:35

вот еще я очень плохо знаком с рфс но не понимаю зачем Вам sub validate_recipient и обработка RCPT
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 14:11:11

1. Ок, сделаю
2. Спасибо, добавил.
3. Спасибо, добавил. Только ключ -S при проверке сокета. Добавил так же проверку и удаление старого файла сокета. Остается если ранее запускался скрипт и падал с ошибкой. А при наличии уже созданного файла новый запуск скрипта вываливается в ошибку.
4. strict и warnings тоже добавил

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 14:29:47

chown принимает цифровые значения uid и gid. Обыграл вот так:

Код: Выделить всё

my $sock = '/tmp/lmtp.sock';
my $eximuser = 'mailnull';

my ($login,$pass,$uid,$gid) = getpwnam($eximuser);
chown($uid, 0, $sock) if -S $sock;

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 15:16:53

thefree писал(а):вот еще я очень плохо знаком с рфс но не понимаю зачем Вам sub validate_recipient и обработка RCPT
Интересно то, что если эти моменты исключить из скрипта, то exim ругается что соединение было закрыто сразу после открытия и сообщение соответственно на скрипт не передается. Пока оставил скрипт так как было, буду разбираться.

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-03 19:58:06

покажите полностью ошибку и попробуйте print @{$data}
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету

Аватара пользователя
serge
майор
Сообщения: 2133
Зарегистрирован: 2006-07-30 15:34:14
Откуда: Саратов
Контактная информация:

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение serge » 2011-08-03 23:08:27

Получилось через $$data.
Также исключил из скрипта функцию validate_recipient
===
Пока перешел к парсингу самого письма. Добиваю функцию выдергивания хедеров из отчета об ошибки и из оригинального письма, которое частично или полностью прикладывается к этому отчету.

Аватара пользователя
thefree
лейтенант
Сообщения: 980
Зарегистрирован: 2008-12-29 9:23:19
Откуда: Весёлая Страна

Re: Обработка почтовых отчетов на Perl

Непрочитанное сообщение thefree » 2011-08-04 8:08:18

будет нужна помощь пишите.
Не судите меня строго, Я не волшебник, а только учусь!
http://planetbsd.ru - RSS-агрегатор *BSD по Рунету