Skip to main content

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.
  • 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 used
      • smtplib.SMTP object
      • list or tuple having host, [port, [username, password, [security]]] parameters
        • ex: envelope --smtp localhost 125 me@example.com will set up host, port and username parameters
      • dict specifying {"host": ..., "port": ...}
        • ex: envelope --smtp '{"host": "localhost"}' will set up host parameter
    • Parameters: security parameter may have "starttls" value for calling smtp.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. If one_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")
    
  • .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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

envelope-0.9.1.tar.gz (21.7 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page