Перевод + комментарии.
Оригинал статьи: FileMaker 18: How to Send HTML Emails Using Insert from URL
Автор статьи: Mislav Kos
Обновление от 15 августа 2019 :
Оригинальный демо-файл использует функцию TextEncode для преобразования тела письма в файл. Эта функция начинает работать мучительно долго, если email содержит «тяжелые» прикрепленные файлы, например, требуется больше минуты, чтобы преобразовать в файл письмо с аттачментом весом в 3 МБ.
Демо-файл был заменен более новой версией, использующей специальные команды (script steps) для записи почтового сообщения во временную папку и последующего считывания этого файла в переменную-контейнер. С большими прикрепленными файлами (attachments) это работает намного лучше.
Обновление от 11 сентября 2019:
Заменен демо-файл, исправлен баг.
HTML Emails (электронные письма в формате HTML)
FileMaker 18 добавил в список поддерживаемых интернет-протоколов, поддерживаемых командой Insert From URL еще несколько: SMB, LDAP(S), SMTP(S). Полный перечень поддерживаемых протоколов теперь включает HTTP(S), FTP(S), FILE, SMB, LDAP(S), SMTP(S). Эта статья акцентирует внимание на использовании cURL и SMTP протокола для отправки электронной почты в формате HTML.
До FileMaker 18 письма в простом текстовом формате можно было отправлять с помощью команды Send Mail. FileMaker 17 добавил возможность включать в письмо несколько прикрепленных файлов. Теперь же можно отправлять письма с любым форматированием с множеством прикрепленных файлов. Но эта процедура не так проста, как команда Send Mail. Эта статья разжевывает многое из того, что вы должны знать, а для дополнительного практического изучения прилагается демо-файл.
Мой комментарий. На самом деле все операции с протоколами передачи данных в сети выполняет не FileMaker, а специальная библиотека libcurl, которая установлена на всех операционных системах. С помощью этой же библиотеки трансфер данных осуществляют другие программы (например, PHP). FileMaker посредством команды Insert From URL и ее настроек позволяет обратиться к этой библиотеке и использовать ее возможности.
Отсюда следует, что
а) список поддерживаемых протоколов и в дальнейшем может расширяться;
б) справка по libcurl имеет для разработчиков известную ценность 🙂
Insert From URL
Чтобы отправить письмо [в оригинале — в формате HTML, но вообще это касается любых писем — А.В.], требуется указать следующие параметры команды Insert From URL:
- Target — сохраняет результат выполнения команды cURL
- URL — определяет адрес SMTP сервера, к которому будет обращаться cURL
- Verify SSL Certificates — определяет, нужно ли проверять SSL сертификат сервера SMTP
- cURL options — указывает опции, используемые для оптимальной настройки команды cURL
URL
Параметр URL указывает, какой именно протокол (SMTP или SMTPS) должен быть использован, правильное доменное имя почтового сервере и порт.
Вот несколько примеров для SMTP URLs:
- smtp://smtp.mydomain.com:25/
- smtps://internal-smtp.mydomain.com:465/
Мой комментарий. Протокол smtps следует указывать, если для диалога с почтовым сервером вы используете SSL-соединение. В большинстве почтовых серверов для SSL соединения используется порт 465 (в оригинальной статье в примере указан порт 587)
cURL Options
cURL опции указывают дополнительные инструкции для доставки почтовых сообщений. В таблице ниже описаны и прокомментированы некоторые опции (обязательные и просто полезные):
--mail-from <address> |
Это email address человека, отправляющего почту. Указывайте только адрес; напр., llopez@email.com. Не добавляйте имя, как в примере ниже: Liam Lopez <llopez@email.com>. |
–-mail-rcpt <address> |
Используйте эту опцию, чтобы передать “To”, “Cc”, и “Bcc” адреса. Не помещайте всех получателей в одну строку. Вместо этого, повторите эту опцию для каждого получателя. Например:
--mail-rcpt cturner@email.com --mail-rcpt awright@email.com |
–-user <account>:<password> |
Не требуется, если SMTP server не требует аутентификации. |
--upload-file <file> |
Эта опция сообщает cURL где найти почтовое сообщение, которое должно быть отправлено (почтовое сообщение — это именно файл/контейнер, а не текст). В файлмейкере можно написать переменную ( напр., $mailFile) вместо <file>. Получить эту переменную можно преобразованием текста в контейнер функцией TextEncode($MessageAsText; «utf-8»; 3)
Далее мы рассмотрим формат файла почтового сообщения более подробно. |
–-trace <trace> |
Эта опция заставит сохранить всю информацию об исполнении команды, что может быть весьма полезно для отладки. В файлмейкере можно на месте <trace> просто указать переменную, например, $trace |
––show-error |
Эта опция заставит функцию Get(LastExternalErrorDetail) возвращать информацию об ошибке |
–-ssl-reqd |
Требуется SSL/TLS. В URL в этом случае в качестве протокола указывается “smtps”. |
––ssl |
Пытаться использовать (но не обязательно) SSL/TLS. |
***
Мой комментарий. Последние две опции мне ни разу не приходилось использовать для серверов, использующих ssl соединение. Достаточно было указать протокол smtps и порт 465
На этом месте я позволю себе небольшое отступление. Я расскажу о том, как работает SMTP протокол, как физически происходит отправка почты. Это даст понимание, какие cURL опции вообще требуются и почему.
Во время отправки почты происходит серия шагов, приложение как бы вступает в «диалог» с почтовым сервером, начиная обмениваться с ним сообщениями. Для того, чтобы этот диалог состоялся, приложение должно должно прежде всего указать сетевой адрес SMTP сервера и порт. В команде Insert From URL адрес задается параметром URL. Приложение «подаст сигнал» почтовому серверу, что у него есть новое письмо для отправки. Приложение указывает, от чьего имени будет отправляться сообщение (–mail-from <address>), сервер проверяет валидность адреса этого почтового ящика и что он не внесен в черные списки, после этого он устанавливает, должна ли проводиться аутентификация, проверяет логин и пароль ( –user <account>:<password>) и сообщает о своей готовности принять почту. Далее приложение передает адреса, на которые сообщение должно быть отправлено (–mail-rcpt <address>), они верифицируются сервером. Наконец, сервер сообщает о готовности принять собственно почтовое сообщение (–upload-file <file>) и только тогда ему передается собственно файл электронного письма. Когда сервер принял файл, он передает код ответа ОК, а затем сообщает о том, удалось ли ему немедленно отправить почтовое сообщение адресату, либо же сообщение поставлено в очередь для отправки. Все это (диалог с сервером) делается приложением автоматически в течение сеанса связи с сервером, и повлиять на это разработчик не может никак.
Протокол организует отправку письма таким образом, чтобы нагрузка на сервер была минимальной. Электронное письмо и вся сопутствующая информация отправляются не одним пакетом, а дозированно, частями. В самом деле, зачем серверу загружать в себя целиком все сообщение (которое вместе с приложенными файлами может достигать огромных размеров), если потом окажется, что не был указан адресат или отправитель не знает логина и пароля к почтовому ящику?
Здесь важно понять и запомнить только один момент. Как мы увидим далее, само почтовое сообщение (message, –upload-file <file>) ТОЖЕ содержит в себе информацию FROM, TO, CC, BCC. Но все, что указывается в теле письма, никак не влияет на то, кому на самом деле письмо направляется. Это влияет лишь на то, как письмо отображается у получателя в почтовом клиенте. Информация может абсолютно не совпадать: то есть письмо может быть отправлено с одного почтового ящика, а у получателя в почтовом клиенте в графе FROM отображается совершенно иное; письмо адресовано множеству людей, а получатель в графе TO видит лишь себя…
Итак, при отправке почтового сообщения приложение (FileMaker + curl) передает информацию, состоящую из двух частей. Часть информации (опции cURL) влияет на то, каким сервером, с какого почтового ящика, кому сообщение будет направляться. Другая часть информации — собственно сообщение (содержится в файле –upload-file <file>) — влияет на то, что увидит пользователь в своем почтовом клиенте и как это будет почтовым клиентом отображаться. Следующие разделы статьи как раз посвящены тому, как правильно составлять письмо и как управлять отображением сообщения у конечного получателя.
Для примера — стандартный набор cURL опций для команды Insert From URL:
--mail-from dino@gmail.com --mail-rcpt onkel@gmail.com --mail-rcpt tante@gmail.com --mail-rcpt schwester@gmail.com --user dino@gmail.com:Fdv9kK0sfR2 --dump-header $cURL_headerDump --show-error --trace $trace --upload-file $message
***
Message Format [формат сообщения]
Адрес почтового сервера (SMTP URL) и опции cURL сообщают утилите cURL, куда отправлять письмо, кому и как, но они прямо не указывают, ЧТО отправить. cURL опции лишь содержат ссылку на файл с письмом, который включает в себя собственно содержание того, что должно быть отправлено. Этот файл, называемый «сообщением» (message), должен быть составлен в соответствии с определенными правилами форматирования.
Правила могут быть довольно сложными, эта сложность в значительной степени обусловлена тем, что каждый новый почтовый стандарт стремится быть совместимым с предыдущими версиями, которые он заменяет. Примером этого является правило, требующее использовать 7-битные US-ASCII символы в каждой строке текста, при этом длина строки не должна превышать 78 символов (подробнее об этом позже).
В ходе моего тестирования я обнаружил различия в степени соблюдения некоторых из этих правил. Я предполагаю, что это происходит из-за различий в том, как различные SMTP-серверы и почтовые клиенты реализовали стандарты, определяющие, как работает интернет-почта, подобно тому, как поддержка HTML и JavaScript по-разному реализована в разных веб-браузерах. Урок из этого состоит в том, что вы можете в обход стандарта заставить что-то работать для определенного SMTP-сервера или почтового клиента, но если один из этих факторов впоследствии изменится, это может привести к нарушению функциональности
MIME Formatting
В простейшем случае, если почтовое сообщение состоит только из символов ASCII, и ни одна из строк в сообщении не превышает 998 символов, и письмо не имеет прикрепленных файлов, то сообщение может быть составлено всего из одной «части». В более сложных случаях, почтовое сообщение может использовать форматирование MIME (Multipurpose Internet Mail Extensions) для того, чтобы можно было в одном письме использовать разные кодировки, прикреплять файлы, или добавлять альтернативные версии сообщения.
Почтовое сообщение всегда состоит из двух секций: «заголовка» и «тела». В случае, если наше письмо состоит из нескольких MIME частей (multipart message, составное сообщение), то его «тело» разделяется на множество самостоятельных частей, каждое из которых в свою очередь будет иметь собственные «заголовок» и «тело».
В каждой части такого составного сообщения в его заголовке должен быть указан его MIME-тип, чтобы явно обозначить тип контента, включенного в эту часть
Ниже приведены некоторые MIME-типы и их обозначения :
- Plain text: text/plain
- HTML text: text/html
- PNG image: image/png
- PDF: application/pdf
- Zip: application/zip
Полный перечень типов MIME вы можете найти здесь.
Чтобы сообщить почтовому клиенту, где заканчивается одна часть и начинается другая, каждая часть составного сообщения разделяется специальным маркером (boundary), который должен представлять собой строку из печатаемых 7-битных ASCII символов (коды с 32 по 126) длиной в 70 символов или меньше. Эта строка не должна больше встречаться нигде во вложенных частях. Вот пример допустимого маркера: gc0p4Jq0M2Yt08j34c0p.
Каждый раз, когда маркер используется [в тексте сообщения для разделения частей], он сопровождается двумя дефисами слева, а если этот маркер употреблен в последний раз, чтобы показать завершение части, то два дефиса добавляются и справа.
(Мой комментарий. Любое почтовое сообщение — это ничто иное, как длинная простыня текста, которая составлена в строгом соответствии с требованиями стандартов). Для того, чтобы почтовый клиент правильно отобразил это письмо, мы должны явно указать, из скольких частей состоит письмо, как эти части можно отделить друг от друга ( с помощью какого маркера) и как следует интерпретировать (отображать) ту или иную часть. Благодаря таким «подсказкам» (указание MIME-типа) почтовый клиент покажет простой текст как простой текст, HTML текст отобразит отформатированным, прикрепленную картинку отобразит как картинку, а прикрепленный PDF файл отобразит как соответствующего вида иконку).
Поэтому для каждой части предусмотрен собственный заголовок)
MIME Parts (MIME-разделы)
Существует три типа разделов MIME и, следовательно, три вида маркеров.
Alternative: Маркер “alternative” отделяет альтернативные части сообщения (внутри раздела Alternative). Например, вы отправляете письмо в формате HTML, но опасаетесь, что получатель не сможет прочитать это письмо, потому что пользуется слишком старым почтовым клиентом: его приложение не отображает форматированный текст. Тогда вы в почтовом сообщении можете дополнить HTML-версию письма альтернативной текстовой версией; она будет отображена, если по каким-то причинам невозможно отобразить HTML. Строго говоря, альтернативных частей может быть сколько угодно (например, PDF/HTML/plain text), но получатель при просмотре почтового сообщения увидит только одну из них.
Два других типа MIME-разделов связаны с вложениями (attachments). Вложения могут быть встроены в само сообщение электронной почты (напр., в виде картинки, баннера), а могут быть прикреплены к письму таким образом, что прежде чем их открыть, получатель письма должен будет их скачать (такие вложения обычно отображаются в виде значка внизу письма).
Related: маркеры «related» отделяют встроенные вложения одно от другого и от сообщения, в которое они встраиваются. MIME-раздел Related может использоваться, например, для оформления красивого HTML текста с картинками или фотографиями (HTML будет ссылаться на картинки, которые прикреплены к письму).
Mixed: Маркеры «mixed» отделяют не встраиваемые вложения одно от другого и от остальной части сообщения. Стандартное письмо из приложения файлмейкер с одним или несколькими вложениями, оформляется с помощью именно этого MIME-раздела.
Вероятно на этом месте у вас уже закружилась голова от обилия деталей, поэтому, надеюсь, иллюстрация ниже поможет лучше во всем разобраться.
Секция основного заголовка может содержать такие поля, как From, To, Cc, Subject, и Date, но помните, что поля To и CC не используются для того, чтобы определить, кому письмо должно быть доставлено. Такая информация определяется cURL опциями (о чем рассказано выше). Аналогом может служить бумажное письмо, которое содержит адрес доставки и на конверте, и в самом письме. Для доставки используется только адрес, указанный на конверте. Адрес же, отображаемый в верхней части письма, помещается туда только для информации.
MIME Format Example (Пример MIME-форматирования)
Ниже приведен пример простого почтового сообщения без MIME форматирования (без MIME разделов):
From: Liam Lopez <llopez@email.com> To: Cameron Turner <cturner@email.com>, April Wright <awright@email.com>, Theodore Hall <thall@email.com> Cc: Zara Allen <zallen@email.com> Subject: Test Date: Wed, 14 May 2019 19:34:21 Content-Type: text/html; charset="utf8" <html><body>Hello, here is my <b>bold</b> email message.</body></html>
Несколько моментов, на которые стоит обратить внимание (!!!):
- «Тело» почтового сообщения отделяется от секции заголовка пустой строкой.
- Каждая строка отделяется символами CRLF (перевод каретки+новая строка).
- Каждая строка должна иметь длину менее 78 символов. Технический лимит составляет на самом деле 998 символов, но рекомендуется ограничиться 78. Более длинные строки должны разбиваться на несколько строк, начинающихся с символа «пробел».
- Сообщение (и «заголовок» и «тело») должны состоять из 7-битных символов ASCII.
- Адреса Bcc обычно не включаются в заголовок письма.
(Мой комментарий. Пробелы в начале и особенно в конце строки в секции заголовка сложно разглядеть, но именно они обычно и служат источником проблем. Если письмо доходит адресату в «странном виде», проверяйте пробелы)
Для MIME-форматированных сообщений, не-ASCII данные могут быть конвертированы в 7-bit ASCII с помощью кодирования в “quoted-printable” или base64 (RFC 2045). Если контент сообщения состоит преимущественно из латиницы, то кодирование “quoted-printable” считается предпочтительным, поскольку упрощает решение возникающих проблем. Обе названные схемы кодирования автоматически разбивают контент на строки длиной 76 символов.
Quoted-printable Encoded Text
Ниже пример того, как выглядит текст, закодированный в quoted-printable:
Original:
Congratulations to Luka Modrić ??⚽ for winning the Ballon d'Or!
Quoted-printable:
Congratulations to Luka Modri=C4=87 =F0=9F=87=AD=F0=9F=87=B7=E2=9A=BD for w= inning the Ballon d'Or!
К сожалению, FileMaker не имеет встроенной функции кодирования в quoted-printable, поэтому я использовал кодирование в base64 в файле примера.
Additional MIME Examples (дополнительные примеры использования MIME)
Вот несколько дополнительных наглядных примеров, показывающих как составляются почтовые сообщения ( MIME и не MIME) .
Знак абзаца (¶) используется для обозначения мест, где следует вставить пустую строку. Для упрощения, некоторые фрагменты текста представлены «заполнителями» (placeholders). Последние обозначены угловыми скобками, например: <<EMAIL_MESSAGE_HTML_TEXT>>.
HTML Email Only
Первый пример показывает простое электронное письмо в формате HTML, оформленное без использования MIME
Когда я тестировал отправку писем этого типа, у меня получалось включать в текст символы не ASCII, но стандарт указывает, что должны использоваться только печатаемые 7-битные ASCII символы. Это еще один пример высказанного ранее мнения о том, что разные SMTP-серверы и почтовые клиенты реализуют стандарты по-разному, в том числе создавая поддержку, которая позволяет расширить правила. Тем не менее, если вы хотите быть уверенными, что ваша имплементация отправки почты будет продолжать работать независимо от того, какой сервер или клиент используется, лучше придерживаться правил, установленных стандартами.
HTML Email + Attached Files (HTML письмо + вложенные файлы)
Второй пример показывает два вложения, добавленные к HTML письму. В этом случае вложения НЕ встроены в сообщение, они отображаются в виде иконок, на которые нужно кликнуть, чтобы скачать.
<<preamble message>> здесь добавлено для того, чтобы почтовые клиенты, не совместимые с MIME, могли отобразить сообщение, понятное пользователю. Преамбула может содержать какой угодно текст, например:
This is a message in MIME format. If you see this, your mail reader does not support this format.
HTML Email + Embedded Files + Attached Files (HTML письмо + встроенные файлы + прикрепленные файлы)
Следующие пример добавляет два дополнительных вложения, но не так, как предыдущие. Эти файлы встроены в письмо, составляют часть его содержимого. Такой эффект достигается, если параметру «content disposition» присвоить значение «inline» .
Обычно в текст письма встраивают файлы-картинки. Когда HTML для электронного письма построен, в нем можно прописать ссылку на эти вложения, используя идентификатор (Content ID), который определен в заголовке. Например, предположим, что content ID для одного из вложений определен следующим образом:
Content-ID: <image001.png@01D4FC56.1590DFB0>
Тогда картинка внутри HTML будет иметь такую ссылку:
<img src="cid:image001.png@01D4FC56.1590DFB0">
Plain Text Email + HTML Email + Embedded Files + Attached Files
Последний пример использует маркер «alternative» для добавления plain text версии электронного сообщения.
Using Encoded-Words in Message Headers (использование Encoded-Words в заголовках сообщений)
Предшествующие примеры описывали, как можно закодировать тело (body) сообщения, чтобы учесть наличие в нем не ASCII символов, но что если заголовок сообщения (header) тоже содержит неподдерживаемые символы?
Например, имя получателя или тема письма могут содержать символы за пределами 7-битного диапазона ASCII. В этом случае, вы можете использовать Encoded-Words, чтобы конвертировать последовательность не поддерживаемых символов, используя следующий синтаксис:
=?charset?encoding?encoded-text?=
Предположим, например, что тема электронного письма состоит просто из следующего улыбающегося эмодзи: ?
Будучи преобразованным в base64 encoded-word, заголовок темы сообщения может отображаться как:
Subject: =?UTF-8?B?8J+Yiw==?=
В этом примере, кодировка (charset) указана как as “UTF-8”, конвертация задана литерой “B” ( base64), и собственно конвертированный текст указан как “8J+Yiw==”, что является base64 эквивалентом смайлика ?
Конвертировать текст можно двумя способами: base64 (опция задается литерой “B”) и quoted-printable (опция задается литерой «Q»)
Похожим образом, следующий заголовок получателя письма (To)…
To: 老子 <laotzu@email.com>
…может быть преобразован в:
To: =?UTF-8?B?6ICB5a2Q?= <laotzu@email.com>
Чтобы больше узнать о правилах и синтаксе преобразования encoded-word, читайте стандарт RFC 2047.
Conclusions and Demo File (выводы и демо-файл)
Теперь у нас есть возможность отправлять почту в формате HTML, но это достигается более сложным способом по сравнению с командой Send Mail.
Прилагаемый демо-файл включает в себя сценарий, который берет большую часть работы на себя. Сценарий написан так, что его можно копировать, переносить в ваши решения без необходимости создавать новые таблицы или поля. Однако скрипт использует пользовательские функции “GetMIMEType” и “ErrorMessage”, поэтому вам придется скопировать их перед копированием скрипта.
Демо-файл поддерживает полностью стилизованные письма, которые можно создавать, используя HTML или форматирование текста в файлмейкере (используя функцию GetAsCSS). Вложения могут быть обозначены как встроенные (“Inline”), или как обычные, не встроенные (“Attachment”).
Однако, демонстрационный файл не поддерживает альтернативные части MIME, которые используются для предоставления альтернативных версий почтового сообщения. И он не выполняет разбиения строк или кодирования заголовков. Так что если заголовки в вашем электронном сообщений (напр, тема или адрес электронной почты получателя) содержат не ASCII символы или имеют длину больше 998 символов, письмо возможно не будет отправлено.
Если у вас нет SMTP-сервера, доступного для тестирования, вы можете создать Gmail акаунт и использовать SMTP-сервер Gmail. Если у вас включена двухфакторная аутентификация (а вы должны ее включить), вам потребуется создать пароль для конкретного приложения. Вы можете следовать этим инструкциям, чтобы сделать так.
Ну, и в заключении я хотел бы поблагодарить моих коллег Brian Engert и Marcelo Piñeyro за их помощь в подготовке данного поста и демо-файла.
References (ссылки)
- Message format for non-MIME emails: https://en.wikipedia.org/wiki/Email#Message_format
- RFC 5322 standard for non-MIME emails: https://tools.ietf.org/html/rfc5322
- Wikipedia article on MIME messages (includes links to the relevant RFC standards): https://en.wikipedia.org/wiki/MIME
- Message header fields: https://www.iana.org/assignments/message-headers/message-headers.xhtml
- Encoded-word syntax (RFP 2047): https://tools.ietf.org/html/rfc2047
- Complete list of MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types
- An excellent guide to multi-part MIME messages written by Daniel Clark: http://qcode.co.uk/post/70
Мой комментарий. Последнюю часть я оставил без изменений. От себя добавлю ссылки на переведенные на русский язык стандарты и спецификации, и прочие полезные ресурсы по теме
RFC 5321 — Протокол SMTP