envelope 2.0.2__tar.gz → 2.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {envelope-2.0.2 → envelope-2.0.3}/PKG-INFO +131 -117
- {envelope-2.0.2 → envelope-2.0.3}/README.md +122 -114
- {envelope-2.0.2 → envelope-2.0.3}/envelope/__main__.py +1 -1
- {envelope-2.0.2 → envelope-2.0.3}/envelope/address.py +8 -2
- {envelope-2.0.2 → envelope-2.0.3}/envelope/envelope.py +40 -14
- {envelope-2.0.2 → envelope-2.0.3}/envelope/smtp_handler.py +4 -3
- {envelope-2.0.2 → envelope-2.0.3}/envelope/utils.py +2 -1
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/PKG-INFO +131 -117
- {envelope-2.0.2 → envelope-2.0.3}/setup.py +2 -2
- {envelope-2.0.2 → envelope-2.0.3}/LICENSE.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope/__init__.py +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope/attachment.py +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope/constants.py +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope/message.py +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope/parser.py +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/SOURCES.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/dependency_links.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/entry_points.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/requires.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/envelope.egg-info/top_level.txt +0 -0
- {envelope-2.0.2 → envelope-2.0.3}/setup.cfg +0 -0
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envelope
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.3
|
|
4
4
|
Summary: Insert a message and attachments and send e-mail / sign / encrypt contents by a single line.
|
|
5
5
|
Home-page: https://github.com/CZ-NIC/envelope
|
|
6
6
|
Author: Edvard Rejthar
|
|
7
7
|
Author-email: edvard.rejthar@nic.cz
|
|
8
8
|
License: GNU GPLv3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Provides-Extra: smime
|
|
13
12
|
License-File: LICENSE.txt
|
|
13
|
+
Requires-Dist: jsonpickle
|
|
14
|
+
Requires-Dist: python-magic
|
|
15
|
+
Requires-Dist: python-gnupg>=0.5
|
|
16
|
+
Requires-Dist: py3-validate-email; python_version >= "3.8"
|
|
17
|
+
Requires-Dist: py3-validate-email<1.0.6; python_version < "3.8"
|
|
18
|
+
Provides-Extra: smime
|
|
19
|
+
Requires-Dist: M2Crypto; extra == "smime"
|
|
14
20
|
|
|
15
21
|
# Envelope
|
|
16
22
|
|
|
17
23
|
[](https://github.com/CZ-NIC/envelope/actions) [](https://pepy.tech/project/envelope)
|
|
18
24
|
|
|
19
|
-
Quick layer over [python-gnupg](https://bitbucket.org/vinay.sajip/python-gnupg/src), [M2Crypto](https://m2crypto.readthedocs.io/), [smtplib](https://docs.python.org/3/library/smtplib.html), [magic](https://pypi.org/project/python-magic/) and [email](https://docs.python.org/3/library/email.html?highlight=email#module-email) handling packages. Their common use cases merged into a single function. Want to sign a text and tired of forgetting how to do it right? You do not need to know everything about GPG or S/MIME, you do not have to bother with importing keys. Do not hassle with reconnecting to an SMTP server. Do not study various headers meanings to let your users unsubscribe via a URL.
|
|
20
|
-
You insert a message, attachments and inline images and receive signed and/or encrypted output to the file or to your recipients' e-mail.
|
|
21
|
-
Just single line of code. With the great help of the examples below.
|
|
25
|
+
Quick layer over [python-gnupg](https://bitbucket.org/vinay.sajip/python-gnupg/src), [M2Crypto](https://m2crypto.readthedocs.io/), [smtplib](https://docs.python.org/3/library/smtplib.html), [magic](https://pypi.org/project/python-magic/) and [email](https://docs.python.org/3/library/email.html?highlight=email#module-email) handling packages. Their common use cases merged into a single function. Want to sign a text and tired of forgetting how to do it right? You do not need to know everything about GPG or S/MIME, you do not have to bother with importing keys. Do not hassle with reconnecting to an SMTP server. Do not study various headers meanings to let your users unsubscribe via a URL.
|
|
26
|
+
You insert a message, attachments and inline images and receive signed and/or encrypted output to the file or to your recipients' e-mail.
|
|
27
|
+
Just single line of code. With the great help of the examples below.
|
|
22
28
|
|
|
23
29
|
```python3
|
|
24
30
|
Envelope("my message")
|
|
@@ -32,10 +38,10 @@ Envelope("my message")
|
|
|
32
38
|
|
|
33
39
|
```python3
|
|
34
40
|
# Inline image
|
|
35
|
-
Envelope("My inline image: <img src='cid:image.jpg' />")
|
|
41
|
+
Envelope("My inline image: <img src='cid:image.jpg' />")
|
|
36
42
|
.attach(path="image.jpg", inline=True)
|
|
37
43
|
|
|
38
|
-
# Load a message and read its attachments
|
|
44
|
+
# Load a message and read its attachments
|
|
39
45
|
Envelope.load(path="message.eml").attachments()
|
|
40
46
|
# in bash: envelope --load message.eml --attachments
|
|
41
47
|
```
|
|
@@ -81,7 +87,7 @@ Envelope.load(path="message.eml").attachments()
|
|
|
81
87
|
|
|
82
88
|
# Installation
|
|
83
89
|
* Install with a single command from [PyPi](https://pypi.org/project/envelope/)
|
|
84
|
-
```bash
|
|
90
|
+
```bash
|
|
85
91
|
pip3 install envelope
|
|
86
92
|
```
|
|
87
93
|
|
|
@@ -91,10 +97,10 @@ Envelope.load(path="message.eml").attachments()
|
|
|
91
97
|
```
|
|
92
98
|
* Or just download the project and launch `python3 -m envelope`
|
|
93
99
|
* If planning to sign/encrypt with GPG, assure you have it on the system with `sudo apt install gpg` and possibly see [Configure your GPG](#configure-your-gpg) tutorial.
|
|
94
|
-
* If planning to use S/MIME, you should ensure some prerequisites: `sudo apt install swig && pip3 install M2Crypto`
|
|
100
|
+
* If planning to use S/MIME, you should ensure some prerequisites: `sudo apt install swig build-essential python3-dev libssl-dev && pip3 install M2Crypto`
|
|
95
101
|
* If planning to send e-mails, prepare SMTP credentials or visit [Configure your SMTP](#configure-your-smtp) tutorial.
|
|
96
102
|
* If your e-mails are to be received outside your local domain, visit [DMARC](#dmarc) section.
|
|
97
|
-
* Package [python-magic](https://pypi.org/project/python-magic/) is used as a dependency. Due to a [well-known](https://github.com/ahupp/python-magic/blob/master/COMPAT.md) name clash with the [file-magic](https://pypi.org/project/file-magic/) package, in case you need to use the latter, don't worry to run `pip uninstall python-magic && pip install file-magic` after installing envelope which is fully compatible with both projects.
|
|
103
|
+
* Package [python-magic](https://pypi.org/project/python-magic/) is used as a dependency. Due to a [well-known](https://github.com/ahupp/python-magic/blob/master/COMPAT.md) name clash with the [file-magic](https://pypi.org/project/file-magic/) package, in case you need to use the latter, don't worry to run `pip uninstall python-magic && pip install file-magic` after installing envelope which is fully compatible with both projects.
|
|
98
104
|
|
|
99
105
|
## Bash completion
|
|
100
106
|
1. Run: `apt install bash-completion jq`
|
|
@@ -105,7 +111,7 @@ Envelope.load(path="message.eml").attachments()
|
|
|
105
111
|
As an example, let's produce in three equal ways an `output_file` with the GPG-encrypted "Hello world" content.
|
|
106
112
|
## CLI
|
|
107
113
|
Launch as a CLI application in terminal, see `envelope --help`
|
|
108
|
-
|
|
114
|
+
|
|
109
115
|
```bash
|
|
110
116
|
envelope --message "Hello world" \
|
|
111
117
|
--output "/tmp/output_file" \
|
|
@@ -141,7 +147,7 @@ Envelope(message="Hello world",
|
|
|
141
147
|
Both `envelope --help` for CLI arguments help and `pydoc3 envelope` to see module arguments help should contain same information as here.
|
|
142
148
|
|
|
143
149
|
## Command list
|
|
144
|
-
All parameters are optional.
|
|
150
|
+
All parameters are optional.
|
|
145
151
|
|
|
146
152
|
* **--param** is used in CLI
|
|
147
153
|
* **.param(value)** denotes a positional argument
|
|
@@ -149,7 +155,14 @@ All parameters are optional.
|
|
|
149
155
|
* **Envelope(param=)** is a one-liner argument
|
|
150
156
|
|
|
151
157
|
#### Any attainable contents
|
|
152
|
-
Whenever any attainable contents is mentioned, we mean plain **text**, **bytes** or **stream** (ex: from `open()`). In *module interface*, you may use a **`Path`** object to the file. In *CLI interface*, additional flags are provided instead.
|
|
158
|
+
Whenever any attainable contents is mentioned, we mean plain **text**, **bytes** or **stream** (ex: from `open()`). In *module interface*, you may use a **`Path`** object to the file. In *CLI interface*, additional flags are provided instead.
|
|
159
|
+
|
|
160
|
+
If the object is not accesible, it will immediately raise `FileNotFoundError`.
|
|
161
|
+
```python3
|
|
162
|
+
Envelope().attach(path="file.jpg")
|
|
163
|
+
# Could not fetch file .../file.jpg
|
|
164
|
+
# FileNotFoundError: [Errno 2] No such file or directory: 'file.jpg'
|
|
165
|
+
```
|
|
153
166
|
|
|
154
167
|
### Input / Output
|
|
155
168
|
* **message**: Message / body text.
|
|
@@ -163,35 +176,35 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
163
176
|
* `alternative`: "auto", "html", "plain" You may specify e-mail text alternative. Some e-mail readers prefer to display plain text version over HTML. By default, we try to determine content type automatically (see *mime*).
|
|
164
177
|
```python3
|
|
165
178
|
print(Envelope().message("He<b>llo</b>").message("Hello", alternative="plain"))
|
|
166
|
-
|
|
179
|
+
|
|
167
180
|
# (output shortened)
|
|
168
181
|
# Content-Type: multipart/alternative;
|
|
169
182
|
# boundary="===============0590677381100492396=="
|
|
170
|
-
#
|
|
183
|
+
#
|
|
171
184
|
# --===============0590677381100492396==
|
|
172
185
|
# Content-Type: text/plain; charset="utf-8"
|
|
173
186
|
# Hello
|
|
174
|
-
#
|
|
187
|
+
#
|
|
175
188
|
# --===============0590677381100492396==
|
|
176
189
|
# Content-Type: text/html; charset="utf-8"
|
|
177
190
|
# He<b>llo</b>
|
|
178
191
|
```
|
|
179
|
-
* *boundary*: When specifying alternative, you may set e-mail boundary if you do not wish a random one to be created.
|
|
192
|
+
* *boundary*: When specifying alternative, you may set e-mail boundary if you do not wish a random one to be created.
|
|
180
193
|
* **.body(path=None)**: Alias of `.message` (without `alternative` and `boundary` parameter)
|
|
181
194
|
* **.text(path=None)**: Alias of `.message` (without `alternative` and `boundary` parameter)
|
|
182
195
|
* **Envelope(message=)**: [Any attainable contents](#any-attainable-contents)
|
|
183
|
-
|
|
196
|
+
|
|
184
197
|
Equivalents for setting a string (in *Python* and in *Bash*).
|
|
185
198
|
```python3
|
|
186
199
|
Envelope(message="hello") == Envelope().message("hello")
|
|
187
200
|
```
|
|
188
201
|
```bash
|
|
189
202
|
envelope --message "hello"
|
|
190
|
-
```
|
|
203
|
+
```
|
|
191
204
|
Equivalents for setting contents of a file (in *Python* and in *Bash*).
|
|
192
205
|
```python3
|
|
193
206
|
from pathlib import Path
|
|
194
|
-
Envelope(message=Path("file.txt")) == Envelope(message=open("file.txt")) == Envelope.message(path="file.txt")
|
|
207
|
+
Envelope(message=Path("file.txt")) == Envelope(message=open("file.txt")) == Envelope.message(path="file.txt")
|
|
195
208
|
```
|
|
196
209
|
```bash
|
|
197
210
|
envelope --input file.txt
|
|
@@ -205,11 +218,11 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
205
218
|
repr(e)
|
|
206
219
|
# WARNING: Cannot decode the message correctly, plain alternative bytes are not in Unicode.
|
|
207
220
|
# Envelope(message="b'\x80'")
|
|
208
|
-
|
|
221
|
+
|
|
209
222
|
# When trying to output a mal-encoded message, we end up with a ValueError exception.
|
|
210
223
|
e.message()
|
|
211
224
|
# ValueError: Cannot decode the message correctly, it is not in Unicode. b'\x80'
|
|
212
|
-
|
|
225
|
+
|
|
213
226
|
# Setting up an encoding (even ex-post) solves the issue.
|
|
214
227
|
e.header("Content-Type", "text/plain;charset=cp1250")
|
|
215
228
|
e.message() # '€'
|
|
@@ -218,7 +231,7 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
218
231
|
* **--output**
|
|
219
232
|
* **.output(output_file)**
|
|
220
233
|
* **Envelope(output=)**
|
|
221
|
-
|
|
234
|
+
|
|
222
235
|
### Recipients
|
|
223
236
|
* **from**: E-mail – needed to choose our key if encrypting.
|
|
224
237
|
* **--from** E-mail. Empty to read value.
|
|
@@ -227,12 +240,12 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
227
240
|
* **Envelope(from_=)**: Sender e-mail or False to explicitly omit. When encrypting without sender, we do not use their key so that we will not be able to decipher again.
|
|
228
241
|
```python3
|
|
229
242
|
# These statements are identical.
|
|
230
|
-
Envelope(from_="identity@example.com")
|
|
243
|
+
Envelope(from_="identity@example.com")
|
|
231
244
|
Envelope().from_("identity@example.com")
|
|
232
|
-
|
|
245
|
+
|
|
233
246
|
# This statement produces both From header and Sender header.
|
|
234
247
|
Envelope(from_="identity@example.com", headers=[("Sender", "identity2@example.com")])
|
|
235
|
-
|
|
248
|
+
|
|
236
249
|
# reading an Address object
|
|
237
250
|
a = Envelope(from_="identity@example.com").from_()
|
|
238
251
|
a == "identity@example.com", a.host == "example.com"
|
|
@@ -240,18 +253,18 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
240
253
|
* **to**: E-mail or more in an iterable. When encrypting, we use keys of these identities. Multiple addresses may be given in a string, delimited by a comma (or semicolon). (The same is valid for `to`, `cc`, `bcc` and `reply-to`.)
|
|
241
254
|
* **--to**: One or more e-mail addresses. Empty to read.
|
|
242
255
|
```bash
|
|
243
|
-
$ envelope --to first@example.com second@example.com --message "hello"
|
|
256
|
+
$ envelope --to first@example.com second@example.com --message "hello"
|
|
244
257
|
$ envelope --to
|
|
245
258
|
first@example.com
|
|
246
259
|
second@example.com
|
|
247
|
-
```
|
|
248
|
-
* **.to(email_or_more)**: If None, current list of [Addresses](#address) returned. If False or "", current list is cleared.
|
|
260
|
+
```
|
|
261
|
+
* **.to(email_or_more)**: If None, current list of [Addresses](#address) returned. If False or "", current list is cleared.
|
|
249
262
|
```python3
|
|
250
263
|
Envelope()
|
|
251
264
|
.to("person1@example.com")
|
|
252
265
|
.to("person1@example.com, John <person2@example.com>")
|
|
253
266
|
.to(["person3@example.com"])
|
|
254
|
-
.to() # ["person1@example.com", "John <person2@example.com>", "person3@example.com"]
|
|
267
|
+
.to() # ["person1@example.com", "John <person2@example.com>", "person3@example.com"]
|
|
255
268
|
```
|
|
256
269
|
* **Envelope(to=)**: E-mail or more in an iterable.
|
|
257
270
|
* **cc**: E-mail or more in an iterable. Multiple addresses may be given in a string, delimited by a comma (or semicolon). (The same is valid for `to`, `cc`, `bcc` and `reply-to`.)
|
|
@@ -262,7 +275,7 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
262
275
|
.cc("person1@example.com")
|
|
263
276
|
.cc("person1@example.com, John <person2@example.com>")
|
|
264
277
|
.cc(["person3@example.com"])
|
|
265
|
-
.cc() # ["person1@example.com", "John <person2@example.com>", "person3@example.com"]
|
|
278
|
+
.cc() # ["person1@example.com", "John <person2@example.com>", "person3@example.com"]
|
|
266
279
|
```
|
|
267
280
|
* **Envelope(cc=)**
|
|
268
281
|
* **bcc**: E-mail or more in an iterable. Multiple addresses may be given in a string, delimited by a comma (or semicolon). (The same is valid for `to`, `cc`, `bcc` and `reply-to`.) The header is not sent.
|
|
@@ -277,7 +290,7 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
277
290
|
* **--from-addr**: E-mail address or empty to read value.
|
|
278
291
|
* **.from_addr(email)**: E-mail or False. If None, current `SMTP envelope MAIL FROM` returned as an [Address](#address) object (even an empty one).
|
|
279
292
|
* **.Envelope(from_addr=)**
|
|
280
|
-
|
|
293
|
+
|
|
281
294
|
### Sending
|
|
282
295
|
* **send**: Send the message to the recipients by e-mail. True (blank in *CLI*) to send now or False to print out debug information.
|
|
283
296
|
* **--send**
|
|
@@ -285,12 +298,12 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
285
298
|
* *send*: True to send now. False (or 0/false/no in *CLI*) to print debug information.
|
|
286
299
|
* Returns the object back which converted to bool returns True if the message has been sent successfully.
|
|
287
300
|
* **Envelope(send=)**
|
|
288
|
-
|
|
301
|
+
|
|
289
302
|
```bash
|
|
290
303
|
$ envelope --to "user@example.org" --message "Hello world" --send 0
|
|
291
304
|
****************************************************************************************************
|
|
292
305
|
Have not been sent from - to user@example.org
|
|
293
|
-
|
|
306
|
+
|
|
294
307
|
Content-Type: text/html; charset="utf-8"
|
|
295
308
|
Content-Transfer-Encoding: 7bit
|
|
296
309
|
MIME-Version: 1.0
|
|
@@ -299,14 +312,14 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
299
312
|
To: user@example.org
|
|
300
313
|
Date: Mon, 07 Oct 2019 16:13:37 +0200
|
|
301
314
|
Message-ID: <157045761791.29779.5279828659897745855@...>
|
|
302
|
-
|
|
315
|
+
|
|
303
316
|
Hello world
|
|
304
317
|
```
|
|
305
318
|
* **subject**: Mail subject. Gets encrypted with GPG, stays visible with S/MIME.
|
|
306
319
|
* **--subject**
|
|
307
320
|
* **.subject(text=None, encrypt=None)**:
|
|
308
321
|
* `text` Subject text.
|
|
309
|
-
* `encrypt` Text used instead of the real protected subject while PGP encrypting. False to not encrypt.
|
|
322
|
+
* `encrypt` Text used instead of the real protected subject while PGP encrypting. False to not encrypt.
|
|
310
323
|
* If neither parameter specified, current subject returned.
|
|
311
324
|
* **Envelope(subject=)**
|
|
312
325
|
* **Envelope(subject_encrypted=)**
|
|
@@ -314,7 +327,7 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
314
327
|
* **.date(date)** `str|False` Specify Date header (otherwise Date is added automatically). If False, the Date header will not be added automatically.
|
|
315
328
|
* **smtp**: SMTP server
|
|
316
329
|
* **--smtp**
|
|
317
|
-
* **.smtp(host="localhost", port=25, user=, password=, security=, timeout=3, attempts=3, delay=3)**
|
|
330
|
+
* **.smtp(host="localhost", port=25, user=, password=, security=, timeout=3, attempts=3, delay=3, local_hostname=None)**
|
|
318
331
|
* **Envelope(smtp=)**
|
|
319
332
|
* Parameters:
|
|
320
333
|
* `host` May include hostname or any of the following input formats (ex: path to an INI file or a `dict`)
|
|
@@ -322,10 +335,11 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
322
335
|
* `timeout` How many seconds should SMTP wait before timing out.
|
|
323
336
|
* `attempts` How many times we try to send the message to an SMTP server.
|
|
324
337
|
* `delay` How many seconds to sleep before re-trying a timed out connection.
|
|
338
|
+
* `local_hostname` FQDN of the local host in the HELO/EHLO command.
|
|
325
339
|
* Input format may be in the following form:
|
|
326
340
|
* `None` default localhost server used
|
|
327
|
-
* `smtplib.SMTP` object
|
|
328
|
-
* `list` or `tuple` having `host, [port, [username, password, [security, [timeout, [attempts, [delay]]]]]]` parameters
|
|
341
|
+
* standard [`smtplib.SMTP`](https://docs.python.org/3/library/smtplib.html) object
|
|
342
|
+
* `list` or `tuple` having `host, [port, [username, password, [security, [timeout, [attempts, [delay, [local_hostname]]]]]]]` parameters
|
|
329
343
|
* ex: `envelope --smtp localhost 125 me@example.com` will set up host, port and username parameters
|
|
330
344
|
* `dict` specifying {"host": ..., "port": ...}
|
|
331
345
|
* ex: `envelope --smtp '{"host": "localhost"}'` will set up host parameter
|
|
@@ -333,11 +347,11 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
333
347
|
```ini
|
|
334
348
|
[SMTP]
|
|
335
349
|
host = example.com
|
|
336
|
-
port = 587
|
|
350
|
+
port = 587
|
|
337
351
|
```
|
|
338
352
|
* Do not fear to pass the `smtp` in a loop, we make just a single connection to the server. If timed out, we attempt to reconnect once.
|
|
339
353
|
```python3
|
|
340
|
-
smtp = localhost, 25
|
|
354
|
+
smtp = "localhost", 25
|
|
341
355
|
for mail in mails:
|
|
342
356
|
Envelope(...).smtp(smtp).send()
|
|
343
357
|
```
|
|
@@ -347,31 +361,31 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
347
361
|
envelope --attachment "/tmp/file.txt" "displayed-name.txt" "text/plain" --attachment "/tmp/another-file.txt"
|
|
348
362
|
```
|
|
349
363
|
* **.attach(attachment=, mimetype=, name=, path=, inline=)**:
|
|
364
|
+
```python3
|
|
365
|
+
Envelope().attach(path="/tmp/file.txt").attach(path="/tmp/another-file.txt")
|
|
366
|
+
```
|
|
350
367
|
* Three different usages when specifying contents:
|
|
351
368
|
* **.attach(attachment=, mimetype=, name=)**: You can put [any attainable contents](#any-attainable-contents) of a single attachment into *attachment* and optionally add mime type or displayed file name.
|
|
352
369
|
* **.attach(mimetype=, name=, path=)**: You can specify path and optionally mime type or displayed file name.
|
|
353
370
|
* **.attach(attachment=)**: You can put a list of attachments. The list may contain tuples: `contents [,mime type] [,file name] [, True for inline]`.
|
|
354
|
-
```python3
|
|
355
|
-
Envelope().attach(path="/tmp/file.txt").attach(path="/tmp/another-file.txt")
|
|
356
|
-
```
|
|
357
371
|
* **.attach(inline=True|str)**: Specify content-id (CID) to reference the image from within HTML message body.
|
|
358
372
|
* True: Filename or attachment or path file name is set as CID.
|
|
359
373
|
* str: The attachment will get this CID.
|
|
360
|
-
```python3
|
|
361
|
-
|
|
374
|
+
```python3
|
|
375
|
+
from pathlib import Path
|
|
376
|
+
Envelope().attach(Path("file.jpg"), inline=True) # <img src='cid:file.jpg' />
|
|
362
377
|
Envelope().attach(b"GIF89a\x03\x00\x03...", name="file.gif", inline=True) # <img src='cid:file.gif' />
|
|
363
|
-
Envelope().attach("file.jpg", inline="foo") # <img src='cid:foo' />
|
|
364
|
-
|
|
378
|
+
Envelope().attach(Path("file.jpg"), inline="foo") # <img src='cid:foo' />
|
|
379
|
+
|
|
365
380
|
# Reference it like: .message("Hey, this is an inline image: <img src='cid:foo' />")
|
|
366
381
|
```
|
|
367
|
-
|
|
368
382
|
* **Envelope(attachments=)**: Attachment or their list. Attachment is defined by [any attainable contents](#any-attainable-contents), optionally in tuple with the file name to be used in the e-mail and/or mime type and/or True for being inline: `contents [,mime type] [,file name] [, True for inline]`
|
|
369
383
|
```python3
|
|
370
384
|
Envelope(attachments=[(Path("/tmp/file.txt"), "displayed-name.txt", "text/plain"), Path("/tmp/another-file.txt")])
|
|
371
|
-
```
|
|
372
|
-
* **mime**: Sets contents mime subtype: "**auto**" (default), "**html**" or "**plain**" for plain text.
|
|
373
|
-
Maintype is always set to "text".
|
|
374
|
-
Set maintype to "text". If a line is longer than 1000 characters, makes the message be transferred safely by bytes (otherwise these non-standard long lines might cause a transferring SMTP server to include line breaks and redundant spaces that might break up ex: DKIM signature).
|
|
385
|
+
```
|
|
386
|
+
* **mime**: Sets contents mime subtype: "**auto**" (default), "**html**" or "**plain**" for plain text.
|
|
387
|
+
Maintype is always set to "text".
|
|
388
|
+
Set maintype to "text". If a line is longer than 1000 characters, makes the message be transferred safely by bytes (otherwise these non-standard long lines might cause a transferring SMTP server to include line breaks and redundant spaces that might break up ex: DKIM signature).
|
|
375
389
|
In case of `Content-Type` header put to the message, **mime** section functionality **is skipped**.
|
|
376
390
|
* **--mime SUBTYPE**
|
|
377
391
|
* **.mime(subtype="auto", nl2br="auto")**
|
|
@@ -389,19 +403,19 @@ Whenever any attainable contents is mentioned, we mean plain **text**, **bytes**
|
|
|
389
403
|
.header("Generic-Header") # ["1", "2"]
|
|
390
404
|
```
|
|
391
405
|
* **Envelope(headers=[(name, value)])**
|
|
392
|
-
|
|
393
|
-
Equivalent headers:
|
|
406
|
+
|
|
407
|
+
Equivalent headers:
|
|
394
408
|
```bash
|
|
395
409
|
envelope --header X-Mailer my-app
|
|
396
410
|
```
|
|
397
|
-
|
|
411
|
+
|
|
398
412
|
```python3
|
|
399
413
|
Envelope(headers=[("X-Mailer", "my-app")])
|
|
400
414
|
Envelope().header("X-Mailer", "my-app")
|
|
401
|
-
```
|
|
415
|
+
```
|
|
402
416
|
#### Specific headers
|
|
403
417
|
These helpers are available via fluent interface.
|
|
404
|
-
|
|
418
|
+
|
|
405
419
|
* **.list_unsubscribe(uri=None, one_click=False, web=None, email=None)**: You can specify either url, email or both.
|
|
406
420
|
* **.list_unsubscribe(uri)**: We try to determine whether this is e-mail and prepend brackets and 'https:'/'mailto:' if needed. Ex: `me@example.com?subject=unsubscribe`, `example.com/unsubscribe`, `<https://example.com/unsubscribe>`
|
|
407
421
|
* **.list_unsubscribe(email=)**: E-mail address. Ex: `me@example.com`, `mailto:me@example.com`
|
|
@@ -413,21 +427,21 @@ These helpers are available via fluent interface.
|
|
|
413
427
|
Envelope().list_unsubscribe("example.com/unsubscribe")
|
|
414
428
|
Envelope().list_unsubscribe(web="example.com/unsubscribe")
|
|
415
429
|
Envelope().list_unsubscribe("<https://example.com/unsubscribe>")
|
|
416
|
-
|
|
430
|
+
|
|
417
431
|
# This will produce:
|
|
418
432
|
# List-Unsubscribe: <https://example.com/unsubscribe>, <mailto:me@example.com?subject=unsubscribe>
|
|
419
433
|
Envelope().list_unsubscribe("example.com/unsubscribe", mail="me@example.com?subject=unsubscribe")
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
* **.auto_submitted**:
|
|
423
|
-
* **.auto_submitted(val="auto-replied")**: Direct response to another message by an automatic process.
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
* **.auto_submitted**:
|
|
437
|
+
* **.auto_submitted(val="auto-replied")**: Direct response to another message by an automatic process.
|
|
424
438
|
* **.auto_submitted.auto_generated()**: automatic (often periodic) processes (such as UNIX "cron jobs") which are not direct responses to other messages
|
|
425
439
|
* **.auto_submitted.no()**: message was originated by a human
|
|
426
440
|
|
|
427
441
|
```python3
|
|
428
|
-
Envelope().auto_submitted() # mark message as automatic
|
|
442
|
+
Envelope().auto_submitted() # mark message as automatic
|
|
429
443
|
Envelope().auto_submitted.no() # mark message as human produced
|
|
430
|
-
```
|
|
444
|
+
```
|
|
431
445
|
|
|
432
446
|
### Cipher standard method
|
|
433
447
|
Note that if neither *gpg* nor *smime* is specified, we try to determine the method automatically.
|
|
@@ -442,13 +456,13 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
442
456
|
### Signing
|
|
443
457
|
* **sign**: Sign the message.
|
|
444
458
|
* **`key`** parameter
|
|
445
|
-
* GPG:
|
|
459
|
+
* GPG:
|
|
446
460
|
* Blank (*CLI*) or True (*module*) for user default key
|
|
447
461
|
* "auto" for turning on signing if there is a key matching to the "from" header
|
|
448
462
|
* key ID/fingerprint
|
|
449
463
|
* e-mail address of the identity whose key is to be signed with
|
|
450
464
|
* [Any attainable contents](#any-attainable-contents) with the key to be signed with (will be imported into keyring)
|
|
451
|
-
* S/MIME: [Any attainable contents](#any-attainable-contents) with key to be signed with. May contain signing certificate as well.
|
|
465
|
+
* S/MIME: [Any attainable contents](#any-attainable-contents) with key to be signed with. May contain signing certificate as well.
|
|
452
466
|
* **--sign key**: (for `key` see above)
|
|
453
467
|
* **--sign-path**: Filename with the From\'s private key. (Alternative to the `sign` parameter.)
|
|
454
468
|
* **--passphrase**: Passphrase to the key if needed.
|
|
@@ -462,10 +476,10 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
462
476
|
* **Envelope(attach_key=)**: If true, append GPG public key as an attachment when sending.
|
|
463
477
|
* **Envelope(cert=)**: S/MIME: [Any attainable contents](#any-attainable-contents)
|
|
464
478
|
### Encrypting
|
|
465
|
-
* **encrypt**: Recipient GPG public key or S/MIME certificate to be encrypted with.
|
|
479
|
+
* **encrypt**: Recipient GPG public key or S/MIME certificate to be encrypted with.
|
|
466
480
|
* **`key`** parameter
|
|
467
481
|
* GPG:
|
|
468
|
-
* Blank (*CLI*) or True (*module*) to force encrypt with the user default keys (identities in the "from", "to", "cc" and "bcc" headers)
|
|
482
|
+
* Blank (*CLI*) or True (*module*) to force encrypt with the user default keys (identities in the "from", "to", "cc" and "bcc" headers)
|
|
469
483
|
* "auto" for turning on encrypting if there is a matching key for every recipient
|
|
470
484
|
* key ID/fingerprint
|
|
471
485
|
* e-mail address of the identity whose key is to be encrypted with
|
|
@@ -477,19 +491,19 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
477
491
|
* **.encrypt(key=True, sign=, key_path=)**:
|
|
478
492
|
* **`sign`** See signing, ex: you may specify boolean or default signing key ID/fingerprint or "auto" for GPG or [any attainable contents](#any-attainable-contents) with an S/MIME key + signing certificate.
|
|
479
493
|
* **`key_path`**: Key/certificate contents (alternative to the `key` parameter)
|
|
480
|
-
* **.encryption(key=True, key_path=)**: Encrypt later (when launched with *.sign()*, *.encrypt()* or *.send()* functions. If needed, in the parameters specify [any attainable contents](#any-attainable-contents) with GPG encryption key or S/MIME encryption certificate.
|
|
494
|
+
* **.encryption(key=True, key_path=)**: Encrypt later (when launched with *.sign()*, *.encrypt()* or *.send()* functions. If needed, in the parameters specify [any attainable contents](#any-attainable-contents) with GPG encryption key or S/MIME encryption certificate.
|
|
481
495
|
* **Envelope(encrypt=key)**: (for `key` see above)
|
|
482
496
|
```bash
|
|
483
497
|
# message gets encrypted for multiple S/MIME certificates
|
|
484
498
|
envelope --smime --encrypt-path recipient1.pem recipient2.pem --message "Hello"
|
|
485
|
-
|
|
499
|
+
|
|
486
500
|
# message gets encrypted with the default GPG key
|
|
487
501
|
envelope --message "Encrypted GPG message!" --subject "Secret subject will not be shown" --encrypt --from person@example.com --to person@example.com
|
|
488
|
-
|
|
502
|
+
|
|
489
503
|
# message not encrypted for the sender (from Bash)
|
|
490
504
|
envelope --message "Encrypted GPG message!" --subject "Secret subject will not be shown" --encrypt receiver@example.com receiver2@example.com --from person@example.com --to receiver@example.com receiver2@example.com
|
|
491
505
|
```
|
|
492
|
-
|
|
506
|
+
|
|
493
507
|
```python3
|
|
494
508
|
# message not encrypted for the sender (from Python)
|
|
495
509
|
Envelope()
|
|
@@ -497,11 +511,11 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
497
511
|
.subject("Secret subject will not be shown")
|
|
498
512
|
.from_("person@example.com")
|
|
499
513
|
.to(("receiver@example.com", "receiver2@example.com"))
|
|
500
|
-
.encrypt(("receiver@example.com", "receiver2@example.com"))
|
|
514
|
+
.encrypt(("receiver@example.com", "receiver2@example.com"))
|
|
501
515
|
```
|
|
502
516
|
|
|
503
517
|
#### GPG notes
|
|
504
|
-
* If the GPG encryption fails, it tries to determine which recipient misses the key.
|
|
518
|
+
* If the GPG encryption fails, it tries to determine which recipient misses the key.
|
|
505
519
|
* By default, GPG encrypts with the key of the **from** header recipient too.
|
|
506
520
|
* Key ID/fingerprint is internally ignored right now, GPG decides itself which key is to be used.
|
|
507
521
|
|
|
@@ -512,19 +526,19 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
512
526
|
* **--attachments [NAME]** Get the list of attachments or a contents of the one specified by `NAME`
|
|
513
527
|
* **.attachments(name=None, inline=None)**
|
|
514
528
|
* **name** (str): The name of the only desired attachment to be returned.
|
|
515
|
-
* **inline** (bool): Filter inline/enclosed attachments only.
|
|
529
|
+
* **inline** (bool): Filter inline/enclosed attachments only.
|
|
516
530
|
* *Attachment* object has the attributes *.name* file name, *.mimetype*, *.data* raw data
|
|
517
531
|
* if casted to *str*/*bytes*, its raw *.data* are returned
|
|
518
|
-
* **.copy()**: Return deep copy of the instance to be used independently.
|
|
519
|
-
```python3
|
|
532
|
+
* **.copy()**: Return deep copy of the instance to be used independently.
|
|
533
|
+
```python3
|
|
520
534
|
factory = Envelope().cc("original@example.com").copy
|
|
521
535
|
e1 = factory().to("to-1@example.com")
|
|
522
|
-
e2 = factory().to("to-2@example.com").cc("additional@example.com") #
|
|
536
|
+
e2 = factory().to("to-2@example.com").cc("additional@example.com") #
|
|
523
537
|
|
|
524
538
|
print(e1.recipients()) # {'to-1@example.com', 'original@example.com'}
|
|
525
539
|
print(e2.recipients()) # {'to-2@example.com', 'original@example.com', 'additional@example.com'}
|
|
526
540
|
```
|
|
527
|
-
* Read message and subject by **.message()** and **.subject()**
|
|
541
|
+
* Read message and subject by **.message()** and **.subject()**
|
|
528
542
|
* **preview**: Returns the string of the message or data as a human-readable text.
|
|
529
543
|
Ex: whilst we have to use quoted-printable (as seen in __str__), here the output will be plain text.
|
|
530
544
|
* **--preview**
|
|
@@ -532,11 +546,11 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
532
546
|
* **check**: Check all e-mail addresses and SMTP connection and return True/False if succeeded. Tries to find SPF, DKIM and DMARC DNS records depending on the From's domain and print them out.
|
|
533
547
|
* **--check**
|
|
534
548
|
* **.check(check_mx=True, check_smtp=True)**
|
|
535
|
-
* `check_mx` E-mail addresses can be checked for MX record, not only for their format.
|
|
549
|
+
* `check_mx` E-mail addresses can be checked for MX record, not only for their format.
|
|
536
550
|
* `check_smtp` We try to connect to the SMTP host.
|
|
537
|
-
|
|
551
|
+
|
|
538
552
|
```bash
|
|
539
|
-
$ envelope --smtp localhost 25 --from me@example.com --check
|
|
553
|
+
$ envelope --smtp localhost 25 --from me@example.com --check
|
|
540
554
|
SPF found on the domain example.com: v=spf1 -all
|
|
541
555
|
See: dig -t SPF example.com && dig -t TXT example.com
|
|
542
556
|
DKIM found: ['v=DKIM1; g=*; k=rsa; p=...']
|
|
@@ -547,12 +561,12 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
547
561
|
* **.as_message()**: Generates an email.message.Message object.
|
|
548
562
|
```python3
|
|
549
563
|
e = Envelope("hello").as_message()
|
|
550
|
-
print(type(e), e.get_payload()) # <class 'email.message.EmailMessage'> hello\n
|
|
564
|
+
print(type(e), e.get_payload()) # <class 'email.message.EmailMessage'> hello\n
|
|
551
565
|
```
|
|
552
566
|
Note: due to a bug in a standard Python library https://github.com/python/cpython/issues/99533 and #19 you void GPG when you access the message this way wihle signing an attachment with a name longer than 34 chars.
|
|
553
567
|
* **load**: Parse [any attainable contents](#any-attainable-contents) (including email.message.Message) like an EML file to build an Envelope object.
|
|
554
568
|
* It can decrypt the message and parse its (inline or enclosed) attachments.
|
|
555
|
-
* Note that if you will send this reconstructed message, you might not probably receive it due to the Message-ID duplication. Delete at least Message-ID header prior to re-sending.
|
|
569
|
+
* Note that if you will send this reconstructed message, you might not probably receive it due to the Message-ID duplication. Delete at least Message-ID header prior to re-sending.
|
|
556
570
|
* (*static*) **.load(message, \*, path=None, key=None, cert=None, gnupg_home=None)**
|
|
557
571
|
* **message**: [Any attainable contents](#any-attainable-contents)
|
|
558
572
|
* **path**: Path to the file, alternative to the `message`
|
|
@@ -562,7 +576,7 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
562
576
|
Envelope.load("Subject: testing message").subject() # "testing message"
|
|
563
577
|
```
|
|
564
578
|
* bash
|
|
565
|
-
* allows use blank `--subject` or `--message` flags to display the
|
|
579
|
+
* allows use blank `--subject` or `--message` flags to display the
|
|
566
580
|
* **--load FILE**
|
|
567
581
|
```bash
|
|
568
582
|
$ envelope --load email.eml
|
|
@@ -570,22 +584,22 @@ Note that if neither *gpg* nor *smime* is specified, we try to determine the met
|
|
|
570
584
|
Content-Transfer-Encoding: 7bit
|
|
571
585
|
MIME-Version: 1.0
|
|
572
586
|
Subject: testing message
|
|
573
|
-
|
|
587
|
+
|
|
574
588
|
Message body
|
|
575
|
-
|
|
589
|
+
|
|
576
590
|
$ envelope --load email.eml --subject
|
|
577
|
-
testing message
|
|
591
|
+
testing message
|
|
578
592
|
```
|
|
579
|
-
* (*bash*) piped in content, envelope executable used with no argument
|
|
593
|
+
* (*bash*) piped in content, envelope executable used with no argument
|
|
580
594
|
```bash
|
|
581
595
|
$ echo "Subject: testing message" | envelope
|
|
582
596
|
Content-Type: text/plain; charset="utf-8"
|
|
583
597
|
Content-Transfer-Encoding: 7bit
|
|
584
598
|
MIME-Version: 1.0
|
|
585
599
|
Subject: testing message
|
|
586
|
-
|
|
600
|
+
|
|
587
601
|
$ cat email.eml | envelope
|
|
588
|
-
|
|
602
|
+
|
|
589
603
|
$ envelope < email.eml
|
|
590
604
|
```
|
|
591
605
|
* **smtp_quit()**: As Envelope tends to re-use all the SMTP instances, you may want to quit them explicitly. Either call this method to the Envelope class to close all the cached connections or to an Envelope object to close only the connection it currently uses.
|
|
@@ -613,7 +627,7 @@ Empty object works too. For example, if the `From` header is not set, we get an
|
|
|
613
627
|
a = Envelope.load("Empty message").from_()
|
|
614
628
|
bool(a) is False, a.host == ""
|
|
615
629
|
Address() == Address("") == "", Address().address == ""
|
|
616
|
-
```
|
|
630
|
+
```
|
|
617
631
|
|
|
618
632
|
Method `.casefold()` returns casefolded `Address` object which is useful for comparing with strings whereas comparing with other `Address` object casefolds automatically
|
|
619
633
|
```python3
|
|
@@ -626,7 +640,7 @@ Method `.is_valid(check_mx=False)` returns boolean if the format is valid. When
|
|
|
626
640
|
|
|
627
641
|
Since the `Address` is a subclass of `str`, you can safely join such objects.
|
|
628
642
|
|
|
629
|
-
```python3
|
|
643
|
+
```python3
|
|
630
644
|
", ".join([a, a]) # "John <person@example.com>, "John <person@example.com>"
|
|
631
645
|
a + " hello" # "John <person@example.com> hello"
|
|
632
646
|
```
|
|
@@ -638,7 +652,7 @@ Address object is equal to a string if the string contains its e-mail address or
|
|
|
638
652
|
"person@example.com" == Address("John <person@example.com>") == "John <person@example.com>" # True
|
|
639
653
|
```
|
|
640
654
|
|
|
641
|
-
Concerning `to`, `cc`, `bcc` and `reply-to`, multiple addresses may always be given in a string, delimited by comma (or semicolon). The `.get(address:bool, name:bool)` method may be called on an `Address` object to filter the desired information.
|
|
655
|
+
Concerning `to`, `cc`, `bcc` and `reply-to`, multiple addresses may always be given in a string, delimited by comma (or semicolon). The `.get(address:bool, name:bool)` method may be called on an `Address` object to filter the desired information.
|
|
642
656
|
```python3
|
|
643
657
|
e = (Envelope()
|
|
644
658
|
.to("person1@example.com")
|
|
@@ -725,7 +739,7 @@ with open("/tmp/message.txt") as f:
|
|
|
725
739
|
```
|
|
726
740
|
|
|
727
741
|
Sign and encrypt the message so that's decryptable by keys for me@example.com and remote_person@example.com (that should already be loaded in the keyring).
|
|
728
|
-
```python3
|
|
742
|
+
```python3
|
|
729
743
|
Envelope(message="Hello world", sign=True,
|
|
730
744
|
encrypt=True,
|
|
731
745
|
from_="me@example.com",
|
|
@@ -733,7 +747,7 @@ Envelope(message="Hello world", sign=True,
|
|
|
733
747
|
```
|
|
734
748
|
|
|
735
749
|
Sign and encrypt the message so that's decryptable by keys for me@example.com and remote_person@example.com (that get's imported to the keyring from the file).
|
|
736
|
-
```python3
|
|
750
|
+
```python3
|
|
737
751
|
Envelope(message="Hello world", sign=True,
|
|
738
752
|
encrypt=Path("/tmp/remote_key.asc"),
|
|
739
753
|
from_="me@example.com",
|
|
@@ -746,12 +760,12 @@ Envelope(message="Hello world", sign=True, gnupg="/tmp/my-keyring/")
|
|
|
746
760
|
```
|
|
747
761
|
|
|
748
762
|
Sign the message with a key that needs passphrase.
|
|
749
|
-
```python3
|
|
763
|
+
```python3
|
|
750
764
|
Envelope(message="Hello world", sign=True, passphrase="my-password")
|
|
751
765
|
```
|
|
752
766
|
|
|
753
|
-
Sign a message with signing by default turned previously on and having a default keyring path. Every `factory` call will honour these defaults.
|
|
754
|
-
```python3
|
|
767
|
+
Sign a message with signing by default turned previously on and having a default keyring path. Every `factory` call will honour these defaults.
|
|
768
|
+
```python3
|
|
755
769
|
factory = Envelope().signature(True).gpg("/tmp/my-keyring").copy
|
|
756
770
|
factory().(message="Hello world")
|
|
757
771
|
```
|
|
@@ -770,17 +784,17 @@ envelope --to "user@example.org" --message "Hello world" --send
|
|
|
770
784
|
Send while having specified the SMTP server host, port, username, password.
|
|
771
785
|
|
|
772
786
|
```bash
|
|
773
|
-
envelope --to "user@example.org" message "Hello world" --send --smtp localhost 123 username password
|
|
787
|
+
envelope --to "user@example.org" message "Hello world" --send --smtp localhost 123 username password
|
|
774
788
|
```
|
|
775
789
|
|
|
776
790
|
Send while having specified the SMTP server through a dictionary.
|
|
777
791
|
```bash
|
|
778
|
-
envelope --to "user@example.org" --message "Hello world" --send --smtp '{"host": "localhost", "port": "123"}'
|
|
792
|
+
envelope --to "user@example.org" --message "Hello world" --send --smtp '{"host": "localhost", "port": "123"}'
|
|
779
793
|
```
|
|
780
794
|
|
|
781
795
|
Send while having specified the SMTP server via module call.
|
|
782
796
|
```python3
|
|
783
|
-
Envelope(message="Hello world", to="user@example.org", send=True, smtp={"host":"localhost"})
|
|
797
|
+
Envelope(message="Hello world", to="user@example.org", send=True, smtp={"host":"localhost"})
|
|
784
798
|
```
|
|
785
799
|
|
|
786
800
|
## Attachment
|
|
@@ -790,15 +804,15 @@ Envelope(attachment=Path("/tmp/file.txt")) # file name will be 'file.txt'
|
|
|
790
804
|
|
|
791
805
|
with open("/tmp/file.txt") as f:
|
|
792
806
|
Envelope(attachment=f) # file name will be 'file.txt'
|
|
793
|
-
|
|
807
|
+
|
|
794
808
|
with open("/tmp/file.txt") as f:
|
|
795
809
|
Envelope(attachment=(f, "filename.txt"))
|
|
796
|
-
|
|
810
|
+
|
|
797
811
|
Envelope().attach(path="/tmp/file.txt", name="filename.txt")
|
|
798
812
|
```
|
|
799
813
|
|
|
800
814
|
## Inline images
|
|
801
|
-
The only thing you have to do is to set the `inline=True` parameter of the attachment. Then, you can reference the image from within your message, with the help of `cid` keyword. For more details, see *attachments* in the [Sending](#sending) section.
|
|
815
|
+
The only thing you have to do is to set the `inline=True` parameter of the attachment. Then, you can reference the image from within your message, with the help of `cid` keyword. For more details, see *attachments* in the [Sending](#sending) section.
|
|
802
816
|
```python3
|
|
803
817
|
(Envelope()
|
|
804
818
|
.attach(path="/tmp/file.jpg", inline=True)
|
|
@@ -859,7 +873,7 @@ RQ8QtLLEza+rs+1lgcPgdBZEHFpYpgDb0AUvYg9d
|
|
|
859
873
|
```
|
|
860
874
|
|
|
861
875
|
# Related affairs
|
|
862
|
-
Sending an e-mail does not mean it will be received. Sending it successfully through your local domain does not mean a public mailbox will accept it as well. If you are not trustworthy enough, your e-mail may not even appear at the recipient's spam bin, it can just be discarded without notice.
|
|
876
|
+
Sending an e-mail does not mean it will be received. Sending it successfully through your local domain does not mean a public mailbox will accept it as well. If you are not trustworthy enough, your e-mail may not even appear at the recipient's spam bin, it can just be discarded without notice.
|
|
863
877
|
|
|
864
878
|
## Configure your SMTP
|
|
865
879
|
It is always easier if you have an account on an SMTP server the application is able to send e-mails with. If it is not the case, various SMTP server exist but as a quick and non-secure solution, I've tested [bytemark/smtp](https://hub.docker.com/r/bytemark/smtp/) docker image that allows you to start up a SMTP server by a single line.
|
|
@@ -884,18 +898,18 @@ GNUPGHOME=/var/www/.gnupg sudo -H -u www-data gpg --export [APPLICATION_EMAIL] |
|
|
|
884
898
|
GNUPGHOME=/var/www/.gnupg sudo -H -u www-data envelope --message "Hello world" --subject "GPG signing test" --sign [key ID] --from [application e-mail] --to [your e-mail] --send # you now receive e-mail and may import the key and set the trust to the key
|
|
885
899
|
```
|
|
886
900
|
|
|
887
|
-
It takes few hours to a key to propagate. If the key cannot be imported in your e-mail client because not found on the servers, try in the morning again or check the online search form at http://hkps.pool.sks-keyservers.net.
|
|
901
|
+
It takes few hours to a key to propagate. If the key cannot be imported in your e-mail client because not found on the servers, try in the morning again or check the online search form at http://hkps.pool.sks-keyservers.net.
|
|
888
902
|
Put your fingerprint on the web or on the business card then so that everybody can check your signature is valid.
|
|
889
903
|
|
|
890
904
|
### Configure your S/MIME
|
|
891
905
|
If you are supposed to use S/MIME, you would probably be told where to take your key and certificate from. If planning to try it all by yourself, generate your `certificate.pem`.
|
|
892
|
-
|
|
906
|
+
|
|
893
907
|
* Either: Do you have private key?
|
|
894
908
|
```bash
|
|
895
909
|
openssl req -key YOUR-KEY.pem -nodes -x509 -days 365 -out certificate.pem # will generate privkey.pem alongside
|
|
896
910
|
```
|
|
897
|
-
|
|
898
|
-
* Or: Do not you have private key?
|
|
911
|
+
|
|
912
|
+
* Or: Do not you have private key?
|
|
899
913
|
```bash
|
|
900
914
|
openssl req -newkey rsa:1024 -nodes -x509 -days 365 -out certificate.pem # will generate privkey.pem alongside
|
|
901
915
|
```
|
|
@@ -908,17 +922,17 @@ envelope --message "Hello world" --subject "S/MIME signing test" --sign-path [ke
|
|
|
908
922
|
## DNS validation tools
|
|
909
923
|
This is just a short explanation on these anti-spam mechanisms so that you can take basic notion what is going on.
|
|
910
924
|
|
|
911
|
-
Every time, the receiver should ask the From's domain these questions over DNS.
|
|
925
|
+
Every time, the receiver should ask the From's domain these questions over DNS.
|
|
912
926
|
|
|
913
927
|
### SPF
|
|
914
|
-
The receiver asks the sender's domain: Do you allow the senders IP/domain to send the e-mail on your behalf? Is the IP/domain the mail originates from enlisted as valid in the DNS of the SMTP envelope MAIL FROM address domain?
|
|
928
|
+
The receiver asks the sender's domain: Do you allow the senders IP/domain to send the e-mail on your behalf? Is the IP/domain the mail originates from enlisted as valid in the DNS of the SMTP envelope MAIL FROM address domain?
|
|
915
929
|
|
|
916
930
|
Check your domain on SPF:
|
|
917
931
|
```bash
|
|
918
932
|
dig -t TXT example.com
|
|
919
933
|
```
|
|
920
934
|
|
|
921
|
-
SPF technology is tied to the SMTP envelope MAIL FROM address which is specified with the `.from_addr` method and then stored into the Return-Path header by the receiving server, and it has nothing in common with the headers like From `.from_`, Reply-To `.reply_to`, or Sender `.header("Sender")`.
|
|
935
|
+
SPF technology is tied to the SMTP envelope MAIL FROM address which is specified with the `.from_addr` method and then stored into the Return-Path header by the receiving server, and it has nothing in common with the headers like From `.from_`, Reply-To `.reply_to`, or Sender `.header("Sender")`.
|
|
922
936
|
|
|
923
937
|
### DKIM
|
|
924
938
|
The receiver asks the sender's domain: Give me the public key so that I may check the hash in the e-mail header that assert the message was composed by your private key. So that the e-mail comes trustworthy from you and nobody modified it on the way.
|
|
@@ -926,7 +940,7 @@ The receiver asks the sender's domain: Give me the public key so that I may chec
|
|
|
926
940
|
Check your domain on DKIM:
|
|
927
941
|
```bash
|
|
928
942
|
dig -t TXT [selector]._domainkey.example.com
|
|
929
|
-
```
|
|
943
|
+
```
|
|
930
944
|
You can obtain the `selector` from an e-mail message you received. Check the line `DKIM-Signature` and the value of the param `s`.
|
|
931
945
|
```
|
|
932
946
|
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com; s=default;
|
|
@@ -938,4 +952,4 @@ What is your policy concerning SPF and DKIM? What abuse address do you have?
|
|
|
938
952
|
Check your domain on DMARC:
|
|
939
953
|
```bash
|
|
940
954
|
dig -t TXT _dmarc.example.com
|
|
941
|
-
```
|
|
955
|
+
```
|