aboutsummaryrefslogtreecommitdiff
#
msgid ""
msgstr ""

msgid "title: 'Awk snippet: send email to multiple recipients with cURL'"
msgstr ""

msgid "date: 2021-01-12"
msgstr ""

msgid "layout: post"
msgstr ""

msgid "lang: en"
msgstr ""

msgid "ref: awk-snippet-send-email-to-multiple-recipients-with-curl"
msgstr ""

msgid "My requirements for the `sendmail` command were:"
msgstr ""

msgid "store the email in a file, and send it later."
msgstr ""

msgid "send from different addresses, using different SMTP servers;"
msgstr ""

msgid ""
"I couldn't find an MTA that could accomplish that, but I was able to quickly"
" write a solution."
msgstr ""

msgid "The first part was the easiest: store the email in a file:"
msgstr ""

msgid ""
"# ~/.config/mutt/muttrc:\n"
"set sendmail=~/bin/enqueue-email.sh\n"
"\n"
"# ~/bin/enqueue-email.sh:\n"
"#!/bin/sh -eu\n"
"\n"
"cat - > \"$HOME/mbsync/my-queued-emails/$(date -Is)\"\n"
msgstr ""

msgid ""
"Now that I had the email file store locally, I needed a program to send the "
"email from the file, so that I could create a cronjob like:"
msgstr ""

msgid ""
"for f in ~/mbsync/my-queued-emails/*; do\n"
"  ~/bin/dispatch-email.sh \"$f\" && rm \"$f\"\n"
"done\n"
msgstr ""

msgid ""
"The `dispatch-email.sh` would have to look at the `From: ` header and decide"
" which SMTP server to use. As I [found "
"out](https://blog.edmdesigner.com/send-email-from-linux-command-line/) that "
"[curl](https://curl.se/) supports SMTP and is able to send emails, this is "
"what I ended up with:"
msgstr ""

msgid "Most of curl flags used are self-explanatory, except for `$rcpt`."
msgstr ""

msgid ""
"curl connects to the SMTP server, but doesn't set the recipient address by "
"looking at the message. My solution was to generate the curl flags, store "
"them in `$rcpt` and use it unquoted to leverage shell word splitting."
msgstr ""

msgid ""
"To me, the most interesting part was building the `$rcpt` flags. My first "
"instinct was to try grep, but it couldn't print only matches in a regex. As "
"I started to turn towards sed, I envisioned needing something else to loop "
"over the sed output, and I then moved to Awk."
msgstr ""

msgid ""
"$ H='To: to@example.com, to2@example.com\\nCc: cc@example.com, cc2@example.com\\nBcc: bcc@example.com,bcc2@example.com\\n'\n"
"$ printf \"$H\" | awk '/^To: .*$/ { print $0 }'\n"
"To: to@example.com, to2@example.com\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { print m }'\n"
"awk: ligne de commande:1: (FILENAME=- FNR=1) fatal : tentative d'utilisation du tableau « m » dans un contexte scalaire\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { print m[0] }'\n"
"To: to@example.com, to2@example.com\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { print m[1] }'\n"
"to@example.com, to2@example.com\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { split(m[1], tos, \" \"); print tos }'\n"
"awk: ligne de commande:1: (FILENAME=- FNR=1) fatal : tentative d'utilisation du tableau « tos » dans un contexte scalaire\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { split(m[1], tos, \" \"); print tos[0] }'\n"
"\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { split(m[1], tos, \" \"); print tos[1] }'\n"
"to@example.com,\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { split(m[1], tos, \" \"); print tos[2] }'\n"
"to2@example.com\n"
"$ printf \"$H\" | awk 'match($0, /^To: (.*)$/, m) { split(m[1], tos, \" \"); print tos[3] }'\n"
"\n"
msgstr ""

msgid ""
"(This isn't the verbatim interactive session, but a cleaned version to make "
"it more readable.)"
msgstr ""

msgid ""
"At this point, I realized I needed a for loop over the `tos` array, and I "
"moved the Awk snippet into the `~/bin/dispatch-email.sh`. I liked the final "
"thing:"
msgstr ""

msgid ""
"match($0, /^(To|Cc|Bcc): (.*)$/, m) {\n"
"  split(m[2], tos, \",\")\n"
"  for (i in tos) {\n"
"    print \"--mail-rcpt \" tos[i]\n"
"  }\n"
"}\n"
msgstr ""

msgid ""
"As I learn more about Awk, I feel that it is too undervalued, as many people"
" turn to Perl or other programming languages when Awk suffices. The "
"advantage is pretty clear: writing programs that run on any POSIX system, "
"without extra dependencies required."
msgstr ""

msgid "Coding to the standards is underrated."
msgstr ""

msgid ""
"#!/bin/sh -eu\n"
"\n"
"F=\"$1\"\n"
"\n"
"rcpt=\"$(awk '\n"
"  match($0, /^(To|Cc|Bcc): (.*)$/, m) {\n"
"    split(m[2], tos, \",\")\n"
"    for (i in tos) {\n"
"      print \"--mail-rcpt \" tos[i]\n"
"    }\n"
"  }\n"
"'  \"$F\")\"\n"
"\n"
"if grep -qE '^From: .*<addr@server1\\.org>$' \"$F\"; then\n"
"  curl                                                      \\\n"
"    -s                                                      \\\n"
"    --url smtp://smtp.server1.org:587                       \\\n"
"    --ssl-reqd                                              \\\n"
"    --mail-from addr@server1.org                            \\\n"
"    $rcpt                                                   \\\n"
"    --user 'addr@server1.org:my-long-and-secure-passphrase' \\\n"
"    --upload-file \"$F\"\n"
"elif grep -qE '^From: .*<addr@server2\\.org>$' \"$F\"; then\n"
"  curl                                                      \\\n"
"    -s                                                      \\\n"
"    --url smtp://smtp.server2.org:587                       \\\n"
"    --ssl-reqd                                              \\\n"
"    --mail-from addr@server2.org                            \\\n"
"    $rcpt                                                   \\\n"
"    --user 'addr@server2.org:my-long-and-secure-passphrase' \\\n"
"    --upload-file \"$F\"\n"
"else\n"
"  echo 'Bad \"From: \" address'\n"
"  exit 1\n"
"fi\n"
msgstr ""

msgid ""
"In the short Awk snippet, 3 things were new to me: the `match(...)`, "
"`split(...)` and `for () {}`. The only other function I have ever used was "
"`gsub(...)`, but these new ones felt similar enough that I could almost "
"guess their behaviour and arguments. `match(...)` stores the matches of a "
"regex on the given array positionally, and `split(...)` stores the chunks in"
" the given array."
msgstr ""

msgid "I even did it incrementally:"
msgstr ""

msgid ""
"As I experiment with [Neomutt](https://neomutt.org/), I wanted to keep being"
" able to enqueue emails for sending later like my previous setup, so that I "
"didn't rely on having an internet connection."
msgstr ""

#~ msgid ""
#~ "As I experimented with [Neomutt](https://neomutt.org/), I wanted to keep "
#~ "being able to enqueue emails for sending later like my previous setup, so "
#~ "that I didn't rely on having an internet connection."
#~ msgstr ""

#~ msgid "I even did this incrementally:"
#~ msgstr ""

#~ msgid ""
#~ "In the short Awk snippet, 3 things were new to me: the `match(...)`, "
#~ "`split(...)` and `for () {}`. The only other function I have ever used was "
#~ "`gsub(...)`, but these new felt similar enough that I could almost guess "
#~ "their behaviour. `match(...)` stores the matches of a regex on the given "
#~ "array positionally, and `split(...)` stores the chunks in the given array."
#~ msgstr ""

#~ msgid ""
#~ "As I experimented with [Neomutt](https://neomutt.org/), I wanted to keep "
#~ "being able to enqueue emails for sending later, so that I didn't rely on "
#~ "having an internet connection."
#~ msgstr ""

#~ msgid ""
#~ "#!/bin/sh -eu\n"
#~ "\n"
#~ "F=\"$1\"\n"
#~ "\n"
#~ "rcpt=\"$(awk '\n"
#~ "  match($0, /^(To|Cc|Bcc): (.*)$/, m) {\n"
#~ "    split(m[2], tos, \",\")\n"
#~ "    for (i in tos) {\n"
#~ "      print \"--mail-rcpt \" tos[i]\n"
#~ "    }\n"
#~ "  }\n"
#~ "'  \"$F\")\"\n"
#~ "\n"
#~ "if grep -qE '^From: .*<addr@server1\\.org>$' \"$F\"; then\n"
#~ "  curl                                                      \\\n"
#~ "    -s                                                      \\\n"
#~ "    --url smtp://smtp.server1.org:587                       \\\n"
#~ "    --ssl-reqd                                              \\\n"
#~ "    --mail-from addr@server1.org                            \\\n"
#~ "    $rcpt                                                   \\\n"
#~ "    --user 'addr@server1.org:my-long-and-secure-passphrase' \\\n"
#~ "    --upload-file \"$F\"\n"
#~ "eliif grep -qE '^From: .*<addr@server2\\.org>$' \"$F\"; then\n"
#~ "  curl                                                      \\\n"
#~ "    -s                                                      \\\n"
#~ "    --url smtp://smtp.server2.org:587                       \\\n"
#~ "    --ssl-reqd                                              \\\n"
#~ "    --mail-from addr@server2.org                            \\\n"
#~ "    $rcpt                                                   \\\n"
#~ "    --user 'addr@server2.org:my-long-and-secure-passphrase' \\\n"
#~ "    --upload-file \"$F\"\n"
#~ "else\n"
#~ "  echo 'Bad \"From: \" address'\n"
#~ "  exit 1\n"
#~ "fi\n"
#~ msgstr ""