В конторе, имеющей собственный почтовый домен и сервер, возникла необходимость рассылать внутренние уведомления не только в почтовые ящики пользователей, но и в некоторых случаях одновременно дублировать тему писем (subject) как SMS на мобильные номера сотрудников.
Так как почтовый сервер работает тут под sendmail, у которого встроено умение обработки "plussed users", естественным образом получился формат адреса для такого "письма/SMS": user+79998765432@domain.ru. Почта, отправленная на такой адрес, должна быть доставлена по адресу user@domain.ru (включая все возможные трансляции этого адреса - алиасы/virtusertable/userdb/etc.) плюс заголовок письма отправлен как SMS на указанный номер. Сам адрес user+79998765432@domain.ru может получиться в результате разворота другого алиаса или списка рассылки, то есть в теле и заголовках письма упоминается необязательно. Но, разумеется, он упоминается в конверте (envelope-to).
Идеальным решением этой задачи для sendmail является создание собственного "mailer" - доставщика почты, аналогичного встроенным в sendmail доставщикам esmtp (перенаправляющего письмо по сети другому серверу), mail.local (доставляющего почту в локальный файл mbox) и прочим. Mailer может общаться с sendmail по протоколу LMTP либо быть просто скриптом, запускаемым из sendmail и получающим письмо через стандартный вход, а параметры конверта через аргументы командной строки.
В .mc-файл конфигурации sendmail необходимо добавить строчки:
define(`SMS_MAILER_PATH', `/path/to/script')dnl
define(`SMS_MAILER_ARGS', `script ${client_addr} ${client_name} $f $h $u')dnl
define(`SMS_MAILER_USER', `username')dnl
MAILER(sms)dnlПоследняя строка подключает возможность направлять письма на наш мейлер, а предыдущие - настраивают его. Первая строка указывает путь к нашему скрипту, который будет запускаться sendmail'ом (для каких именно писем будет использован этот мейлер - об этом ниже).
Вторая строка описывает формат командной строки, через которую sendmail передаёт скрипту параметры - он умеет передавать их великое множество, все они описаны в документации, в Sendmail Installation and Operation Guide в разделе "5.2. D -- Define Macro". Файл doc/op.me в дистрибутиве sendmail, который в FreeBSD стандартно расположен по пути /usr/src/contrib/sendmail/doc/op.me, переводится в plaintext командой nroff -me op.me | col -bx > op.txt. В данном случае мы передаём нашему скрипту адрес SMTP-клиента (IPv4 или IPv6), отправившего почту нашему sendmail, имя хоста этого SMTP-клиента (или тот же IP-адрес в квадратных скобках, если в обратной зоне DNS имени для него не нашлось), адрес e-mail отправителя из конверта письма (envelope-from), домен получателя (domain.ru в примере выше) и адрес(а) назначения в этом домене.
Третья строчка, необязательная, указывает имя системного (псевдо)пользователя, с привилегиями которого sendmail запустит наш скрипт, тут можно указать и группу в формате username:group (можно использовать как имена, так и цифровые значения). Если эту строку не указать, по умолчанию sendmail использует имя пользователя, заданного параметром DefaultUser в конфигурации sendmail (mailnull в FreeBSD по умолчанию).
Для того, чтобы .mc-файл с такими строчками скомпилировался в sendmail.cf, необходимо создать файл cf/mailer/sms.m4 в инсталляционном каталоге sendmail (/usr/share/sendmail/cf/mailer/sms.m4 для штатного sendmail в FreeBSD). Этот файл мы создаём, скопировав и немного отредактировав лежащий там же fax.m4:
PUSHDIVERT(-1)
ifdef(`SMS_MAILER_ARGS',,
`define(`SMS_MAILER_ARGS', mail2sms $h $u)')
ifdef(`SMS_MAILER_PATH',,
`define(`SMS_MAILER_PATH', /path/to/mail2sms)')
define(`_SMS_QGRP', `ifelse(defn(`SMS_MAILER_QGRP'),`',`', ` Q=SMS_MAILER_QGRP,')')dnl
define(`_SMS_USER', `ifelse(defn(`SMS_MAILER_USER'),`',`', ` U=SMS_MAILER_USER,')')dnl
POPDIVERT
####################################
### SMS Mailer specification ###
####################################
VERSIONID(`$Id: sms.m4$')
Msms, P=SMS_MAILER_PATH, F=Acdl9, S=14, R=24, r=1,_SMS_USER
T=X-Phone/X-FAX/X-Unix,_SMS_QGRP
A=SMS_MAILER_ARGS
LOCAL_CONFIG
CPSMS(Такое описание мейлера позволяет ещё задавать define-параметр SMS_MAILER_QGRP, если вам по какой-то причине хочется, чтобы почта для нашего мейлера укладывалась не в общую очередь доставки, а в отдельную (возможно, с отдельными параметрами тонкой настройки), но для нас такие тонкости пока не требуются.)
В этом описании для нас особую важность несут два момента. Во-первых, именно наличие $u в параметре A= (то есть, в SMS_MAILER_ARGS - в аргументах скрипта) означает, что sendmail не будет пытаться общаться с ним по протоколу LMTP, а просто отдаст письмо на стандартный ввод (при отсутствии $u в скрипте пришлось бы реализовывать LMTP). Во-вторых, флаги F=Acdl9 обозначают следующее:
- A - пользовательская часть адреса назначения письма (user part, до знака @ в адресе) будет разворачиваться по файлу aliases, как и для "обычной" доставки почты;
- c - sendmail будет удалять "комментарии" из адреса назначения перед передачей его мейлеру: "Phrase <address>" или "address (Comment)" превратится просто в "address" (без кавычек);
- d - удалять угловые скобки <address> из адреса при передаче его мейлеру;
- 1 - вырезать нулевые символы (ASCII 0) из письма при передаче его мейлеру;
- 9 - если тело письма оформлено по стандарту MIME как text/plain и закодировано в 7-битный формат (напр., в base64), sendmail раскодирует его в 8bit перед передачей мейлеру.
Эти и все остальные возможные флаги, позволяющие гибко настраивать взаимодействие sendmail и mailer, описаны в упомянутой выше документации в разделе "5.4. M -- Define Mailer". В частности, отсутствие флага m означает, что при отправке одного письма множеству получателей одного домена через наш мейлер sendmail не станет подставлять весь список в командную строку на место параметра $u, а станет запускать наш скрипт по отдельному разу на каждого получателя, подставляя в $u каждый раз только одно имя назначения (что ухудшает эффективность при большом темпе поступления почты, но упрощает написание скрипта - мы не ожидаем здесь большого темпа).
Последняя строка в описании мейлера (CPSMS) создаёт "псевдодомен" первого уровня по имени SMS и говорит sendmail'у не пытаться искать домены вида domain.ru.SMS в DNS. Вместо этого, почта на адреса такого вида будет передаваться нашему мейлеру. Для того, чтобы это происходило, нам придётся приложить такой патч к стандартному proto.m4 из дистрибутива (к сожалению, другими способами добиться этого нельзя): proto.m4.diff. Сам файл proto.m4 находится подкаталоге cf/m4 инсталляционного каталога sendmail (/usr/share/sendmail/cf/m4/proto.m4 в FreeBSD по умолчанию). (Update: вообще-то можно обойтись без патча ценой отказа от использования стандартного мейлера fax по назначению (для рассылки факсов), перенастройки его на наш скрипт и использования предопределенного псевдодомена FAX вместо SMS для наших целей. Update 2: а также можно использовать LOCAL_NET_CONFIG в .mc-файле и вставить прямо в него правило, направляющее письма к домену .SMS в наш мейлер).
Теперь, если в нашем .mc есть FEATURE(virtusertable) (по умолчанию есть в штатном конфиге для FreeBSD), мы можем написать в /etc/mail/virtusertable:
username++@domain.ru %1%3@domain.ru.SMS ++@anotherdomain.ru %1%3@anotherdomain.ru.SMS
После этого почта на любые "plussed" адреса для username@domain.ru (то есть, на адреса с "добавкой" символа + и произвольной "добавки" к имени далее) будет доставляться через наш мейлер. А для домена anotherdomain.ru почта на любые адреса с "плюсом" будет доставляться через наш мейлер.
Отметим, что почта на адреса "без плюса" будет ходить, как и раньше, через другие (стандартные) мейлеры.
Такая настройка мейлера оставляет нашему скрипту всего ничего: проверить аргументы на соответствие внутриконторским правилам доступа к SMS-сервису (во избежание спам-рассылок через алиасы вида maillist: dir+79876543210, user+79871234567), скормить без изменений полученную со стандартного ввода почту обратно sendmail'у, запустив отдельный процесс его командой /usr/sbin/sendmail -oem -i -f $from $box@$domain (вырезав номер телефона из адреса предварительно) и любым способом доставить содержимое темы письма на указанный телефонный номер (в этой конторе используется iqsms.ru).