☰ Оглавление

Иногда требуется организовать отправку писем с картинками. Мне это понадобилось для системы мониторинга. Чтобы скрипт отправлял мне графики. Для этого можно написать программы на любых языках, но самое переносимое решение, работающие на любых системах, — это shell.

MIME e-mail сообщения на shell

Этот пример легко адаптировать к любым надобностям и даже перенести на другие языки, хотя там, возможно, есть более удобные средства для работы с MIME.

Обзор MIME-структуры письма

Будем формировать письмо с двумя вложенными контейнерами:

На верхнем уровне тип будет multipart/related. В этом контейнере первым будет идти текстовый контейнер, а далее картинки.

Текстовый контейнер

Состоит из двух альтернатив (multipart/alternative). Первая — обычный текст для клиентов, которые не понимают HTML. Вторая — HTML. Здесь будут ссылки на картинки.

Картинки

Просто кодируются в base64.

Сам скрипт, формирующий и отправляющий письмо

# список картинок
# он нам понадобится дважды, поэтому кладём его в переменную
# (у такого подхода есть нехорошее ограничение -- имена картинок
# не могу содержать, например, пробелов; но мы закроем на это глаза)
export graph='http http-state loadavg memory ping proc uwsgi'

(

# формируем заголовки письма,
# начало контейнера верхнего уровня,
# первую часть контейнера с текстом (именно текстовую)
# и начало контейнера с HTML

t=`date '+%Y:%m:%d %H:%M:%S'`

cat <<TEXT
To: xxxxx@gmail.com
From: yyyyy@rol.ru
Return-Path: yyyyy@rol.ru
Subject: BHost $t
X-Mailer: BHost dayly report
MIME-Version: 1.0
Content-Type: multipart/related; boundary=XXXXXXXXXXXXXXXXXXXX

--XXXXXXXXXXXXXXXXXXXX
Content-Type: multipart/alternative; boundary=YYYYYYYYYYYYYYYYYYYY

--YYYYYYYYYYYYYYYYYYYY
Content-Type: text/plain; charset=utf8

HTML not supported by email client

--YYYYYYYYYYYYYYYYYYYY
Content-Type: text/html; charset=utf8
Content-Transfer-Encoding: base64

TEXT

# Формируем тело HTML-письма
(
# вывод нескольких полезных команд,
# отражающий текущее положение дел,
# прогоняем через sed, чтобы придать
# всему HTML-вид
(
echo '--- uptime'
uptime
echo '--- who'
who
echo '--- last -10'
last -10
echo '--- df'
df
echo '--- free'
free
echo '--- nginx'
wget -qO- http://localhost:81/stub
echo '--- django'
nc 127.0.0.1 18019
echo '--- ps'
ps auxwwwf
echo '--- netstat'
netstat -ant
echo '--- eof'
) | sed '
s-&-\&amp;-g
s-<-\&lt;-g
s->-\&gt;-g
s-"-\&quot;-g
s- -\&nbsp;-g
s-\t-\&nbsp;\&nbsp;-g
s-^-<nobr>-
s-$-</nobr><br>-
1 i \
<tt>
$ a \
</tt>'
# добавляем ко всему этому ссылки на картинки
# вида
# <img src="cid:N@w-report"><br>
j=0
for i in $graph
do
  j=$(($j+1))
  echo "<img src=\"cid:$j@w-report\"><br>"
done
# и всё это кодируем в base64
) | openssl enc -a

# закрываем контейнер с HTML
cat <<"TEXT"

--YYYYYYYYYYYYYYYYYYYY--
TEXT

# теперь прикрепляем картинки
# в цикле формируем части для каждой
# картинки из списка
j=0
for i in $graph
do
  j=$(($j+1))
  echo '--XXXXXXXXXXXXXXXXXXXX'
  echo "Content-Type: image/png; name=\"$j.png\""
  echo 'Content-Transfer-Encoding: base64'
  echo "X-Attachment-Id: $j@w-report"
  echo "Content-ID: <$j@w-report>"
  echo ''
  cat "../../../var/rrd-graph/$i-1d.png" | openssl enc -a
done
echo '--XXXXXXXXXXXXXXXXXXXX--'

# и весь результат отправляем по почте
#
) 2>report.log |
msmtp --logfile msmtp2.log \
 --host=smtp.gmail.com --port=587 --protocol=smtp \
 --auth=on --user=xxx@xxx.xx \
 --tls=on --tls-certcheck=off \
 --read-envelope-from --read-recipients

Готово.

Если вы планируете запусать это дело из cron или ещё как-то автоматически, то очень полезно будет добавить в начало что-то типа

export LANG=C
export TZ='Europe/Moscow'

cd "${0%/*}"

Успехов!