Insert a message and attachments and send e-mail / sign / encrypt contents by a single line.
Project description
Envelope
Quick layer over python-gnupg, smime, smtplib and email handling packages. Their common usecases 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 SMTP server. Do not study various headers meanings to let your users unsubscribe via a URL. You insert a message and attachments and receive signed and/or encrypted output to the file or to your recipients' e-mail. Just single line of code. With the great help of the examples below.
envelope("my message").subject("hello world").to("example@example.com").sign().send()
Installation
- Install with a single command from PyPi
pip3 install envelope
- Or install current GitHub master
pip3 install git+https://github.com/CZ-NIC/envelope.git
- Or just download the project and launch
./envelope.py
- If planning to sign/encrypt with GPG, install the corresponding package
sudo apt install gpg
Usage
As an example, let's produce in three equal ways an output_file
with the GPG-encrypted "Hello world" content.
CLI
Launch as a CLI application in terminal, see envelope --help
envelope --message "Hello world" \
--output "/tmp/output_file" \
--sender "me@example.com" \
--to "remote_person@example.com" \
--encrypt-path "/tmp/remote_key.asc"
Module: one-liner function
You can easily write a one-liner function that encrypts your code or sends an e-mail from within your application when imported as a module. See pydoc3 envelope
or documentation below.
import envelope
envelope(message="Hello world",
output="/tmp/output_file",
sender="me@example.com",
to="remote_person@example.com",
encrypt="/tmp/remote_key.asc")
Module: fluent interface
Comfortable way to create the structure if your IDE supports autocompletion.
import envelope
envelope().message("Hello world")\
.output("/tmp/output_file")\
.sender("me@example.com")\
.to("remote_person@example.com")\
.encrypt(key_path="/tmp/remote_key.asc")
Note: if autocompletion does not work, use from envelope import envelope
instead of import envelope
.
(For example, Jupyter can autocomplete with import envelope
but PyCharm cannot because it does not serves itself with a running kernel.)
Documentation
Both envelope --help
for CLI arguments help and pydoc3 envelope
to see module arguments help should contain same information as here.
Command list
All parameters are optional.
- --param is used in CLI
- envelope(param=) is a one-liner argument
- .param(value) denotes a positional argument
- .param(value=) denotes a keyword argument
Any fetchable content means plain text, bytes or stream (ex: from open()). In module interface, you may use Path object to the file. In CLI interface, additional flags are provided.
Input / Output
-
message: Message / body text.
- --message: String
- --input: (CLI only) Path to the message file. (Alternative to
--message
parameter.) - envelope(message=): Any fetchable content
- .message(text): String or stream.
- .message(path=None): Path to the file.
Equivalents for setting a string.
envelope(message="hello") == envelope().message("hello")
envelope --message "hello"
Equivalents for setting contents of a file.
from pathlib import Path envelope(message=Path("file.txt")) == envelope(message=open("file.txt")) == envelope.message(path="file.txt")
envelope --input file.txt
-
output: Path to file to be written to (else the contents is returned).
- --output
- envelope(output=)
- .output(output_file)
Cipher standard method
Note that if neither gpg nor smime is specified, we try to determine the method automatically.
- gpg: True to prefer GPG over S/MIME or home path to GNUPG rings (otherwise default ~/.gnupg is used)
- --gpg [path]
- envelope(gpg=True)
- .gpg(path=True)
- .smime: Prefer S/MIME over GPG
- --smime
- envelope(smime=True)
- .smime()
Signing
- sign: Sign the message.
- --sign: Blank for user default key or key-id.
- --passphrase: Passphrase to the key if needed.
- envelope(sign=): True for user default key or key-id.
- envelope(passphrase=): Passphrase to the key if needed.
- .sign(key_id=, passphrase=): Sign now (and you may specify the parameters)
- .signature(key_id=, passphrase=): Sign later (when launched with .sign(), .encrypt() or .send() functions
Encrypting
If the GPG encryption fails, it tries to determine which recipient misses the key.
- encrypt: Recipient GPG public key or S/MIME certificate to be encrypted with.
- --encrypt: String for key-id or blank or 1/true/yes if the key should be in the ring from before. Put 0/false/no to disable
encrypt-file
. - --encrypt-file (CLI only): Recipient public key stored in a file path. (Alternative to
--encrypt
.) - envelope(encrypt=): Any fetchable content
- .encrypt(sign=, key_id=, key_path=, key=): With sign, you may specify boolean or default signing key-id. Put your key-id to key-id, path to the key file in key_path or key contents to key.
- .encryption(key_id=, key_path=, key=): Encrypt later (when launched with .sign(), .encrypt() or .send() functions.
- --encrypt: String for key-id or blank or 1/true/yes if the key should be in the ring from before. Put 0/false/no to disable
- to: E-mail or list. When encrypting, we use keys of these identities.
- --to: One or more e-mail addresses.
- envelope(to=): E-mail or their list.
- .to(email_or_list):
envelope --to first@example.com second@example.com --message "hello"
- sender: E-mail – needed to choose our key if encrypting.
- --sender E-mail
- --no-sender Declare we want to encrypt and never decrypt back.
- envelope(sender=): 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.
- .sender(email): E-mail or False.
- .from_(email): an alias for .sender
Sending
- send: Send the message to the recipients by e-mail. True (blank in CLI) to send now or False to print out debug information.
- --send
- envelope(send=)
- .send(now=True)
- subject: Mail subject. Gets encrypted with GPG, stays visible with S/MIME.
- --subject
- envelope(subject=)
- .subject(text)
- cc: E-mail or their list
- --cc
- envelope(cc=)
- .cc(email_or_list)
- bcc: E-mail or their list
- --bcc
- envelope(bcc=)
- .bcc(email_or_list)
- reply-to: E-mail to be replied to. The field is not encrypted.
- --reply-to
- envelope(reply_to=)
- .reply_to(email)
- smtp: SMTP server
- --smtp
- envelope(smtp=)
- .smtp(host="localhost", port=25, user=, password=, security=)
- Input format may be in the following form:
None
default localhost server usedsmtplib.SMTP
objectlist
ortuple
havinghost, [port, [username, password, [security]]]
parameters- ex:
envelope --smtp localhost 125 me@example.com
will set up host, port and username parameters
- ex:
dict
specifying {"host": ..., "port": ...}- ex:
envelope --smtp '{"host": "localhost"}'
will set up host parameter
- ex:
- Parameters:
security
parameter may have "starttls" value for callingsmtp.starttls()
connection security - 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.
smtp = localhost, 25 for mail in mails: envelope(...).smtp(smtp).send()
- attachments
- --attachment: Path to the attachment, followed by optional file name to be used and/or mime type. This parameter may be used multiple times.
envelope --attachment "/tmp/file.txt" "displayed-name.txt" "text/plain" --attachment "/tmp/another-file.txt"
- gpggp(attachments=): Attachment or their list. Attachment is defined by any fetchable content, optionally in tuple with the file name to be used in the e-mail and/or mime type:
content [,name] [,mimetype]
envelope(attachments=[(Path("/tmp/file.txt"), "displayed-name.txt", "text/plain"), Path("/tmp/another-file.txt"])
- .attach(attachment_or_list=, path=, mimetype=, filename=): Three different usages.
- .attach(attachment_or_list=, mimetype=, filename=): You can put any fetchable content in attachment_or_list and optionally mimetype or displayed filename.
- .attach(path=, mimetype=, filename=): You can specify path and optionally mimetype or displayed filename.
- .attach(attachment_or_list=): You can put a list of attachments.
envelope().attach(path="/tmp/file.txt").attach(path="/tmp/another-file.txt")
-
headers: Any custom headers (these will not be encrypted with GPG nor S/MIME)
- --header name value (may be used multiple times)
- envelope(headers=[(name, value)])
- .header(name, value)
Equivalent headers:
envelope --header X-Mailer my-app
envelope(headers=[("X-Mailer", "my-app")]) envelope().header("X-Mailer", "my-app")
Specific headers
These helpers are available via fluent interface.
-
.list_unsubscribe(uri=None, one_click=False, web=None, email=None): You can specify either url, email or both.
- .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>
- .list_unsubscribe(email=): E-mail address. Ex:
me@example.com
,mailto:me@example.com
- .list_unsubscribe(web=, one_click=False): Specify URL. Ex:
example.com/unsubscribe
,http://example.com/unsubscribe
. Ifone_click=True
, rfc8058 List-Unsubscribe-Post header is added. This says user can unsubscribe with a single click that is realized by a POST request in order to prevent e-mail scanner to access the unsubscribe page by mistake. A 'https' url must be present.
# These will produce: # List-Unsubscribe: <https://example.com/unsubscribe> envelope().list_unsubscribe("example.com/unsubscribe") envelope().list_unsubscribe(web="example.com/unsubscribe") envelope().list_unsubscribe("<https://example.com/unsubscribe>") # This will produce: # List-Unsubscribe: <https://example.com/unsubscribe>, <mailto:me@example.com?subject=unsubscribe> envelope().list_unsubscribe("example.com/unsubscribe", mail="me@example.com?subject=unsubscribe")
- .list_unsubscribe(uri): We try to determine whether this is e-mail and prepend brackets and 'https:'/'mailto:' if needed. Ex:
-
.auto_submitted:
- .auto_submitted(val="auto-replied"): Direct response to another message by an automatic process.
- .auto_submitted.auto_generated(): automatic (often periodic) processes (such as UNIX "cron jobs") which are not direct responses to other messages
- .auto_submitted.no(): message was originated by a human
envelope().auto_submitted() # mark message as automatic
envelope().auto_submitted.no() # mark message as human produced
Supportive
- check: Check SMTP connection and returns True/False
- --check
- .check()
Default values
In module interface, you may set the defaults when accessing envelope.default
instance.
envelope.default.subject("Test subject").signature()
envelope("Hello") # this message has a default subject and is signed by default when sent
Converting object to str or bool
When successfully signing, encrypting or sending, object is resolvable to True and signed text / produced e-mail could be obtained via str().
o = envelope("message", sign=True)
str(o) # signed text
bool(o) # True
Examples
Signing and encrypting
Sign the message.
envelope(message="Hello world", sign=True)
Sign the message loaded from a file by standard pathlib library
from pathlib import Path
envelope(message=Path("/tmp/message.txt"), sign=True)
Sign the message got from a file-stream
with open("/tmp/message.txt") as f:
envelope(message=f, sign=True)
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).
envelope(message="Hello world", sign=True
encrypt=True,
sender="me@example.com",
to="remote_person@example.com")
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).
envelope(message="Hello world", sign=True
encrypt=Path("/tmp/remote_key.asc"),
sender="me@example.com",
to="remote_person@example.com")
Sign the message via different keyring.
envelope(message="Hello world", sign=True, gnupg="/tmp/my-keyring/")
Sign the message with a key that needs passphrase.
envelope(message="Hello world", sign=True, passphrase="my-password")
Sign a message without signing by default turned previously on and having a default keyring path. Every envelope
call will honour these defaults.
envelope.default.signature(True).gnupghome("/tmp/my-keyring")
envelope(message="Hello world")
Sending
Send an e-mail via module call.
envelope(message="Hello world", send=True)
Send an e-mail via CLI and default SMTP server localhost on port 25.
envelope --to "user@example.org" --message "Hello world" --send
Send while having specified the SMTP server host, port, username, password.
envelope --to "user@example.org" message "Hello world" --send --smtp localhost 123 username password
Send while having specified the SMTP server through a dictionary.
envelope --to "user@example.org" --message "Hello world" --send --smtp '{"host": "localhost", "port": "123"}'
Send while having specified the SMTP server via module call.
envelope(message="Hello world", to="user@example.org", send=True, smtp={"host":"localhost"})
Attachment
You can attach a file in many different ways. Pick the one that suits you the best.
envelope(attachment=Path("/tmp/file.txt")) # filename will be 'file.txt'
with open("/tmp/file.txt") as f:
envelope(attachment=f) # filename will be 'file.txt'
with open("/tmp/file.txt") as f:
envelope(attachment=(f, "filename.txt"))
envelope().attach(path="/tmp/file.txt",filename="filename.txt")
Complex example
Send an encrypted and signed message via the default SMTP server.
envelope --message "Hello world" --to "user@example.org" --sender "me@example.org" --subject "Test" --send --sign --encrypt --attachment /tmp/file.txt --attach /tmp/file2 application/gzip zipped-file.zip
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.