UnixMountainSkiFun

Unix Горы Лыжи

02-06-2008 18:09

Рецепты


Рецепт -- это способ, которым вы можете объяснить Procmail-у, что ему требуется найти и что затем сделать, когда он это найдет. Рецепт это сердцевина Procmail-а. Требуется создать рецепт для каждой новой штучки, которую потребуется найти, и действия которое потребуется выполнить. Имеется довольно много хороших Internet-ресурсов по созданию рецептов, но лучше всего начать конечно же с web-сайта Procmail. Однако куда поместить рецепты, когда они будут созданы? Их помещают, конечно же, в домашнем каталоге, в файле .procmailrc. Вот простенький, начальный .procmailrc с двумя рецептами:

 01 #----------------------
 02 # Мой файл .procmailrc 
 03 #----------------------
 04 SHELL=/bin/bash  # Путь к Вашему sh (выполните "which sh", чтобы найти его)
 05 MAILDIR=${HOME}/Mail # e-mail перенаправится сюда, после того как Ваш клиент получит его
 06 LOGFILE=${MAILDIR}/procmail.log
 07 LOG="--- Logging ${LOGFILE} for ${LOGNAME}, "
 08
 09 #------------------------------------------------------
 10 # Ниже следуют Ваши рецепты (порядок предельно важен!)
 11 #------------------------------------------------------
 12 :0
 13 * ^Subject: test test test
 14 /dev/null
 15
 16 #------------------------------------------------------------------
 17 # Принимаем все остальные e-mail в Ваш почтовый ящик, по-умолчанию
 18 #------------------------------------------------------------------
 19 :0:
 20 ${DEFAULT}

Я добавил номера строк в этот пример, чтобы было удобно ссылаться на строки, требующие внимания. Не помещайте эти номера в ваш файл, так как они внесут путаницу в работу рецептов.

Строки с 04-ой по 07-ую вполне очевидны, поэтому просто прочтите комментарии к ним. Строки с 12-ой по 14-ую составляют первый рецепт. Этот рецепт будет искать в e-mail-файле подстроку "Subject: test test test", начинающуюся с начала строки. Помните, что все e-mail-ы пересылаются Procmail-у, в виде одного большого файла, однако Procmail знает, как разбить этот файл на отдельные e-mail-ы, и каким образом проверять шаблон, который Вы ему предоставили. В данном случае, строка 13 это и есть шаблон, с помощью которого осуществляется поиск. Знак ^ перед словом Subject означает, что искомая подстрока должна быть выровнена на начало анализируемой строки. Строка 14 предписывает Procmail-у перенаправлять e-mail, удовлетворяющий такому шаблону, в электро-помойку (черный ящик, или битодробилку, -- уж как вам больше нравится). Каждый e-mail прошедший первый рецепт передается на следующий, и так до тех пор, пока не обнаружится совпадение. Как вы уже наверное поняли, поисковый критерий представляет из себя регулярное выражение (regular expression).

Если вы знакомы с регулярными выражениями, то наверняка уже знаете как сформировать поисковый шаблон, для любого текста, который потребуется найти. Если же не знакомы, то я крайне рекомендую разобраться с ними. Это один из самых мощных и часто используемых инструментариев, доступных в Linux. Строка 14 предписывает Procmail-у вычленить e-mail, для которого подходит данное регулярное выражение и отправить его на битодробилку (null device). Другими словами он копирует текст письма на битодробилку и удаляет его из e-mail-файла. Вы можете сказать, -- "спам-фильтр"? Почему бы и нет, -- я могу придумать несколько правил, отбрасывающие нежелательные e-mail. Ну, и конечно же, в Internet можно найти многочисленные примеры спам-фильтров, построенных на основе Procmail. Помните о Google.com -- вашем секретном оружии.

Другим замечательным использованием Procmail является распределение e-mail, полученных из различных списков рассылки, по различным каталогам в вашем e-mail-каталоге. Эта тема выходит за рамки рассмотрения этой главы, однако вы можете поэкспериментировать и почитать списки рассылки наиболее часто задаваемых вопросов (FAQ), чтобы получить ответы на свои вопросы, относительно того, как добиться той или иной функциональности. Следующий рецепт является наверное самым важным из всех рецептов.

Итак, второй рецепт располагается на 19 и 20-ой строках, -- этот рецепт можно назвать ловушкой, которая "отлавливает" все e-mail-ы, которые не были обработаны предыдущими рецептами. Когда я написал свой первый файл .procmailrc, то я не позаботился о том, чтобы включить в него этот рецепт, и в результате вся моя почта "улетучилась". Не совершайте моей ошибки. Этот последний рецепт предписывает Procmail-у помещать все e-mail-ы, для которых "не отработали" все предыдущие рецепты, в их обычное месторасположение (/var/mail/your_account). Погодите -- я почему-то думал, что этим местом является почтовый каталог в моем домашнем каталоге? Как раз-таки и нет. Должно быть вы помните, что когда Fetchmail забирает e-mail с Вашего ISP, то он доставляет этот e-mail Вашему MDA, а MDA, в свою очередь, просматривает ваш домашний каталог на наличие файла .forward, перед тем, как записать e-mail в файл /var/mail/your-account. Файл .forward предписывает вашему MDA отправлять всякий входящий e-mail на адрес, указанный внутри этого файла. Совершенно ясно и то, что вы можете перенаправить, посредством pipe, такой e-mail некоторой программе, например Procmail-у. В этом случае Procmail, будет выступать в качестве фильтра, и будет иметь возможность удалять e-mail из нашего огромного e-mail-файла. Когда Procmail выполнит свою задачу, то неотфильтрованный e-mail попадет в /var/mail/your_account, откуда он может быть выбран с помощью обычного почтового клиента. Резонно? Я поговорю о содержимом файла .forward чуть позже, в этой главе. Итак, что же собственно кроется за всеми этими штучками, по типу :0, *, :0:> и ${DEFAULT}? Вот таблица, которая является попыткой внести некоторую ясность в эту загадочную символику:

:0Начало рецепта
:0:Начало рецепта с использованием блокировочного файла. Использование блокировочный файла, не позволяет нескольким процессам Procmail одновременно записывать почту в один и тот же файл. (Выдержка из FAQ по Procmail: Используйте блокировочный файл, когда используйте доставку в файл. Не используйте блокировочный файл, когда используете доставку в /dev/null.
*Начало условия.
${DEFAULT}Это ваш почтовый учетный файл по-умолчанию (/var/mail/your_account)
|Труба (pipe) -- выполнить команду

Фильтрация e-mail это конечно хорошо, а что насчет выполнения команд? Нет проблем! Procmail имеет поддержку и для этого, и может исполнять даже целые скрипты. Рецепт, который я использую для e-mail-консоли выглядит так:

 :0
 * ^Subject: <console/>
 |/usr/bin/perl /home/stmurphy/bin/email_console.pl

В нашем рецепте для e-mail-консоли, выполняется поиск подстроки в строке темы письма, которая говорит о том, что e-mail будет содержать команды, которые необходимо выполнить, а их результат вернуть по e-mail отправителю. Лучший способ разбирать и исполнять команды в таком e-mail, -- это написать Perl-скрипт и предписать Procmail-у исполнять такой скрипт, "скармливая" целиком весь e-mail этому скрипту. Почему важно действовать именно так, станет ясно из раздела "Обеспечиваем безопасность". Должно быть вы уже заметили, что я поместил скрипт email_console.pl в каталог bin, своего домашнего каталога. Это место, в котором я размещаю различные утилиты, которые применимы только для моей учетной записи. Теперь сосредоточимся на Perl-скрипте.

Скрипт

Перед тем, как приступить к скрипту, давайте определимся с требованиями. Для того, чтобы Procmail мог определить и обработать e-mail, содержащий команды для Perl-скрипта, положим такому e-mail содержать в строке темы письма следующую фразу:

 <console/>

Достаточно просто. Я решил не использовать теги в стиле XML. Я могу использовать любое слово, однако нельзя забывать и о случайностях. Все будет замечательно, пока кто-нибудь не пришлет e-mail c темой письма похоже на такую:

 <console/> is the XML tag I was talking about... let's talk

Конечно, со временем вы превратитесь в Procmail-гуру, и найдете массу способов избежать таких неприятностей. Однако продолжим, -- тело e-mail будет содержать (а может и НЕ содержать) несколько команд, которые требуется выполнить. Perl-скрипт не должно "перекособочить" от пустого e-mail, и кроме того он должен быть в состоянии выполнить более чем одну команду. Результат выполнения каждой команды должен быть отделен некоторым горизонтальным разделителем:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Уяснили? Отлично. Еще одно требование состоит в том, что каждая команда должна быть окружена тегами exec. Например:

 <exec>......</exec>

Прикольно? Ну, по-крайней мере, я уж точно так считаю! Такой подход позволит разбирать файл с меньшими усилиями. Чтобы еще больше упростить себе жизнь, давайте также потребуем, чтобы каждая команда располагалась на отдельной строке. Разбор одной команды "размазанной" по нескольким строкам, слишком сложен для нашего простого проекта. И наконец, отправка результата работы команд по e-mail, будет основываться на информации содержащейся в полях From: или Reply-To:. Чтобы выполнить эту задачу, мы будем использовать xmail, а посему убедитесь, что он установлен в вашей системе.

Рисунок 7-5 показывает пример e-mail-файла, который я отправил со своего web-email, digitalman@musician.org (хостинг www.mail.ru). Охохошеньки, столько всякой всячины! Однако по большей части все понятно. В этом e-mail, я прошу e-mail-консоль исполнить три различных командных блока. Первый блок -- это команда free. Второй -- это набор из трех команд cd, pwd и ls. И в последнем командном блоке команда netstat''. Задача Perl-скрипта состоит в том, чтобы разобраться во всей этой мешанине. К счастью, это последнее, что нам потребуется сделать. Итак, пишем Perl-скрипт, который разбирает e-mail. Вот его код:

Рисунок 7-5. Заголовок e-mail, полученного от digitalman.

 #!/usr/bin/perl

 use IPC::Open3;
 use Symbol;

 #-----------------------
 # setup vars for open3
 #-----------------------
 $WTR = gensym();
 $RDR = gensym();
 $ERR = gensym();

 #---------------------------
 # some handy variable defs
 #---------------------------
 $logfile = "/home/stmurphy/.email_console.log";
 $line_sep = 0;
 $separator = "\n" . "~' x 72 . "\n";
 $theOutput = "\n\n";
 $reply_subject = "E-mail Console Results";
 $signature_line =  "\n--\nOutput generated by the E-mail Console\n";
 $no_commands_mesg = "No commands found to execute.";
 $from = "";
 $reply_to = "";

 #---------------
 # open log file
 #---------------
 open LOG, "<<$logfile";

 #--------------------------
 # our parse/execution loop
 #--------------------------
 while(<>) {

     #------------------------
     # get the sender address
     #------------------------
     if ( $_ =~ m/From: (.*)/) {
         $from = $1;
     }

     #---------------------------------
     # get the sender address override
     #---------------------------------
     if ( $_ =~ m/Reply-To: (.*)/) {
         $reply_to = $1;
     }

     #---------------------------
     # look for <exec>...</exec>
     # and process the command
     #---------------------------
     if ( $_ =~ m/^<exec>(.*)<\/exec>/ ) {
         undef $cmd_output;
         if ($line_sep) {
             $theOutput .= $separator;
         } else {
             $line_sep = 1;
         }
         $who = ($reply_to ne "") ? "F:$from R:$reply_to" : $from;
         print LOG scalar localtime() . " $who executed [$1]\n";
         $theOutput .= "Executing [$1]\n\n";

         #------------------------
         # execute the command(s)
         #------------------------
         open3($WTR, $RDR, $ERR, $1);
         close($WTR);
         while (<$RDR>) {
             $cmd_output .= $_;
         }
         while (<$ERR>) {
             $cmd_output .= $_;
         }
         $theOutput .= $cmd_output;
     }
 }

 #-----------------------------
 # report if there was
 # no command executed at all
 #-----------------------------
 if (!$line_sep) {
     $theOutput .= $no_commands_mesg;
 }

 #---------------------------
 # tack on a handy dandy
 # signature line
 #---------------------------
 $theOutput .= $signature_line;

 #-----------------------------
 # override the from address
 # with the reply-to address
 #-----------------------------
 if ($reply_to ne "") {
     $from = $reply_to;
 }

 #----------------------
 # close the log file
 #----------------------
 close LOG;

 #-----------------------
 # send the reply e-mail
 # with the execution
 # results
 #-----------------------
 exec "echo \"$theOutput\" | mailx -s \"$reply_subject\" \"$from\"";

 exit;

Я не буду построчно разбирать этот код, так как он довольно объемный. Отмечу, что можно найти и другие варианты написания такого кода. Я старался сделать его как можно проще. Учитывая это, необходимо отметить также, что необходимо внести некоторые улучшения в процедуру, обрабатывающую критические ситуации, -- например, чтобы она сообщала e-mail-консоли о вашей попытке выполнить интерактивное приложение, типа top. Если вы попытаетесь выполнить top то процесс email_console.pl просто "зависнет", ожидая клавиатурного ввода (который никогда не поступит). Обратите внимание, что этот код принимает несколько строк <exec> ... </exec> и исполняет содержащиеся в них команды в контексте пользователя, от имени которого выполняется Fetchmail. Это конечно же может быть root, однако я крайне рекомендую дважды подумать, перед тем как позволить всякому встречному-поперечному исполнять команды по e-mail , от имени суперпользователя. Опасно! Кроме того, этот скрипт позволяет формировать цепочки команд, с использованием && || и ; (если конечно же shell, под которым запущен Perl-скрипт, позволяет это использовать). Теперь я могу совершенно спокойно сказать, что проект близок к завершению, -- наберитесь еще немного терпения.

Собираем все вместе

Последнее, что нам осталось сделать -- это создать файл .forward в вашем домашнем каталоге. Именно таким образом Procmail сможет быть запущен вашим MDA.

 |IFS=' '  && exec /usr/bin/procmail

Убедитесь, что первый символ вертикальная черта (pipe |), и что нет пробела до и после следующей команды. Иначе это может стать причиной странного поведения. (Выдержка из FAQ по Procmail: "IFS=' ' устанавливает внутренний разделитель shell-а в одиночный пробел. Это необходимо для того, чтобы предотвратить некоторые из давнишних взломов Sendmail-а, основанные на хитрых настройках IFS-а.")

Итак, и это мы сделали. Теперь вы должны уже быть в состоянии отправить сами себе e-mail со строкой <console/> в теме и произвольными командами, заключенными в XML-подобные теги <exec>...</exec>, в теле письма, и ваша e-mail-консоль должна "воспрянуть ото сна" и выполнить свою задачу. Конечно, на данный момент, даже я смогу выполнить команду на вашей системе, если узнаю ваш e-mail-адрес.

Итак, мы получили e-mail-консоль, полностью открытую и небезопасную. (Или это вас устраивает?) Тем не менее, чтобы не позволить нежелательным персонам исполнять команды на вашей системе посредством e-mail-консоли, требуется некий способ определения того, кто отправляет команды, а также, дозволено ли ему исполнять такие команды. Кроме того, информация, которая возвращается вам по e-mail-у, представляется открытым текстом. Если вы не желаете, чтобы результаты работы команд представлялись открытым текстом, то его потребуется зашифровать. Для решения обоих этих задач, мы будем использовать другой очень удобный инструмент Linux, -- GnuPG.

<< Procmail | Multi Tool Linux | Обеспечиваем безопасность >>


edit RightSideBar