You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
12 KiB

Syncing with the original repo (#2) * change arvan api script * change Author name * change name actor * Updated --preferred-chain to issue ISRG properly To support different openssl crl2pkcs7 help cli format * dnsapi/pdns: also normalize json response in detecting root zone * Chain (#3408) * fix https://github.com/acmesh-official/acme.sh/issues/3384 match the issuer to the root CA cert subject * fix format * fix https://github.com/acmesh-official/acme.sh/issues/3384 * remove the alt files. https://github.com/acmesh-official/acme.sh/issues/3384 * upgrade freebsd and solaris * duckdns - fix "integer expression expected" errors (#3397) * fix "integer expression expected" errors * duckdns fix * Update dns_duckdns.sh * Update dns_duckdns.sh * Implement smtp notify hook Support notifications via direct SMTP server connection. Uses Python (2.7.x or 3.4+) to communicate with SMTP server. * Make shfmt happy (I'm open to better ways of formatting the heredoc that embeds the Python script.) * Only save config if send is successful * Add instructions for reporting bugs * Prep for curl or Python; clean up SMTP_* variable usage * Implement curl version of smtp notify-hook * More than one blank line is an abomination, apparently I will not try to use whitespace to group code visually * Fix: Unifi deploy hook support Unifi Cloud Key (#3327) * fix: unifi deploy hook also update Cloud Key nginx certs When running on a Unifi Cloud Key device, also deploy to /etc/ssl/private/cloudkey.{crt,key} and reload nginx. This makes the new cert available for the Cloud Key management app running via nginx on port 443 (as well as the port 8443 Unifi Controller app the deploy hook already supported). Fixes #3326 * Improve settings documentation comments * Improve Cloud Key pre-flight error messaging * Fix typo * Add support for UnifiOS (Cloud Key Gen2) Since UnifiOS does not use the Java keystore (like a Unifi Controller or Cloud Key Gen1 deploy), this also reworks the settings validation and error messaging somewhat. * PR review fixes * Detect unsupported Cloud Key java keystore location * Don't try to restart inactive services (and remove extra spaces from reload command) * Clean up error messages and internal variables * Change to _getdeployconf/_savedeployconf * Switch from cp to cat to preserve file permissions * feat: add huaweicloud error handling * fix: fix freebsd and solaris * support openssl 3.0 fix https://github.com/acmesh-official/acme.sh/issues/3399 * make the fix for rsa key only * Use PROJECT_NAME and VER for X-Mailer header Also add X-Mailer header to Python version * Add _clearaccountconf_mutable() * Rework read/save config to not save default values Add and use _readaccountconf_mutable_default and _saveaccountconf_mutable_default helpers to capture common default value handling. New approach also eliminates need for separate underscore-prefixed version of each conf var. * Implement _rfc2822_date helper * Clean email headers and warn on unsupported address format Just in case, make sure CR or NL don't end up in an email header. * Clarify _readaccountconf_mutable_default * Add Date email header in Python implementation * Use email.policy.default in Python 3 implementation Improves standards compatibility and utf-8 handling in Python 3.3-3.8. (email.policy.default becomes the default in Python 3.9.) * Prefer Python to curl when both available * Change default SMTP_SECURE to "tls" Secure by default. Also try to minimize configuration errors. (Many ESPs/ISPs require STARTTLS, and most support it.) * Update dns_dp.sh 没有encode中文字符会导致提交失败 * No need to include EC parameters explicitly with the private key. (they are embedded) * Fixes response handling and thereby allow issuing of subdomain certs * Adds comment * fix https://github.com/acmesh-official/acme.sh/issues/3402 * dnsapi/ionos: Use POST instead of PATCH for adding TXT record The API now supports a POST route for adding records. Therefore checking for already existing records and including them in a PATCH request is no longer necessary. * fix https://github.com/acmesh-official/acme.sh/issues/3433 * fix https://github.com/acmesh-official/acme.sh/issues/3019 * fix format * Update dns_servercow.sh to support wildcard certs Updated dns_servercow.sh to support txt records with multiple entries. This supports wildcard certificates that require txt records with the same name and different contents. * Update dns_servercow.sh to support wildcard certs Updated dns_servercow.sh to support txt records with multiple entries. This supports wildcard certificates that require txt records with the same name and different contents. * fix https://github.com/acmesh-official/acme.sh/issues/3312 * fix format * feat: add dns_porkbun * fix: prevent rate limit Co-authored-by: Vahid Fardi <vahid.fardi@snapp.cab> Co-authored-by: neil <github@neilpang.com> Co-authored-by: Gnought <1684105+gnought@users.noreply.github.com> Co-authored-by: manuel <manuel@mausz.at> Co-authored-by: jerrm <jerrm@users.noreply.github.com> Co-authored-by: medmunds <medmunds@gmail.com> Co-authored-by: Mike Edmunds <github@to.mikeedmunds.com> Co-authored-by: Easton Man <manyang.me@outlook.com> Co-authored-by: czeming <loser_wind@163.com> Co-authored-by: Geert Hendrickx <geert@hendrickx.be> Co-authored-by: Kristian Johansson <kristian.johansson86@gmail.com> Co-authored-by: Lukas Brocke <lukas@brocke.net> Co-authored-by: anom-human <80478363+anom-human@users.noreply.github.com> Co-authored-by: neil <win10@neilpang.com> Co-authored-by: Quentin Dreyer <quentin.dreyer@rgsystem.com>
4 years ago
Syncing with the original repo (#2) * change arvan api script * change Author name * change name actor * Updated --preferred-chain to issue ISRG properly To support different openssl crl2pkcs7 help cli format * dnsapi/pdns: also normalize json response in detecting root zone * Chain (#3408) * fix https://github.com/acmesh-official/acme.sh/issues/3384 match the issuer to the root CA cert subject * fix format * fix https://github.com/acmesh-official/acme.sh/issues/3384 * remove the alt files. https://github.com/acmesh-official/acme.sh/issues/3384 * upgrade freebsd and solaris * duckdns - fix "integer expression expected" errors (#3397) * fix "integer expression expected" errors * duckdns fix * Update dns_duckdns.sh * Update dns_duckdns.sh * Implement smtp notify hook Support notifications via direct SMTP server connection. Uses Python (2.7.x or 3.4+) to communicate with SMTP server. * Make shfmt happy (I'm open to better ways of formatting the heredoc that embeds the Python script.) * Only save config if send is successful * Add instructions for reporting bugs * Prep for curl or Python; clean up SMTP_* variable usage * Implement curl version of smtp notify-hook * More than one blank line is an abomination, apparently I will not try to use whitespace to group code visually * Fix: Unifi deploy hook support Unifi Cloud Key (#3327) * fix: unifi deploy hook also update Cloud Key nginx certs When running on a Unifi Cloud Key device, also deploy to /etc/ssl/private/cloudkey.{crt,key} and reload nginx. This makes the new cert available for the Cloud Key management app running via nginx on port 443 (as well as the port 8443 Unifi Controller app the deploy hook already supported). Fixes #3326 * Improve settings documentation comments * Improve Cloud Key pre-flight error messaging * Fix typo * Add support for UnifiOS (Cloud Key Gen2) Since UnifiOS does not use the Java keystore (like a Unifi Controller or Cloud Key Gen1 deploy), this also reworks the settings validation and error messaging somewhat. * PR review fixes * Detect unsupported Cloud Key java keystore location * Don't try to restart inactive services (and remove extra spaces from reload command) * Clean up error messages and internal variables * Change to _getdeployconf/_savedeployconf * Switch from cp to cat to preserve file permissions * feat: add huaweicloud error handling * fix: fix freebsd and solaris * support openssl 3.0 fix https://github.com/acmesh-official/acme.sh/issues/3399 * make the fix for rsa key only * Use PROJECT_NAME and VER for X-Mailer header Also add X-Mailer header to Python version * Add _clearaccountconf_mutable() * Rework read/save config to not save default values Add and use _readaccountconf_mutable_default and _saveaccountconf_mutable_default helpers to capture common default value handling. New approach also eliminates need for separate underscore-prefixed version of each conf var. * Implement _rfc2822_date helper * Clean email headers and warn on unsupported address format Just in case, make sure CR or NL don't end up in an email header. * Clarify _readaccountconf_mutable_default * Add Date email header in Python implementation * Use email.policy.default in Python 3 implementation Improves standards compatibility and utf-8 handling in Python 3.3-3.8. (email.policy.default becomes the default in Python 3.9.) * Prefer Python to curl when both available * Change default SMTP_SECURE to "tls" Secure by default. Also try to minimize configuration errors. (Many ESPs/ISPs require STARTTLS, and most support it.) * Update dns_dp.sh 没有encode中文字符会导致提交失败 * No need to include EC parameters explicitly with the private key. (they are embedded) * Fixes response handling and thereby allow issuing of subdomain certs * Adds comment * fix https://github.com/acmesh-official/acme.sh/issues/3402 * dnsapi/ionos: Use POST instead of PATCH for adding TXT record The API now supports a POST route for adding records. Therefore checking for already existing records and including them in a PATCH request is no longer necessary. * fix https://github.com/acmesh-official/acme.sh/issues/3433 * fix https://github.com/acmesh-official/acme.sh/issues/3019 * fix format * Update dns_servercow.sh to support wildcard certs Updated dns_servercow.sh to support txt records with multiple entries. This supports wildcard certificates that require txt records with the same name and different contents. * Update dns_servercow.sh to support wildcard certs Updated dns_servercow.sh to support txt records with multiple entries. This supports wildcard certificates that require txt records with the same name and different contents. * fix https://github.com/acmesh-official/acme.sh/issues/3312 * fix format * feat: add dns_porkbun * fix: prevent rate limit Co-authored-by: Vahid Fardi <vahid.fardi@snapp.cab> Co-authored-by: neil <github@neilpang.com> Co-authored-by: Gnought <1684105+gnought@users.noreply.github.com> Co-authored-by: manuel <manuel@mausz.at> Co-authored-by: jerrm <jerrm@users.noreply.github.com> Co-authored-by: medmunds <medmunds@gmail.com> Co-authored-by: Mike Edmunds <github@to.mikeedmunds.com> Co-authored-by: Easton Man <manyang.me@outlook.com> Co-authored-by: czeming <loser_wind@163.com> Co-authored-by: Geert Hendrickx <geert@hendrickx.be> Co-authored-by: Kristian Johansson <kristian.johansson86@gmail.com> Co-authored-by: Lukas Brocke <lukas@brocke.net> Co-authored-by: anom-human <80478363+anom-human@users.noreply.github.com> Co-authored-by: neil <win10@neilpang.com> Co-authored-by: Quentin Dreyer <quentin.dreyer@rgsystem.com>
4 years ago
  1. #!/usr/bin/env sh
  2. # support smtp
  3. # Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3358
  4. # This implementation uses either curl or Python (3 or 2.7).
  5. # (See also the "mail" notify hook, which supports other ways to send mail.)
  6. # SMTP_FROM="from@example.com" # required
  7. # SMTP_TO="to@example.com" # required
  8. # SMTP_HOST="smtp.example.com" # required
  9. # SMTP_PORT="25" # defaults to 25, 465 or 587 depending on SMTP_SECURE
  10. # SMTP_SECURE="tls" # one of "none", "ssl" (implicit TLS, TLS Wrapper), "tls" (explicit TLS, STARTTLS)
  11. # SMTP_USERNAME="" # set if SMTP server requires login
  12. # SMTP_PASSWORD="" # set if SMTP server requires login
  13. # SMTP_TIMEOUT="30" # seconds for SMTP operations to timeout
  14. # SMTP_BIN="/path/to/python_or_curl" # default finds first of python3, python2.7, python, pypy3, pypy, curl on PATH
  15. SMTP_SECURE_DEFAULT="tls"
  16. SMTP_TIMEOUT_DEFAULT="30"
  17. # subject content statuscode
  18. smtp_send() {
  19. SMTP_SUBJECT="$1"
  20. SMTP_CONTENT="$2"
  21. # UNUSED: _statusCode="$3" # 0: success, 1: error 2($RENEW_SKIP): skipped
  22. # Load and validate config:
  23. SMTP_BIN="$(_readaccountconf_mutable_default SMTP_BIN)"
  24. if [ -n "$SMTP_BIN" ] && ! _exists "$SMTP_BIN"; then
  25. _err "SMTP_BIN '$SMTP_BIN' does not exist."
  26. return 1
  27. fi
  28. if [ -z "$SMTP_BIN" ]; then
  29. # Look for a command that can communicate with an SMTP server.
  30. # (Please don't add sendmail, ssmtp, mutt, mail, or msmtp here.
  31. # Those are already handled by the "mail" notify hook.)
  32. for cmd in python3 python2.7 python pypy3 pypy curl; do
  33. if _exists "$cmd"; then
  34. SMTP_BIN="$cmd"
  35. break
  36. fi
  37. done
  38. if [ -z "$SMTP_BIN" ]; then
  39. _err "The smtp notify-hook requires curl or Python, but can't find any."
  40. _err 'If you have one of them, define SMTP_BIN="/path/to/curl_or_python".'
  41. _err 'Otherwise, see if you can use the "mail" notify-hook instead.'
  42. return 1
  43. fi
  44. fi
  45. _debug SMTP_BIN "$SMTP_BIN"
  46. _saveaccountconf_mutable_default SMTP_BIN "$SMTP_BIN"
  47. SMTP_FROM="$(_readaccountconf_mutable_default SMTP_FROM)"
  48. SMTP_FROM="$(_clean_email_header "$SMTP_FROM")"
  49. if [ -z "$SMTP_FROM" ]; then
  50. _err "You must define SMTP_FROM as the sender email address."
  51. return 1
  52. fi
  53. if _email_has_display_name "$SMTP_FROM"; then
  54. _err "SMTP_FROM must be only a simple email address (sender@example.com)."
  55. _err "Change your SMTP_FROM='$SMTP_FROM' to remove the display name."
  56. return 1
  57. fi
  58. _debug SMTP_FROM "$SMTP_FROM"
  59. _saveaccountconf_mutable_default SMTP_FROM "$SMTP_FROM"
  60. SMTP_TO="$(_readaccountconf_mutable_default SMTP_TO)"
  61. SMTP_TO="$(_clean_email_header "$SMTP_TO")"
  62. if [ -z "$SMTP_TO" ]; then
  63. _err "You must define SMTP_TO as the recipient email address(es)."
  64. return 1
  65. fi
  66. if _email_has_display_name "$SMTP_TO"; then
  67. _err "SMTP_TO must be only simple email addresses (to@example.com,to2@example.com)."
  68. _err "Change your SMTP_TO='$SMTP_TO' to remove the display name(s)."
  69. return 1
  70. fi
  71. _debug SMTP_TO "$SMTP_TO"
  72. _saveaccountconf_mutable_default SMTP_TO "$SMTP_TO"
  73. SMTP_HOST="$(_readaccountconf_mutable_default SMTP_HOST)"
  74. if [ -z "$SMTP_HOST" ]; then
  75. _err "You must define SMTP_HOST as the SMTP server hostname."
  76. return 1
  77. fi
  78. _debug SMTP_HOST "$SMTP_HOST"
  79. _saveaccountconf_mutable_default SMTP_HOST "$SMTP_HOST"
  80. SMTP_SECURE="$(_readaccountconf_mutable_default SMTP_SECURE "$SMTP_SECURE_DEFAULT")"
  81. case "$SMTP_SECURE" in
  82. "none") smtp_port_default="25" ;;
  83. "ssl") smtp_port_default="465" ;;
  84. "tls") smtp_port_default="587" ;;
  85. *)
  86. _err "Invalid SMTP_SECURE='$SMTP_SECURE'. It must be 'ssl', 'tls' or 'none'."
  87. return 1
  88. ;;
  89. esac
  90. _debug SMTP_SECURE "$SMTP_SECURE"
  91. _saveaccountconf_mutable_default SMTP_SECURE "$SMTP_SECURE" "$SMTP_SECURE_DEFAULT"
  92. SMTP_PORT="$(_readaccountconf_mutable_default SMTP_PORT "$smtp_port_default")"
  93. case "$SMTP_PORT" in
  94. *[!0-9]*)
  95. _err "Invalid SMTP_PORT='$SMTP_PORT'. It must be a port number."
  96. return 1
  97. ;;
  98. esac
  99. _debug SMTP_PORT "$SMTP_PORT"
  100. _saveaccountconf_mutable_default SMTP_PORT "$SMTP_PORT" "$smtp_port_default"
  101. SMTP_USERNAME="$(_readaccountconf_mutable_default SMTP_USERNAME)"
  102. _debug SMTP_USERNAME "$SMTP_USERNAME"
  103. _saveaccountconf_mutable_default SMTP_USERNAME "$SMTP_USERNAME"
  104. SMTP_PASSWORD="$(_readaccountconf_mutable_default SMTP_PASSWORD)"
  105. _secure_debug SMTP_PASSWORD "$SMTP_PASSWORD"
  106. _saveaccountconf_mutable_default SMTP_PASSWORD "$SMTP_PASSWORD"
  107. SMTP_TIMEOUT="$(_readaccountconf_mutable_default SMTP_TIMEOUT "$SMTP_TIMEOUT_DEFAULT")"
  108. _debug SMTP_TIMEOUT "$SMTP_TIMEOUT"
  109. _saveaccountconf_mutable_default SMTP_TIMEOUT "$SMTP_TIMEOUT" "$SMTP_TIMEOUT_DEFAULT"
  110. SMTP_X_MAILER="$(_clean_email_header "$PROJECT_NAME $VER --notify-hook smtp")"
  111. # Run with --debug 2 (or above) to echo the transcript of the SMTP session.
  112. # Careful: this may include SMTP_PASSWORD in plaintext!
  113. if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then
  114. SMTP_SHOW_TRANSCRIPT="True"
  115. else
  116. SMTP_SHOW_TRANSCRIPT=""
  117. fi
  118. SMTP_SUBJECT=$(_clean_email_header "$SMTP_SUBJECT")
  119. _debug SMTP_SUBJECT "$SMTP_SUBJECT"
  120. _debug SMTP_CONTENT "$SMTP_CONTENT"
  121. # Send the message:
  122. case "$(basename "$SMTP_BIN")" in
  123. curl) _smtp_send=_smtp_send_curl ;;
  124. py*) _smtp_send=_smtp_send_python ;;
  125. *)
  126. _err "Can't figure out how to invoke '$SMTP_BIN'."
  127. _err "Check your SMTP_BIN setting."
  128. return 1
  129. ;;
  130. esac
  131. if ! smtp_output="$($_smtp_send)"; then
  132. _err "Error sending message with $SMTP_BIN."
  133. if [ -n "$smtp_output" ]; then
  134. _err "$smtp_output"
  135. fi
  136. return 1
  137. fi
  138. return 0
  139. }
  140. # Strip CR and NL from text to prevent MIME header injection
  141. # text
  142. _clean_email_header() {
  143. printf "%s" "$(echo "$1" | tr -d "\r\n")"
  144. }
  145. # Simple check for display name in an email address (< > or ")
  146. # email
  147. _email_has_display_name() {
  148. _email="$1"
  149. expr "$_email" : '^.*[<>"]' >/dev/null
  150. }
  151. ##
  152. ## curl smtp sending
  153. ##
  154. # Send the message via curl using SMTP_* variables
  155. _smtp_send_curl() {
  156. # Build curl args in $@
  157. case "$SMTP_SECURE" in
  158. none)
  159. set -- --url "smtp://${SMTP_HOST}:${SMTP_PORT}"
  160. ;;
  161. ssl)
  162. set -- --url "smtps://${SMTP_HOST}:${SMTP_PORT}"
  163. ;;
  164. tls)
  165. set -- --url "smtp://${SMTP_HOST}:${SMTP_PORT}" --ssl-reqd
  166. ;;
  167. *)
  168. # This will only occur if someone adds a new SMTP_SECURE option above
  169. # without updating this code for it.
  170. _err "Unhandled SMTP_SECURE='$SMTP_SECURE' in _smtp_send_curl"
  171. _err "Please re-run with --debug and report a bug."
  172. return 1
  173. ;;
  174. esac
  175. set -- "$@" \
  176. --upload-file - \
  177. --mail-from "$SMTP_FROM" \
  178. --max-time "$SMTP_TIMEOUT"
  179. # Burst comma-separated $SMTP_TO into individual --mail-rcpt args.
  180. _to="${SMTP_TO},"
  181. while [ -n "$_to" ]; do
  182. _rcpt="${_to%%,*}"
  183. _to="${_to#*,}"
  184. set -- "$@" --mail-rcpt "$_rcpt"
  185. done
  186. _smtp_login="${SMTP_USERNAME}:${SMTP_PASSWORD}"
  187. if [ "$_smtp_login" != ":" ]; then
  188. set -- "$@" --user "$_smtp_login"
  189. fi
  190. if [ "$SMTP_SHOW_TRANSCRIPT" = "True" ]; then
  191. set -- "$@" --verbose
  192. else
  193. set -- "$@" --silent --show-error
  194. fi
  195. raw_message="$(_smtp_raw_message)"
  196. _debug2 "curl command:" "$SMTP_BIN" "$*"
  197. _debug2 "raw_message:\n$raw_message"
  198. echo "$raw_message" | "$SMTP_BIN" "$@"
  199. }
  200. # Output an RFC-822 / RFC-5322 email message using SMTP_* variables.
  201. # (This assumes variables have already been cleaned for use in email headers.)
  202. _smtp_raw_message() {
  203. echo "From: $SMTP_FROM"
  204. echo "To: $SMTP_TO"
  205. echo "Subject: $(_mime_encoded_word "$SMTP_SUBJECT")"
  206. echo "Date: $(_rfc2822_date)"
  207. echo "Content-Type: text/plain; charset=utf-8"
  208. echo "X-Mailer: $SMTP_X_MAILER"
  209. echo
  210. echo "$SMTP_CONTENT"
  211. }
  212. # Convert text to RFC-2047 MIME "encoded word" format if it contains non-ASCII chars
  213. # text
  214. _mime_encoded_word() {
  215. _text="$1"
  216. # (regex character ranges like [a-z] can be locale-dependent; enumerate ASCII chars to avoid that)
  217. _ascii='] $`"'"[!#%&'()*+,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ~^_abcdefghijklmnopqrstuvwxyz{|}~-"
  218. if expr "$_text" : "^.*[^$_ascii]" >/dev/null; then
  219. # At least one non-ASCII char; convert entire thing to encoded word
  220. printf "%s" "=?UTF-8?B?$(printf "%s" "$_text" | _base64)?="
  221. else
  222. # Just printable ASCII, no conversion needed
  223. printf "%s" "$_text"
  224. fi
  225. }
  226. # Output current date in RFC-2822 Section 3.3 format as required in email headers
  227. # (e.g., "Mon, 15 Feb 2021 14:22:01 -0800")
  228. _rfc2822_date() {
  229. # Notes:
  230. # - this is deliberately not UTC, because it "SHOULD express local time" per spec
  231. # - the spec requires weekday and month in the C locale (English), not localized
  232. # - this date format specifier has been tested on Linux, Mac, Solaris and FreeBSD
  233. _old_lc_time="$LC_TIME"
  234. LC_TIME=C
  235. date +'%a, %-d %b %Y %H:%M:%S %z'
  236. LC_TIME="$_old_lc_time"
  237. }
  238. ##
  239. ## Python smtp sending
  240. ##
  241. # Send the message via Python using SMTP_* variables
  242. _smtp_send_python() {
  243. _debug "Python version" "$("$SMTP_BIN" --version 2>&1)"
  244. # language=Python
  245. "$SMTP_BIN" <<PYTHON
  246. # This code is meant to work with either Python 2.7.x or Python 3.4+.
  247. try:
  248. try:
  249. from email.message import EmailMessage
  250. from email.policy import default as email_policy_default
  251. except ImportError:
  252. # Python 2 (or < 3.3)
  253. from email.mime.text import MIMEText as EmailMessage
  254. email_policy_default = None
  255. from email.utils import formatdate as rfc2822_date
  256. from smtplib import SMTP, SMTP_SSL, SMTPException
  257. from socket import error as SocketError
  258. except ImportError as err:
  259. print("A required Python standard package is missing. This system may have"
  260. " a reduced version of Python unsuitable for sending mail: %s" % err)
  261. exit(1)
  262. show_transcript = """$SMTP_SHOW_TRANSCRIPT""" == "True"
  263. smtp_host = """$SMTP_HOST"""
  264. smtp_port = int("""$SMTP_PORT""")
  265. smtp_secure = """$SMTP_SECURE"""
  266. username = """$SMTP_USERNAME"""
  267. password = """$SMTP_PASSWORD"""
  268. timeout=int("""$SMTP_TIMEOUT""") # seconds
  269. x_mailer="""$SMTP_X_MAILER"""
  270. from_email="""$SMTP_FROM"""
  271. to_emails="""$SMTP_TO""" # can be comma-separated
  272. subject="""$SMTP_SUBJECT"""
  273. content="""$SMTP_CONTENT"""
  274. try:
  275. msg = EmailMessage(policy=email_policy_default)
  276. msg.set_content(content)
  277. except (AttributeError, TypeError):
  278. # Python 2 MIMEText
  279. msg = EmailMessage(content)
  280. msg["Subject"] = subject
  281. msg["From"] = from_email
  282. msg["To"] = to_emails
  283. msg["Date"] = rfc2822_date(localtime=True)
  284. msg["X-Mailer"] = x_mailer
  285. smtp = None
  286. try:
  287. if smtp_secure == "ssl":
  288. smtp = SMTP_SSL(smtp_host, smtp_port, timeout=timeout)
  289. else:
  290. smtp = SMTP(smtp_host, smtp_port, timeout=timeout)
  291. smtp.set_debuglevel(show_transcript)
  292. if smtp_secure == "tls":
  293. smtp.starttls()
  294. if username or password:
  295. smtp.login(username, password)
  296. smtp.sendmail(msg["From"], msg["To"].split(","), msg.as_string())
  297. except SMTPException as err:
  298. # Output just the error (skip the Python stack trace) for SMTP errors
  299. print("Error sending: %r" % err)
  300. exit(1)
  301. except SocketError as err:
  302. print("Error connecting to %s:%d: %r" % (smtp_host, smtp_port, err))
  303. exit(1)
  304. finally:
  305. if smtp is not None:
  306. smtp.quit()
  307. PYTHON
  308. }
  309. ##
  310. ## Conf helpers
  311. ##
  312. #_readaccountconf_mutable_default name default_value
  313. # Given a name like MY_CONF:
  314. # - if MY_CONF is set and non-empty, output $MY_CONF
  315. # - if MY_CONF is set _empty_, output $default_value
  316. # (lets user `export MY_CONF=` to clear previous saved value
  317. # and return to default, without user having to know default)
  318. # - otherwise if _readaccountconf_mutable MY_CONF is non-empty, return that
  319. # (value of SAVED_MY_CONF from account.conf)
  320. # - otherwise output $default_value
  321. _readaccountconf_mutable_default() {
  322. _name="$1"
  323. _default_value="$2"
  324. eval "_value=\"\$$_name\""
  325. eval "_name_is_set=\"\${${_name}+true}\""
  326. # ($_name_is_set is "true" if $$_name is set to anything, including empty)
  327. if [ -z "${_value}" ] && [ "${_name_is_set:-}" != "true" ]; then
  328. _value="$(_readaccountconf_mutable "$_name")"
  329. fi
  330. if [ -z "${_value}" ]; then
  331. _value="$_default_value"
  332. fi
  333. printf "%s" "$_value"
  334. }
  335. #_saveaccountconf_mutable_default name value default_value base64encode
  336. # Like _saveaccountconf_mutable, but if value is default_value
  337. # then _clearaccountconf_mutable instead
  338. _saveaccountconf_mutable_default() {
  339. _name="$1"
  340. _value="$2"
  341. _default_value="$3"
  342. _base64encode="$4"
  343. if [ "$_value" != "$_default_value" ]; then
  344. _saveaccountconf_mutable "$_name" "$_value" "$_base64encode"
  345. else
  346. _clearaccountconf_mutable "$_name"
  347. fi
  348. }