mailout

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2016 License: Apache-2.0 Imports: 24 Imported by: 0

README

mailout - CaddyServer SMTP Client

Post form data from a website to this route and receive the data as nicely formatted email.

Read more: https://cyrillschumacher.com/projects/2016-02-26-mailout-caddyserver-email-smtp/

Mailout config options in the Caddyfile:

mailout [endpoint] {
	maillog         [path/to/logdir]
	errorlog        [path/to/logdir]

	to              email@address1.tld       
	cc              ["email@address2.tld, email@addressN.tld"]        
	bcc             ["email@addressN.tld, email@addressN.tld"]
    subject         "Email from {{.firstname}} {{.lastname}}"
	body            path/to/tpl.[txt|html]

	[email@address1.tld]       [path/to/pgp1.pub|ENV:MY_PGP_KEY_PATH_1|https://keybase.io/cyrill1/key.asc]
	[email@address2.tld]       [path/to/pgp2.pub|ENV:MY_PGP_KEY_PATH_2|https://keybase.io/cyrill2/key.asc]
	[email@addressN.tld]       [path/to/pgpN.pub|ENV:MY_PGP_KEY_PATH_N|https://keybase.io/cyrillN/key.asc]

	username        "ENV:MY_SMTP_USERNAME|gopher"
	password        "ENV:MY_SMTP_PASSWORD|g0ph3r"
	host            "ENV:MY_SMTP_HOST|smtp.gmail.com"
	port             ENV:MY_SMTP_PORT|25|465|587
	
	ratelimit_interval 24h
	ratelimit_capacity 1000
}
  • endpoint: Can be any path but your POST request must match it. Default path: /mailout
  • [email-address]: if provided mails get encrypted. Set a path to a file, an environment variable or an URL to a key on a HTTPS site. Key = email address; value = PGP Key
  • maillog: Specify a directory, which gets created recursively, and emails will be written in there, as a backup. Leaving the maillog setting empty does not log anything. Every sent email is saved into its own file. Strict file permissions apply.
  • errorlog: Specify a directory, which gets created recursively, and errors gets logged in there. Leaving the errorlog setting empty does not log anything. Strict file permissions apply.
  • to, cc, bcc: Multiple email addresses must be separated by a colon and within double quotes.
  • subject: Has the same functionality as the body template, but text only.
  • body: Text or HTML template stored on the hard disk of your server. More details below.
  • username, password, host: Self explanatory, access credentials to the SMTP server.
  • port: Plain text on port 25, SSL uses port 465, for TLS use port 587. Internally for TLS the host name gets verified with the certificate of the SMTP server.
  • ratelimit_interval: the duration in which the capacity can be consumed. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Default: 24h
  • ratelimit_capacity: the overall capacity within the interval. Default: 1000

The default filename for an encrypted message attached to an email is: encrypted.gpg.

The extension .gpg has been chosen to allow easy handling with https://www.gnupg.org/

If you don't like this file name you can overwrite it with the key publickeyAttachmentFileName.

To implement a fully working This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) PGP attachment, I need some help. It's possible that the gomail package needs to be refactored.

Note on sensitive information leakage when using PGP with multiple email message receivers: For each email address in the to, cc and bcc field you must add a public PGP key, if not, emails to recipients without a public key won't be encrypted. For all email addresses with a PGP key, the mailout middleware will send a separated email encrypted with the key of the receiver.

Rate limit: Does not require external storage since it uses an algorithm called Token Bucket (Go library: juju/ratelimit).

JSON API

Server response on success (Status 200 OK):

{"code":200}

Server response on error (Status 422 Unprocessable Entity):

{"code":422,"error":"Invalid email address: \"doe.john40nonexistantServer.email\""}

Server response on non-POST requests (Status 405 Method Not Allowed):

{"code":405,"error":"Method Not Allowed"}

Server response on form parse error (Status 400 Bad Request):

{"code":400,"error":"Bad request"}

Server response on reaching the rate limit (Status 429 Too Many Requests):

{"code":429,"error":"Too Many Requests"}

Server response on internal errors:

500 Internal Server Error
Email template

The rendering engine for the email templates depends on the suffix of the template file name.

HTML form

Create a simple HTML form with some JavaScript and AJAX functions.

Mandatory input field is email. Optional recommended field: name. Those two fields will be later joined to create the From: header of an email.

The following snipped has been extracted from a Hugo template.

  <div id="contactThankYou" style="display:hidden;">Thank you for contacting us!</div>
  <form action="#" id="myContactForm" method="POST">
    <div class="row uniform 50%">
      <div class="6u 12u$(xsmall)">
        <input type="text" name="name" id="name"
               placeholder="{{ .Site.Params.contact.form.name }}" required/>
      </div>
      <div class="6u$ 12u$(xsmall)">
        <input type="email" name="email" id="email"
               placeholder="{{ .Site.Params.contact.form.email }}" required/>
      </div>
      <div class="12u$">
        <textarea name="message" id="message" placeholder="{{ .Site.Params.contact.form.message }}"
                  rows="4" required></textarea>
      </div>
      <input type="hidden" name="user_agent" value="Will be filled out via JavaScript"/>
      <ul class="actions">
        <li><input type="submit" value="{{ .Site.Params.contact.form.submit }}"/></li>
      </ul>
    </div>
  </form>

A jQuery AJAX handler might look like (untested):

$(document).ready(function() {

    $('#myContactForm').submit(function(event) {

        $.ajax({
            type        : 'POST', 
            url         : 'https://myCaddyServer.com/mailout', 
            data        : $('#myContactForm').serialize(),
            dataType    : 'json',
            encode      : true
        })
        .done(function(data) {
            console.log(data); 
            $('#contactThankYou').show();
            $('#myContactForm').hide();

        })
         .fail(function() {
            alert( "error" );
         });

        event.preventDefault();
    });

});    

An email template for an outgoing mail may look like in plain text:

Hello,

please find below a new contact:

Name            {{.Form.Get "name"}}
Email           {{.Form.Get "email"}}

Message:
{{.Form.Get "message"}}

User Agent: {{.Form.Get "user_agent"}}
HTML Form Fields

A must-have form field is the email address: <input type="text" name="email" value=""/> or for HTML5 <input type="email" name="email" value=""/>

Optional field should be name: <input type="text" name="name" value=""/>

Both fields will be merged to the From Email address: "name" <email@address>.

If you do not provide the name field, only the email address will be used.

GMail

If you use Gmail as outgoing server these pages can help:

I need some help to pimp the authentication feature of gomail to avoid switching on the less secure "feature".

Todo

  • file uploads
  • CORS
  • For each receiver email address its own PGP key.
  • implement ideas and improvements from open issues

Contribute

Send me a pull request or open an issue if you encounter a bug or something can be improved!

Multi-time pull request senders gets collaborator access.

License

Cyrill Schumacher - My pgp public key

Copyright 2016 Cyrill Schumacher All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Documentation

Index

Constants

View Source
const StatusEmpty = 0

StatusEmpty returned by mailout middleware because the proper status gets written previously

View Source
const StatusUnprocessableEntity = 422

StatusUnprocessableEntity gets returned whenever parsing of the form fails.

Variables

This section is empty.

Functions

func Setup

func Setup(c *setup.Controller) (mw middleware.Middleware, err error)

Setup used internally by Caddy to set up this middleware

Types

type JSONError

type JSONError struct {
	// Code represents the HTTP Status Code, a work around.
	Code int `json:"code,omitempty"`
	// Error the underlying error, if there is one.
	Error string `json:"error,omitempty"`
}

JSONError defines how an REST JSON looks like. Code 200 and empty Error specifies a successful request Any other Code value s an error.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL