Сортировка писем по imap-папкам на почтовом сервере без использования sieve и иже с ними

в 10:16, , рубрики: sieve, метки:

Почта в своей конторе разрослась и наличие более чем 1 рабочего места у более чем 1 сотрудника начали требовать большего, нежели простейшая реализация мультидоменного почтового сервера на базе exim+teapop без использования mysql на обычных файликах. Главная причина изменений — постоянное вычищение спама и просто ненужных писем на каждом из рабочих мест по многу раз начало доставать, и было принято решение реализовать-таки почту по известной статье лиссяры "Связка exim и courier-imap". Самое главное, чего в ней не хватало из того, что требовалось, так это сортировщика писем по папкам. Для того, кто подписан на рассылки, весьма полезно раскладывать письма по требуемым каталогам, чтобы: а) не писать сортировщики в каждом клиенте; б) иметь такую же структуру папок при доступе к почте через веб, как и в почтовом клиенте.

Беглое (а потом и достаточно настырное) исследование интернетов показало, что сортировать письма предлагается только с помощью различных скриптов sieve. Но, на мой взгляд, это способ не для пользователей (как в почтовых клиентах), а для администраторов, которые будут фактически не писать правила для сортировщика, а программировать фильтры, используя соответствующий скриптовый язык:

require ["fileinto", "reject", "regex", "subaddress", "envelope"];

#spam
if header :contains "Subject" "[SPAM]" {
   reject "thank you";
   stop;
}
if header :regex "Received" "from ([[:alnum:]\-]{1,}\.){3,}[[:alnum:]]{1,}.*" {
   reject "thank you";
   stop;
}


Я могу в этом разобраться, но на кой, простите, чёрт это нужно? А что делать с «простыми смертными», которым нужен тот же функционал, но для которых приведенное выше сродни китайской грамоте. В наше время, когда системы затачиваются под палец домохозяйки неимение таких же тупых методов сортировки почты, которая используется уже десятилетия как минимум удивляет.

На собственную реализацию сортировки меня натолкнул фрагмент вышеупомянутой статьи, в которой путь для размещения письма exim в секции transports формирует запросом из БД:

mysql_delivery:
  driver = appendfile
  check_string = ""
  create_directory
  delivery_date_add
  <b>directory = ${lookup mysql{SELECT CONCAT('/data/mail/${domain}/', `maildir`) 
      FROM `mailbox` WHERE `username`='${local_part}@${domain}'}}</b>
  directory_mode = 770
  envelope_to_add
  group = mail
  maildir_format
  maildir_tag = ,S=$message_size
  message_prefix = ""
  message_suffix = ""
  mode = 0600


Ничто не мешает слегка дописать запрос и добавить туда имя imap-папки, чтобы работали тупые фильтры на основе определения содержания подстроки в отправителе/теме письма:

mysql_delivery:
  driver = appendfile
  check_string = ""
  create_directory
  delivery_date_add
  <b>directory = ${lookup mysql{SELECT CONCAT('/data/mail/${domain}/', `maildir`, 
        (select dir from sorter where ( 
        ( 
            (locate(text,'${sender_address}')!=0 and target='sender') 
        or 
             (locate(text,'${sg{${extract{Subject}{${sg{${sg{$message_headers}{ }{_spAce_}}}{:_spAce_}{=}}}}}{_spAce_}{ }}')!=0 and target='subject') 
        ) 
        and email='${local_part}@${domain}') or priority=0 order by priority desc limit 1 
        )) 
        FROM `mailbox` WHERE `username`='${local_part}@${domain}'}}</b>
  directory_mode = 770
  envelope_to_add
  group = mail
  maildir_format
  maildir_tag = ,S=$message_size
  message_prefix = ""
  message_suffix = ""
  mode = 0600


После чего пишем 7 кил примитивного кода на PHP (скачать), чтобы создавать правила сортировки для конкретного пользователя и ага — летальный исход потребности сортировки писем реализованы процентов на 80%. А лично мои — на 100%. Правда, неожиданно обнаружилось (а может просто не нашел?), что на этом этапе exim не имеет отдельных переменных под каждый заголовок, поэтому здоровенная борода эксимовых экспансий строк призвана всего лишь выделить заголовок Subject из многострочного поля содержащего сразу все заголовки письма.

Создаем таблицу для хранений правил сортировки с обязательной нулевой записью, которая будет определять размещение письма во «Входящие» если правил не нашлось:

CREATE TABLE `sorter` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `priority` int(11) NOT NULL DEFAULT '0',
  `target` varchar(255) NOT NULL DEFAULT '',
  `text` varchar(255) NOT NULL DEFAULT '',
  `dir` varchar(255) NOT NULL DEFAULT '',
  `email` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
);

INSERT INTO `sorter` SET `priority`=0;


Скриншот админки:

image
Скрипты берут список уже имеющихся у юзера папок на сервере.

Система неидеальна, и имеет как минимум три ограничения:
1. работает с русскими кодировками только если кодировка письма совпадает с кодировкой записей в таблице правил;
2. работает только одно правило в порядке очередности (чем выше, тем приоритетнее), так что правила с логикой И сделать не получится, только ИЛИ;
3. количество полей для обработки ограничено отправителем и темой.

В принципе, можно будет потом допилить под использование для выборок и других заголовков, а не только subject, возможно сделаю, если понадобится. Также была мысль написать плагин для RoundCube, но поглядев на то, как они пишутся, было решено забить на это, хоть и с большой жалостью (авось кто-нть, заинтересовавшийся реализацией, сделает это: таки-улыбка:). Также хочу отметить, что sql-запрос тестировался на последней версии порта MySQL 5.5 для FreeBSD, так что неизвестно, покатит ли этот запрос для более старых версий.

Автор: ingiboy

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js