blackmail

package module
v0.0.0-...-f1b7a42 Latest Latest
Warning

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

Go to latest
Published: Dec 10, 2025 License: MIT Imports: 23 Imported by: 9

README

Blackmail is a Go package to send emails.

Import the library as zgo.at/blackmail; API docs: https://godocs.io/zgo.at/blackmail

There is also a smtp client library at zgo.at/blackmail/smtp which can be used without the main blackmail client if you want. It's a modified version of net/smtp (via go-smtp, with some changes).

There is a small commandline utility at cmd/blackmail.

Example

// Send a new message using blackmail.DefaultMailer
err := blackmail.Send("Send me bitcoins or I will leak your browsing history!",
    blackmail.From("", "blackmail@example.com"),
    blackmail.To("Name", "victim@example.com"),
    blackmail.Bodyf("I can haz ur bitcoinz?"))

// A more complex message with a text and HTML part and inline image.
err = blackmail.Send("I saw what you did last night 😏",
    blackmail.From("😏", "blackmail@example.com"),
    append(blackmail.To("Name", "victim@example.com"), blackmail.Cc("Other", "other@example.com")...),
    blackmail.Text("Text part")
    blackmail.HTML("HTML part: <img src="cid:blackmail:1">",
        blackmail.InlineImage("image/png", "logo.png", imgbytes)))

// You can create your own (re-usable) mailer.
mailer := blackmail.NewMailer("smtp://user:pass@localhost:25")
err = mailer.Send([..])

// Add some options to your mailer.
mailer = blackmail.NewMailer("smtp://user:pass@localhost:25
    blackmail.MailerAuth(..),
    blackmail.MailerTLS(&tls.Config{}),
    blackmail.RequireSTARTLS(true))

// Get RF5322 message with a list of recipients to send it to (To + Cc + Bcc).
msg, to := blackmail.Message([.. same arguments as Send() ..])

See the test cases in blackmail_test.go for various other examples.

Questions you may have

I get the error "tls: first record does not look like a TLS handshake"

You are attempting to establish a TLS connection to a server which doesn't support TLS or only supports it via the STARTTLS command.

I get the error "x509: certificate signed by unknown authority"

The certificate chain used for the TLS connection is not signed by a known authority. It's a self-signed certificate or you don't have the root certificates installed.

How can I use a @ in my username?

Encode as %40:

smtp://martin%40arp242.net:PASS@smtp.example.com:587'

Documentation

Overview

Package blackmail sends emails.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Attachment

func Attachment(contentType, filename string, body []byte) bodyPart

Attachment returns a new attachment part with the given Content-Type.

It will try to guess the Content-Type if empty.

func Bcc

func Bcc(addr ...string) []recipient

func BccAddress

func BccAddress(addr ...mail.Address) []recipient

func BccNames

func BccNames(nameAddr ...string) []recipient

func Body

func Body(contentType string, body []byte) bodyPart

Body returns a new part with the given Content-Type.

func BodyHTML

func BodyHTML(body []byte, images ...bodyPart) bodyPart

BodyHTML returns a new text/html part.

func BodyMust

func BodyMust(contentType string, fn func() ([]byte, error)) bodyPart

BodyMust sets the body using a callback, propagating any errors back up.

This is useful when using Go templates for the mail body;

buf := new(bytes.Buffer)
err := tpl.ExecuteTemplate(buf, "email", struct{
    Name string
}{"Martin"})
if err != nil {
    log.Fatal(err)
}

err := Send("Basic test", From("", "me@example.com"),
    To("to@to.to"),
    Body("text/plain", buf.Bytes()))

With BodyMust(), it's simpler; you just need to define a little helper re-usable helper function and call that:

func template(tplname string, args any) func() ([]byte, error) {
    return func() ([]byte, error) {
        buf := new(bytes.Buffer)
        err := tpl.ExecuteTemplate(buf, tplname, args)
        return buf.Bytes(), err
    }
}

err := Send("Basic test", From("", "me@example.com"),
    To("to@to.to"),
    BodyMust("text/html", template("email", struct {
        Name string
    }{"Martin"})))

Other use cases include things like loading data from a file, reading from a stream, etc.

func BodyMustHTML

func BodyMustHTML(fn func() ([]byte, error)) bodyPart

BodyMustHTML is like BodyMust() with contentType text/html.

func BodyMustText

func BodyMustText(fn func() ([]byte, error)) bodyPart

BodyMustText is like BodyMust() with contentType text/plain.

func BodyText

func BodyText(body []byte) bodyPart

BodyText returns a new text/plain part.

func Bodyf

func Bodyf(s string, args ...any) bodyPart

Bodyf returns a new text/plain part.

func Cc

func Cc(addr ...string) []recipient

func CcAddress

func CcAddress(addr ...mail.Address) []recipient

func CcNames

func CcNames(nameAddr ...string) []recipient

func From

func From(name, address string) mail.Address

From makes creating a mail.Address a bit more convenient.

mail.Address{Name: "foo, Address: "foo@example.com}
blackmail.From{"foo, "foo@example.com)

func Headers

func Headers(keyValue ...string) bodyPart

Headers adds the headers to the message.

This will override any headers set automatically by the system, such as Date: or Message-Id:

Headers("My-Header", "value",
    "Message-Id", "<my-message-id@example.com>")

func HeadersAutoreply

func HeadersAutoreply() bodyPart

HeadersAutoreply sets headers to indicate this message is a an autoreply.

See e.g: https://www.arp242.net/autoreply.html#what-you-need-to-set-on-your-auto-response

func InlineImage

func InlineImage(contentType, filename string, body []byte) bodyPart

InlineImage returns a new inline image part.

It will try to guess the Content-Type if empty.

Then use "cid:blackmail:<n>" to reference it:

<img src="cid:blackmail:1">     First InlineImage()
<img src="cid:blackmail:2">     Second InlineImage()

func Message

func Message(subject string, from mail.Address, rcpt []recipient, firstPart bodyPart, parts ...bodyPart) ([]byte, []string, error)

Message formats a message.

func NopCloser

func NopCloser(r io.Writer) io.WriteCloser

func To

func To(addr ...string) []recipient

To sets the To: from a list of email addresses.

func ToAddress

func ToAddress(addr ...mail.Address) []recipient

ToAddress sets the To: from a list of mail.Addresses.

func ToNames

func ToNames(nameAddr ...string) []recipient

ToNames sets the To: from a list of "name", "addr" arguments.

func With

func With(ctx context.Context, m Mailer) context.Context

With returns a copy of the context with the Mailer as a value.

Types

type Mailer

type Mailer interface {
	// Send an email.
	Send(subject string, from mail.Address, rcpt []recipient, firstPart bodyPart, parts ...bodyPart) error

	// Info returns some information about this mailer for debugging purposes.
	Info() map[string]any
}

A Mailer sends messages.

func Get

func Get(ctx context.Context) Mailer

Get retrieves the Mailer stored on the context with With, returning nil if there is no context stored.

func MustGet

func MustGet(ctx context.Context) Mailer

MustGet works like Get, but will panic on errors.

func NewRelay

func NewRelay(smtpURL string, opts *RelayOptions) (Mailer, error)

NewRelay returns a new relay mailer.

Any URL will be used as a SMTP relay; "smtp://" for unencrypted or STARTTLS connections, and "smtps://" for TLS connections.

For example:

"smtp://user:pass@mail.example.com:587"
"smtps://user:pass@mail.example.com"

func NewWriter

func NewWriter(w io.Writer) Mailer

NewWriter creates a writer mailer, which writes to w. Mainly for test and dev setups.

type RelayOptions

type RelayOptions struct {
	// Auth sets the AUTH method; currently LOGIN, PLAIN, and CRAM-MD5 are
	// supported.
	//
	// In general, PLAIN is preferred and is the default. Note that CRAM-MD5
	// only provides weak security over untrusted connections.
	Auth smtp.AuthMethod

	// TLS configuration for smtps and STARTTLS.
	TLS *tls.Config

	Debug io.Writer
}

func (RelayOptions) String

func (o RelayOptions) String() string

Directories

Path Synopsis
cmd
blackmail command
internal
ztest
Package ztest contains helper functions that are useful for writing tests.
Package ztest contains helper functions that are useful for writing tests.
ztest/image
Package image contains helpers for testing image-related code.
Package image contains helpers for testing image-related code.
Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.
Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.

Jump to

Keyboard shortcuts

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