go-message: github.com/emersion/go-message Index | Examples | Files | Directories

package message

import "github.com/emersion/go-message"

Package message implements reading and writing multipurpose messages.

RFC 2045, RFC 2046 and RFC 2047 defines MIME, and RFC 2183 defines the Content-Disposition header field.

Add this import to your package if you want to handle most common charsets by default:

import (
	_ "github.com/emersion/go-message/charset"
)

Code:

// Let's assume r is an io.Reader that contains a message.
var r io.Reader

m, err := message.Read(r)
if message.IsUnknownCharset(err) {
    log.Println("Unknown encoding:", err)
} else if err != nil {
    log.Fatal(err)
}

// We'll add "This message is powered by Go" at the end of each text entity.
poweredBy := "\n\nThis message is powered by Go."

var b bytes.Buffer
w, err := message.CreateWriter(&b, m.Header)
if err != nil {
    log.Fatal(err)
}

// Define a function that transforms message.
var transform func(w *message.Writer, e *message.Entity) error
transform = func(w *message.Writer, e *message.Entity) error {
    if mr := e.MultipartReader(); mr != nil {
        // This is a multipart entity, transform each of its parts
        for {
            p, err := mr.NextPart()
            if err == io.EOF {
                break
            } else if err != nil {
                return err
            }

            pw, err := w.CreatePart(p.Header)
            if err != nil {
                return err
            }

            if err := transform(pw, p); err != nil {
                return err
            }

            pw.Close()
        }
        return nil
    } else {
        body := e.Body
        if strings.HasPrefix(m.Header.Get("Content-Type"), "text/") {
            body = io.MultiReader(body, strings.NewReader(poweredBy))
        }
        _, err := io.Copy(w, body)
        return err
    }
}

if err := transform(w, m); err != nil {
    log.Fatal(err)
}
w.Close()

log.Println(b.String())

Index

Examples

Package Files

charset.go encoding.go entity.go header.go message.go multipart.go writer.go

Variables

var CharsetReader func(charset string, input io.Reader) (io.Reader, error)

CharsetReader, if non-nil, defines a function to generate charset-conversion readers, converting from the provided charset into UTF-8. Charsets are always lower-case. utf-8 and us-ascii charsets are handled by default. One of the the CharsetReader's result values must be non-nil.

Importing github.com/emersion/go-message/charset will set CharsetReader to a function that handles most common charsets. Alternatively, CharsetReader can be set to e.g. golang.org/x/net/html/charset.NewReaderLabel.

func IsUnknownCharset Uses

func IsUnknownCharset(err error) bool

IsUnknownCharset returns a boolean indicating whether the error is known to report that the charset advertised by the entity is unknown.

type Entity Uses

type Entity struct {
    Header Header    // The entity's header.
    Body   io.Reader // The decoded entity's body.
    // contains filtered or unexported fields
}

An Entity is either a whole message or a one of the parts in the body of a multipart entity.

func New Uses

func New(header Header, body io.Reader) (*Entity, error)

New makes a new message with the provided header and body. The entity's transfer encoding and charset are automatically decoded to UTF-8.

If the message uses an unknown transfer encoding or charset, New returns an error that verifies IsUnknownCharset, but also returns an Entity that can be read.

func NewMultipart Uses

func NewMultipart(header Header, parts []*Entity) (*Entity, error)

NewMultipart makes a new multipart message with the provided header and parts. The Content-Type header must begin with "multipart/".

If the message uses an unknown transfer encoding, NewMultipart returns an error that verifies IsUnknownCharset, but also returns an Entity that can be read.

func Read Uses

func Read(r io.Reader) (*Entity, error)

Read reads a message from r. The message's encoding and charset are automatically decoded to raw UTF-8. Note that this function only reads the message header.

If the message uses an unknown transfer encoding or charset, Read returns an error that verifies IsUnknownCharset, but also returns an Entity that can be read.

Code:

// Let's assume r is an io.Reader that contains a message.
var r io.Reader

m, err := message.Read(r)
if message.IsUnknownCharset(err) {
    // This error is not fatal
    log.Println("Unknown encoding:", err)
} else if err != nil {
    log.Fatal(err)
}

if mr := m.MultipartReader(); mr != nil {
    // This is a multipart message
    log.Println("This is a multipart message containing:")
    for {
        p, err := mr.NextPart()
        if err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }

        t, _, _ := p.Header.ContentType()
        log.Println("A part with type", t)
    }
} else {
    t, _, _ := m.Header.ContentType()
    log.Println("This is a non-multipart message with type", t)
}

func (*Entity) MultipartReader Uses

func (e *Entity) MultipartReader() MultipartReader

MultipartReader returns a MultipartReader that reads parts from this entity's body. If this entity is not multipart, it returns nil.

func (*Entity) WriteTo Uses

func (e *Entity) WriteTo(w io.Writer) error

WriteTo writes this entity's header and body to w.

type Header struct {
    textproto.Header
}

A Header represents the key-value pairs in a message header.

func (*Header) ContentDisposition Uses

func (h *Header) ContentDisposition() (disp string, params map[string]string, err error)

ContentDisposition parses the Content-Disposition header field, as defined in RFC 2183.

func (*Header) ContentType Uses

func (h *Header) ContentType() (t string, params map[string]string, err error)

ContentType parses the Content-Type header field.

If no Content-Type is specified, it returns "text/plain".

func (*Header) Fields Uses

func (h *Header) Fields() HeaderFields

Fields iterates over all the header fields.

The header may not be mutated while iterating, except using HeaderFields.Del.

func (*Header) FieldsByKey Uses

func (h *Header) FieldsByKey(k string) HeaderFields

FieldsByKey iterates over all fields having the specified key.

The header may not be mutated while iterating, except using HeaderFields.Del.

func (*Header) SetContentDisposition Uses

func (h *Header) SetContentDisposition(disp string, params map[string]string)

SetContentDisposition formats the Content-Disposition header field, as defined in RFC 2183.

func (*Header) SetContentType Uses

func (h *Header) SetContentType(t string, params map[string]string)

SetContentType formats the Content-Type header field.

func (*Header) SetText Uses

func (h *Header) SetText(k, v string)

SetText sets a plaintext header field.

func (*Header) Text Uses

func (h *Header) Text(k string) (string, error)

Text parses a plaintext header field. The field charset is automatically decoded to UTF-8. If the header field's charset is unknown, the raw field value is returned and the error verifies IsUnknownCharset.

type HeaderFields Uses

type HeaderFields interface {
    textproto.HeaderFields

    // Text parses the value of the current field as plaintext. The field
    // charset is decoded to UTF-8. If the header field's charset is unknown,
    // the raw field value is returned and the error verifies IsUnknownCharset.
    Text() (string, error)
}

HeaderFields iterates over header fields.

type MultipartReader Uses

type MultipartReader interface {
    io.Closer

    // NextPart returns the next part in the multipart or an error. When there are
    // no more parts, the error io.EOF is returned.
    //
    // Entity.Body must be read completely before the next call to NextPart,
    // otherwise it will be discarded.
    NextPart() (*Entity, error)
}

MultipartReader is an iterator over parts in a MIME multipart body.

type Writer Uses

type Writer struct {
    // contains filtered or unexported fields
}

Writer writes message entities.

If the message is not multipart, it should be used as a WriteCloser. Don't forget to call Close.

If the message is multipart, users can either use CreatePart to write child parts or Write to directly pipe a multipart message. In any case, Close must be called at the end.

Code:

var b bytes.Buffer

var h message.Header
h.SetContentType("multipart/alternative", nil)
w, err := message.CreateWriter(&b, h)
if err != nil {
    log.Fatal(err)
}

var h1 message.Header
h1.SetContentType("text/html", nil)
w1, err := w.CreatePart(h1)
if err != nil {
    log.Fatal(err)
}
io.WriteString(w1, "<h1>Hello World!</h1><p>This is an HTML part.</p>")
w1.Close()

var h2 message.Header
h1.SetContentType("text/plain", nil)
w2, err := w.CreatePart(h2)
if err != nil {
    log.Fatal(err)
}
io.WriteString(w2, "Hello World!\n\nThis is a text part.")
w2.Close()

w.Close()

log.Println(b.String())

func CreateWriter Uses

func CreateWriter(w io.Writer, header Header) (*Writer, error)

CreateWriter creates a new message writer to w. If header contains an encoding, data written to the Writer will automatically be encoded with it. The charset needs to be utf-8 or us-ascii.

func (*Writer) Close Uses

func (w *Writer) Close() error

Close implements io.Closer.

func (*Writer) CreatePart Uses

func (w *Writer) CreatePart(header Header) (*Writer, error)

CreatePart returns a Writer to a new part in this multipart entity. If this entity is not multipart, it fails. The body of the part should be written to the returned io.WriteCloser.

func (*Writer) Write Uses

func (w *Writer) Write(b []byte) (int, error)

Write implements io.Writer.

Directories

PathSynopsis
charsetPackage charset provides functions to decode and encode charsets.
mailPackage mail implements reading and writing mail messages.
textproto

Package message imports 10 packages (graph) and is imported by 46 packages. Updated 2019-09-18. Refresh now. Tools for package owners.