]>
Commit | Line | Data |
---|---|---|
b50e701c | 1 | #!/usr/bin/env sh |
2 | ||
3 | # support smtp | |
4 | ||
e272fde9 | 5 | # Please report bugs to https://github.com/acmesh-official/acme.sh/issues/3358 |
6 | ||
65a1b892 | 7 | # This implementation uses either curl or Python (3 or 2.7). |
8 | # (See also the "mail" notify hook, which supports other ways to send mail.) | |
2d9506eb | 9 | |
10 | # SMTP_FROM="from@example.com" # required | |
11 | # SMTP_TO="to@example.com" # required | |
12 | # SMTP_HOST="smtp.example.com" # required | |
13 | # SMTP_PORT="25" # defaults to 25, 465 or 587 depending on SMTP_SECURE | |
14 | # SMTP_SECURE="none" # one of "none", "ssl" (implicit TLS, TLS Wrapper), "tls" (explicit TLS, STARTTLS) | |
15 | # SMTP_USERNAME="" # set if SMTP server requires login | |
16 | # SMTP_PASSWORD="" # set if SMTP server requires login | |
65a1b892 | 17 | # SMTP_TIMEOUT="30" # seconds for SMTP operations to timeout |
18 | # SMTP_BIN="/path/to/curl_or_python" # default finds first of curl, python3, or python on PATH | |
2d9506eb | 19 | |
65a1b892 | 20 | # subject content statuscode |
b50e701c | 21 | smtp_send() { |
65a1b892 | 22 | _SMTP_SUBJECT="$1" |
23 | _SMTP_CONTENT="$2" | |
24 | # UNUSED: _statusCode="$3" # 0: success, 1: error 2($RENEW_SKIP): skipped | |
25 | ||
26 | # Load config: | |
27 | SMTP_FROM="${SMTP_FROM:-$(_readaccountconf_mutable SMTP_FROM)}" | |
28 | SMTP_TO="${SMTP_TO:-$(_readaccountconf_mutable SMTP_TO)}" | |
29 | SMTP_HOST="${SMTP_HOST:-$(_readaccountconf_mutable SMTP_HOST)}" | |
30 | SMTP_PORT="${SMTP_PORT:-$(_readaccountconf_mutable SMTP_PORT)}" | |
31 | SMTP_SECURE="${SMTP_SECURE:-$(_readaccountconf_mutable SMTP_SECURE)}" | |
32 | SMTP_USERNAME="${SMTP_USERNAME:-$(_readaccountconf_mutable SMTP_USERNAME)}" | |
33 | SMTP_PASSWORD="${SMTP_PASSWORD:-$(_readaccountconf_mutable SMTP_PASSWORD)}" | |
34 | SMTP_TIMEOUT="${SMTP_TIMEOUT:-$(_readaccountconf_mutable SMTP_TIMEOUT)}" | |
35 | SMTP_BIN="${SMTP_BIN:-$(_readaccountconf_mutable SMTP_BIN)}" | |
36 | ||
37 | _debug "SMTP_FROM" "$SMTP_FROM" | |
38 | _debug "SMTP_TO" "$SMTP_TO" | |
39 | _debug "SMTP_HOST" "$SMTP_HOST" | |
40 | _debug "SMTP_PORT" "$SMTP_PORT" | |
41 | _debug "SMTP_SECURE" "$SMTP_SECURE" | |
42 | _debug "SMTP_USERNAME" "$SMTP_USERNAME" | |
43 | _secure_debug "SMTP_PASSWORD" "$SMTP_PASSWORD" | |
44 | _debug "SMTP_TIMEOUT" "$SMTP_TIMEOUT" | |
45 | _debug "SMTP_BIN" "$SMTP_BIN" | |
46 | ||
47 | _debug "_SMTP_SUBJECT" "$_SMTP_SUBJECT" | |
48 | _debug "_SMTP_CONTENT" "$_SMTP_CONTENT" | |
49 | ||
50 | # Validate config and apply defaults: | |
51 | # _SMTP_* variables are the resolved (with defaults) versions of SMTP_*. | |
52 | # (The _SMTP_* versions will not be stored in account conf.) | |
53 | ||
54 | if [ -n "$SMTP_BIN" ] && ! _exists "$SMTP_BIN"; then | |
55 | _err "SMTP_BIN '$SMTP_BIN' does not exist." | |
56 | return 1 | |
57 | fi | |
58 | _SMTP_BIN="$SMTP_BIN" | |
59 | if [ -z "$_SMTP_BIN" ]; then | |
60 | # Look for a command that can communicate with an SMTP server. | |
61 | # (Please don't add sendmail, ssmtp, mutt, mail, or msmtp here. | |
62 | # Those are already handled by the "mail" notify hook.) | |
63 | for cmd in curl python3 python2.7 python pypy3 pypy; do | |
64 | if _exists "$cmd"; then | |
65 | _SMTP_BIN="$cmd" | |
66 | break | |
67 | fi | |
68 | done | |
69 | if [ -z "$_SMTP_BIN" ]; then | |
70 | _err "The smtp notify-hook requires curl or Python, but can't find any." | |
71 | _err 'If you have one of them, define SMTP_BIN="/path/to/curl_or_python".' | |
72 | _err 'Otherwise, see if you can use the "mail" notify-hook instead.' | |
2d9506eb | 73 | return 1 |
74 | fi | |
65a1b892 | 75 | _debug "_SMTP_BIN" "$_SMTP_BIN" |
2d9506eb | 76 | fi |
2d9506eb | 77 | |
2d9506eb | 78 | if [ -z "$SMTP_FROM" ]; then |
79 | _err "You must define SMTP_FROM as the sender email address." | |
80 | return 1 | |
81 | fi | |
65a1b892 | 82 | _SMTP_FROM="$SMTP_FROM" |
2d9506eb | 83 | |
2d9506eb | 84 | if [ -z "$SMTP_TO" ]; then |
85 | _err "You must define SMTP_TO as the recipient email address." | |
86 | return 1 | |
87 | fi | |
65a1b892 | 88 | _SMTP_TO="$SMTP_TO" |
2d9506eb | 89 | |
2d9506eb | 90 | if [ -z "$SMTP_HOST" ]; then |
91 | _err "You must define SMTP_HOST as the SMTP server hostname." | |
92 | return 1 | |
93 | fi | |
65a1b892 | 94 | _SMTP_HOST="$SMTP_HOST" |
2d9506eb | 95 | |
65a1b892 | 96 | _SMTP_SECURE="${SMTP_SECURE:-none}" |
97 | case "$_SMTP_SECURE" in | |
98 | "none") smtp_default_port="25" ;; | |
99 | "ssl") smtp_default_port="465" ;; | |
100 | "tls") smtp_default_port="587" ;; | |
6e61c34f | 101 | *) |
102 | _err "Invalid SMTP_SECURE='$SMTP_SECURE'. It must be 'ssl', 'tls' or 'none'." | |
103 | return 1 | |
104 | ;; | |
2d9506eb | 105 | esac |
106 | ||
65a1b892 | 107 | _SMTP_PORT="${SMTP_PORT:-$smtp_default_port}" |
108 | if [ -z "$SMTP_PORT" ]; then | |
109 | _debug "_SMTP_PORT" "$_SMTP_PORT" | |
110 | fi | |
2d9506eb | 111 | |
65a1b892 | 112 | _SMTP_USERNAME="$SMTP_USERNAME" |
113 | _SMTP_PASSWORD="$SMTP_PASSWORD" | |
114 | _SMTP_TIMEOUT="${SMTP_TIMEOUT:-30}" | |
115 | ||
116 | # Run with --debug 2 (or above) to echo the transcript of the SMTP session. | |
117 | # Careful: this may include SMTP_PASSWORD in plaintext! | |
118 | if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then | |
119 | _SMTP_SHOW_TRANSCRIPT="True" | |
120 | else | |
121 | _SMTP_SHOW_TRANSCRIPT="" | |
122 | fi | |
2d9506eb | 123 | |
d48bff0e | 124 | # Send the message: |
65a1b892 | 125 | case "$(basename "$_SMTP_BIN")" in |
126 | curl) _smtp_send=_smtp_send_curl ;; | |
127 | py*) _smtp_send=_smtp_send_python ;; | |
128 | *) | |
129 | _err "Can't figure out how to invoke $_SMTP_BIN." | |
130 | _err "Please re-run with --debug and report a bug." | |
131 | return 1 | |
132 | ;; | |
133 | esac | |
134 | ||
135 | if ! smtp_output="$($_smtp_send)"; then | |
136 | _err "Error sending message with $_SMTP_BIN." | |
137 | _err "${smtp_output:-(No additional details; try --debug or --debug 2)}" | |
d48bff0e | 138 | return 1 |
139 | fi | |
140 | ||
65a1b892 | 141 | # Save config only if send was successful: |
142 | _saveaccountconf_mutable SMTP_BIN "$SMTP_BIN" | |
2d9506eb | 143 | _saveaccountconf_mutable SMTP_FROM "$SMTP_FROM" |
144 | _saveaccountconf_mutable SMTP_TO "$SMTP_TO" | |
145 | _saveaccountconf_mutable SMTP_HOST "$SMTP_HOST" | |
146 | _saveaccountconf_mutable SMTP_PORT "$SMTP_PORT" | |
147 | _saveaccountconf_mutable SMTP_SECURE "$SMTP_SECURE" | |
148 | _saveaccountconf_mutable SMTP_USERNAME "$SMTP_USERNAME" | |
149 | _saveaccountconf_mutable SMTP_PASSWORD "$SMTP_PASSWORD" | |
150 | _saveaccountconf_mutable SMTP_TIMEOUT "$SMTP_TIMEOUT" | |
151 | ||
2d9506eb | 152 | return 0 |
153 | } | |
154 | ||
8de3698b | 155 | |
65a1b892 | 156 | # Send the message via curl using _SMTP_* variables |
157 | _smtp_send_curl() { | |
158 | # TODO: implement | |
159 | echo "_smtp_send_curl not implemented" | |
160 | return 1 | |
161 | } | |
2d9506eb | 162 | |
65a1b892 | 163 | |
164 | # Send the message via Python using _SMTP_* variables | |
165 | _smtp_send_python() { | |
166 | _debug "Python version" "$("$_SMTP_BIN" --version 2>&1)" | |
2d9506eb | 167 | |
168 | # language=Python | |
65a1b892 | 169 | "$_SMTP_BIN" <<EOF |
2d9506eb | 170 | # This code is meant to work with either Python 2.7.x or Python 3.4+. |
171 | try: | |
172 | try: | |
173 | from email.message import EmailMessage | |
174 | except ImportError: | |
175 | from email.mime.text import MIMEText as EmailMessage # Python 2 | |
176 | from smtplib import SMTP, SMTP_SSL, SMTPException | |
177 | from socket import error as SocketError | |
178 | except ImportError as err: | |
179 | print("A required Python standard package is missing. This system may have" | |
180 | " a reduced version of Python unsuitable for sending mail: %s" % err) | |
181 | exit(1) | |
182 | ||
65a1b892 | 183 | show_transcript = """$_SMTP_SHOW_TRANSCRIPT""" == "True" |
2d9506eb | 184 | |
65a1b892 | 185 | smtp_host = """$_SMTP_HOST""" |
186 | smtp_port = int("""$_SMTP_PORT""") | |
187 | smtp_secure = """$_SMTP_SECURE""" | |
188 | username = """$_SMTP_USERNAME""" | |
189 | password = """$_SMTP_PASSWORD""" | |
190 | timeout=int("""$_SMTP_TIMEOUT""") # seconds | |
2d9506eb | 191 | |
65a1b892 | 192 | from_email="""$_SMTP_FROM""" |
193 | to_emails="""$_SMTP_TO""" # can be comma-separated | |
194 | subject="""$_SMTP_SUBJECT""" | |
195 | content="""$_SMTP_CONTENT""" | |
2d9506eb | 196 | |
197 | try: | |
198 | msg = EmailMessage() | |
199 | msg.set_content(content) | |
200 | except (AttributeError, TypeError): | |
201 | # Python 2 MIMEText | |
202 | msg = EmailMessage(content) | |
203 | msg["Subject"] = subject | |
204 | msg["From"] = from_email | |
205 | msg["To"] = to_emails | |
206 | ||
207 | smtp = None | |
208 | try: | |
209 | if smtp_secure == "ssl": | |
210 | smtp = SMTP_SSL(smtp_host, smtp_port, timeout=timeout) | |
211 | else: | |
212 | smtp = SMTP(smtp_host, smtp_port, timeout=timeout) | |
65a1b892 | 213 | smtp.set_debuglevel(show_transcript) |
2d9506eb | 214 | if smtp_secure == "tls": |
215 | smtp.starttls() | |
216 | if username or password: | |
217 | smtp.login(username, password) | |
218 | smtp.sendmail(msg["From"], msg["To"].split(","), msg.as_string()) | |
219 | ||
220 | except SMTPException as err: | |
221 | # Output just the error (skip the Python stack trace) for SMTP errors | |
222 | print("Error sending: %r" % err) | |
223 | exit(1) | |
224 | ||
225 | except SocketError as err: | |
226 | print("Error connecting to %s:%d: %r" % (smtp_host, smtp_port, err)) | |
227 | exit(1) | |
228 | ||
229 | finally: | |
230 | if smtp is not None: | |
231 | smtp.quit() | |
232 | EOF | |
b50e701c | 233 | } |