Exim - обработка почты внешней программой

EXIM, sendmail, postfix, Dovecot и прочие. Решение проблем связанных с работой электронной почты

Модератор: xM

Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
KeshaV
проходил мимо
Сообщения: 7
Зарегистрирован: 2013-03-23 1:11:45

Exim - обработка почты внешней программой

Непрочитанное сообщение KeshaV » 2014-01-08 12:57:31

Добрый день.

У меня стоит настроенный exim с таким конфигом:

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

log_selector =  \
        +all_parents \
        +lost_incoming_connection \
        +received_sender \
        +received_recipients \
        +tls_cipher +tls_peerdn \
        +smtp_confirmation \
        +smtp_syntax_error \
        +smtp_protocol_error
  
trusted_users = apache

domainlist local_domains = lsearch;/etc/exim/domains
domainlist dummy_domains =
hostlist relay_from_hosts = 127.0.0.1

domainlist relay_to_domains = lsearch;/etc/exim/domains
exim_user = exim
exim_group = exim

never_users = root
host_lookup = *
rfc1413_hosts = *
rfc1413_query_timeout = 0s
ignore_bounce_errors_after = 2d
timeout_frozen_after = 7d
acl_smtp_rcpt = acl_check_rcpt
acl_smtp_data = acl_check_data



begin acl
	acl_check_rcpt:
		accept	hosts = net-lsearch;/etc/exim/whitelist
		
		deny	hosts = net-lsearch;/etc/exim/blacklist
				message = $host_data                        

		deny	message       = Restricted characters in address
				domains       = +local_domains
				local_parts   = ^[.] : ^.*[@%!/|]

		deny    message       = Restricted characters in address
				domains       = !+local_domains
				local_parts   = ^[./|] : ^.*[@%!] : ^.*/\\.\\./

		accept  local_parts   = postmaster
				verify        = recipient
				domains       = +local_domains

		require verify        = sender

		accept  hosts         = +relay_from_hosts
				control       = submission

		accept  authenticated = *
				condition     = ${if eq{${extract{5}{:}{${lookup{$authenticated_id}lsearch{/etc/exim/passwd}}}}}{no} {yes}{no}}
				condition     = ${if eq{${extract{3}{:}{${lookup{${domain:$authenticated_id}}lsearch{/etc/exim/domains}}}}}{no} {yes}{no}}
				control       = submission/domain=

		deny	message       = rejected because $sender_host_address is in a black list at $dnslist_domain\\n$dnslist_text
				dnslists      = ${readfile {/etc/exim/dnsblists}{:}} 

		require message       = relay not permitted
				domains       = +local_domains : +relay_to_domains

		require verify        = recipient

		accept

	acl_check_data:
		accept

begin routers
	dnslookup:
		driver = dnslookup
		domains = !+dummy_domains
		transport = remote_smtp
		ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
		self = pass
		no_more

	disabled_domains:
		driver = redirect
		condition = ${extract{3}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		allow_fail = yes
		data = :fail: Domain disabled
		no_more

	disabled_users:
		driver = redirect
		condition = ${extract{5}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}
		allow_fail = yes
		data = :fail: User disabled
		no_more

	local_domains:
		driver = redirect
		data = ${quote_local_part:$local_part}@${extract{1}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		cannot_route_message = Unknown user
		no_more

	group_aliases:
		driver = redirect
		data = ${extract{1}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/aliases}}}}
		condition = ${if and{\
						{exists{/etc/exim/aliases}}\
						{eq {${extract{2}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/aliases}}}}} {group} }\
					} {yes} {no} }
		redirect_router = a_dnslookup

	aliases:
		driver = redirect
		data = ${extract{1}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/aliases}}}}
		condition = ${if exists{/etc/exim/aliases} {yes} {no} }

	aliases_pipe:
                driver = redirect
		pipe_transport = address_pipe
		data = ${lookup {$local_part@$domain} lsearch{/etc/exim/pipe-aliases}}
                condition =${lookup {$local_part@$domain} lsearch{/etc/exim/pipe-aliases} {yes} {no} }
								

	local_users:
		driver = redirect
		condition = ${lookup {$local_part@$domain} lsearch {/etc/exim/passwd} {yes} {no} }
		data = $local_part@$domain
		redirect_router = autoreplay

	catchall_for_domains:
		driver = redirect
		headers_add = X-redirected: yes
		data = ${extract{2}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		file_transport = local_delivery

	unknown_users:
		driver = redirect
		allow_fail = yes
		data = :fail: Unknown user
		no_more

	autoreplay:
		driver = accept
		condition = ${if exists{${extract{4}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}/message.txt} {yes} {no}}
		retry_use_local_part
		transport = address_reply
		unseen

	localuser:
		driver = accept
		transport = local_delivery

# Same routers without autoreplay

	a_dnslookup:
		driver = dnslookup
		domains = !+dummy_domains
		transport = remote_smtp
		ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
		self = pass
		no_more

	a_disabled_domains:
		driver = redirect
		condition = ${extract{3}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		allow_fail = yes
		data = :fail: Domain disabled
		no_more

	a_disabled_users:
		driver = redirect
		condition = ${extract{5}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}
		allow_fail = yes
		data = :fail: User disabled
		no_more

	a_local_domains:
		driver = redirect
		data = ${quote_local_part:$local_part}@${extract{1}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		cannot_route_message = Unknown user
		redirect_router = a_dnslookup
		no_more

	a_aliases:
		driver = redirect
		data = ${extract{1}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/aliases}}}}
		condition = ${if exists{/etc/exim/aliases} {yes} {no} }
		redirect_router = a_dnslookup

	a_aliases_pipe:
		driver = accept
		transport = aliases_pipe
		condition = ${lookup {$local_part@$domain} lsearch {/etc/exim/pipe-aliases} {yes} {no} }

	a_local_users:
		driver = accept
		transport = local_delivery
		condition = ${lookup {$local_part@$domain} lsearch {/etc/exim/passwd} {yes} {no} }

	a_catchall_for_domains:
		driver = redirect
		headers_add = X-redirected: yes
		data = ${extract{2}{:}{${lookup{$domain}lsearch{/etc/exim/domains}}}}
		file_transport = local_delivery
		redirect_router = a_dnslookup

begin transports
 
    DKIM_DOMAIN = ${lc:${domain:$h_from:}}
    DKIM_FILE = /etc/exim/${lc:${domain:$h_from:}}.key
    DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}
 
	remote_smtp:
		driver = smtp
        dkim_domain = DKIM_DOMAIN
        dkim_selector = mail
        dkim_private_key = DKIM_PRIVATE_KEY
		interface = ${extract{1}{:}{${lookup{$sender_address_domain}lsearch{/etc/exim/domainips}}}}
  
	local_delivery:
		driver = appendfile
		file = ${extract{4}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}/inbox
		delivery_date_add
		envelope_to_add
		return_path_add
		mode = 0660
		quota = ${extract{3}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}M
		quota_warn_threshold = 75%
		use_lockfile = no
		no_mode_fail_narrower
		user = ${extract{1}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}
		group = ${extract{2}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}
						  
	address_pipe:
		driver = pipe
		return_output
							  
	aliases_pipe:
		driver = pipe
		command = ${extract{1}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/pipe-aliases}}}}
		use_shell
									
	address_reply:
		driver = autoreply
		headers = ${readfile{${extract{4}{:}{${lookup{$local_part@$domain}lsearch{/etc/exim/passwd}}}}/message.txt}}
		to = $sender_address


begin retry
*		*		F,2h,15m; G,16h,1h,1.5; F,4d,6h

begin rewrite

begin authenticators

login:
    driver = dovecot
    public_name = LOGIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $1

plain:
    driver = dovecot
    public_name = PLAIN
    server_socket = /var/run/dovecot/auth-client
    server_set_id = $1
Как сделать так, чтобы для всех принимаемых сообщений вызывался php скрипт (или программа на c++), которому бы передавалось тело письма целиком (с заголовками) ?
То есть, письмо принимается, передается скрипту, он отрабатывает, а письмо дальше передается в папку пользователю.
Подскажите что куда вписать, чтобы не "накосячить" случайно. Почему-то решения такой задачи я не нашел. Заранее спасибо за ответ.

Хостинговая компания 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/

Аватара пользователя
xM
ст. лейтенант
Сообщения: 1316
Зарегистрирован: 2009-01-15 23:57:41
Откуда: Königsberg
Контактная информация:

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение xM » 2014-01-08 14:05:03

Ну через транспорты, традиционно.
Или уже опосля через системный фильтр и команду pipe.
IT voodoo blog https://kostikov.co

KeshaV
проходил мимо
Сообщения: 7
Зарегистрирован: 2013-03-23 1:11:45

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение KeshaV » 2014-01-08 14:36:20

А есть в сети какой-то пример этого?
просто еще не совсем понятно где будет тело письма и как его получить в скрипте.

Аватара пользователя
xM
ст. лейтенант
Сообщения: 1316
Зарегистрирован: 2009-01-15 23:57:41
Откуда: Königsberg
Контактная информация:

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение xM » 2014-01-08 18:01:59

Да какой там пример... Я так понимаю, что письмо пихается в STDIN. Можете вызывать внешний обработчик стандартным путём.
Тренируйтесь

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

exim -bF myfilter <test-message
IT voodoo blog https://kostikov.co

KeshaV
проходил мимо
Сообщения: 7
Зарегистрирован: 2013-03-23 1:11:45

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение KeshaV » 2014-01-08 22:21:58

Я просто не совсем понимаю куда что вписывать. Вот например у меня должен быть такой роутер:

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

externalprog_router:
  driver = accept
  transport = externalprog_transport
Что еще сюда нужно вписать и в какое место моего конфига вставить?

А транспорт получается вот таким, правильно?

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

externalprog_transport:
  driver = pipe
  command = php /var/myprog/myprog.php >> /var/log/receive_proc.log
  group = mail
  delivery_date_add
  envelope_to_add
В самом скрипте пишем так:

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

<?php
 $message = file_get_contents('php://stdin');
 // Обработка
?>
Так вроде получается?

Аватара пользователя
xM
ст. лейтенант
Сообщения: 1316
Зарегистрирован: 2009-01-15 23:57:41
Откуда: Königsberg
Контактная информация:

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение xM » 2014-01-09 11:26:23

Да вроде бы. Конкретикой не помогу, поскольку не занимался такой задачей.
Пробуйте. Заодно и нам расскажете как оно всё работает...
IT voodoo blog https://kostikov.co

KeshaV
проходил мимо
Сообщения: 7
Зарегистрирован: 2013-03-23 1:11:45

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение KeshaV » 2014-01-10 13:25:55

В общем разобрался. Так что если кому то пригодится, то будет здорово.

Итак, в конфиге exim перед строками:

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

   localuser:
      driver = accept
      transport = local_delivery
Вписываем:

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

        externalprog_router:
                driver = accept
                unseen = true
                transport = externalprog_transport
Мы создаем роутер для всего что пришло, который ведет на соответствующий транспорт. Параметр unseen = true позволяет не останавливаться на этом роутере, а передать сообщение дальше для обработки (чтобы письма были положены в mailbox пользователя).

Далее в секции begin transports вписываем в конец еще один транспорт:

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

        externalprog_transport:
                driver = pipe
                command = php -f /var/myprog/myprog.php
                group = root
                delivery_date_add
                envelope_to_add
                log_output = true
Создаем каталог /var/myprog/ (если скрипт будет писать в свой каталог, то выставляем права на папку 777 на всякий случай).

В этом каталоге создаем скрипт обработки myprog.php.

В моем примере скрипт тупо сваливает приходящую почту в один файл.

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

<?php
 $message = file_get_contents('php://stdin');
 file_put_contents('/var/myprog/msglist.txt', $message, FILE_APPEND | LOCK_EX );
?>
Обратите внимание, что скрипты для обработки почты могут быть вызваны параллельно, поэтому обязательно надо лочить файл для записи, чтобы не писали туда одновременно. Для этого стоит флаг LOCK_EX.

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

Аватара пользователя
xM
ст. лейтенант
Сообщения: 1316
Зарегистрирован: 2009-01-15 23:57:41
Откуда: Königsberg
Контактная информация:

Re: Exim - обработка почты внешней программой

Непрочитанное сообщение xM » 2014-01-10 16:43:39

В вашем случае, как мне кажется, всё это можно было бы сделать одной строчкой в system filter

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

unseen pipe "php -f /var/myprog/myprog.php"
Хотя, конечно, смотря какие цели на выходе преследуются.
IT voodoo blog https://kostikov.co