message

package
v2.3.1 Latest Latest
Warning

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

Go to latest
Published: Jan 31, 2023 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package message is the heart of this library. It provides objects for flexibly parsing and reading email messages (that survive even when the input is not strictly correct) and for generating new messages that are strictly correct. You can pair the parsing/reading tools with the generating tools to perform advanced email transformations.

You can deal with any message as an Opaque message object. You can create these from existing messages by calling the ParseOpaque() function. You can generate these objects by using a Buffer and then calling the Opaque() method.

If you want to be able to work with individual parts of a multipart method, you can use the Multipart message object instead. This is achieved for existing messages by parsing an Opaque message gotten via ParseOpaque() via the Parse() function:

opMsg, err := message.ParseOpaque(in)
if err != nil {
  panic(err)
}

msg, err := message.Parse(opMsg)
if err != nil {
  panic(err)
}

Or you can generate these messages by using a Buffer to add parts to a message and then call the Multipart() method.

Example (Create_message)
package main

import (
	"fmt"
	"net/smtp"

	"github.com/zostay/go-email/v2/message"
	"github.com/zostay/go-email/v2/message/transfer"
)

func main() {
	// Build a part that will be the attached document
	resume, _ := message.AttachmentFile(
		"resume.pdf",
		"application/pdf",
		transfer.Base64,
	)

	// Build a part that will contain the message content as text
	text := &message.Buffer{}
	text.SetMediaType("text/plain")
	_, _ = fmt.Fprintln(text, "You will find my awesome resume attached.")

	// Build a part that will contain the message content as HTML
	html := &message.Buffer{}
	html.SetMediaType("text/html")
	_, _ = fmt.Fprintln(html, "You will find my <strong>awesome</strong> resume attached.")

	// Build the top-level message from the parts.
	main := &message.Buffer{}
	main.SetSubject("My resume")
	_ = main.SetTo("recruiter@example.com")
	_ = main.SetFrom("me@example.com")
	main.SetMediaType("multipart/mixed")
	main.Add(
		message.MultipartAlternative(html.Opaque(), text.Opaque()),
		resume,
	)
	mainMsg := main.Opaque()

	// send the message via SMTP
	c, err := smtp.Dial("smtp.example.com:25")
	if err != nil {
		panic(err)
	}

	_ = c.Hello("me")
	_ = c.Mail("me@example.com")
	_ = c.Rcpt("recruiter@example.com")
	w, err := c.Data()
	if err != nil {
		panic(err)
	}
	_, _ = mainMsg.WriteTo(w)
	_ = w.Close()
	_ = c.Quit()
}
Output:

Example (Rewrite_keywords)
package main

import (
	"io"
	"os"

	"github.com/zostay/go-email/v2/message"
)

func main() {
	msg, err := os.Open("input.msg")
	if err != nil {
		panic(err)
	}

	// WithoutMultipart() means we want the top level headers only.
	m, err := message.Parse(msg, message.WithoutMultipart())
	if err != nil {
		panic(err)
	}

	// update the keywords of the new message
	if kws, err := m.GetHeader().GetKeywords(); err == nil && len(kws) > 0 {
		for _, kw := range kws {
			if kw == "Snuffle" {
				out := &message.Buffer{}
				out.Header = *m.GetHeader() // copy the original header
				content := m.GetReader()
				_, err = io.Copy(out, content) // copy the original message body
				if err != nil {
					panic(err)
				}

				// add Upagus to Keywords
				outKws := make([]string, len(kws)+1)
				outKws[len(kws)] = "Upagus"
				out.SetKeywords(outKws...)

				outMsg, err := os.Create("output.msg")
				if err != nil {
					panic(err)
				}

				_, err = out.WriteTo(outMsg)
				if err != nil {
					panic(err)
				}
			}
		}
	}
}
Output:

Index

Examples

Constants

View Source
const (
	// DefaultMaxMultipartDepth is the default depth the parser will recurse
	// into a message.
	DefaultMaxMultipartDepth = 10

	// DefaultChunkSize the default size of chunks to read from the input while
	// splitting the message into header and body. Defaults to 16K, though this
	// could change at any time.
	DefaultChunkSize = 16_384

	// DefaultMaxHeaderLength is the default maximum byte length to scan before
	// giving up on finding the end of the header.
	DefaultMaxHeaderLength = bufio.MaxScanTokenSize

	// DefaultMaxPartLength is the default maximum byte length to scan before
	// given up on scanning a message part at any given level.
	DefaultMaxPartLength = bufio.MaxScanTokenSize
)

Constants related to Parse() options.

View Source
const (
	// DefaultMultipartContentType is the Content-type to use with a multipart
	// message when no explicit Content-type header has been set.
	DefaultMultipartContentType = "multipart/mixed"
)

Variables

View Source
var (
	// ErrPartsBuffer is returned by Write() if that method is called after
	// calling the Add() method.
	ErrPartsBuffer = errors.New("message buffer is in parts mode")

	// ErrOpaqueBuffer is returned by Add() if that method is called after
	// calling the Write() method.
	ErrOpaqueBuffer = errors.New("message buffer is in opaque mode")

	// ErrModeUnset is returned by Opaque() and Multipart() when they are called
	// before anything has been written to the current buffer.
	ErrModeUnset = errors.New("no message has been built")

	// ErrParsesAsNotMultipart is returned by Multipart() when the Buffer is in
	// ModeOpaque and the message is not at all a *Multipart message.
	ErrParsesAsNotMultipart = errors.New("cannot parse non-multipart message as multipart")
)
View Source
var (
	// ErrLargeHeader is returned by Parse when the header is longer than the
	// configured WithMaxHeaderLength option (or the default,
	// DefaultMaxHeaderLength).
	ErrLargeHeader = errors.New("the header exceeds the maximum parse length")

	// ErrLargePart is returned by Parse when  apart is longer than the configured
	// WithMaxPartLength option (or the default, DefaultMaxPartLength).
	ErrLargePart = errors.New("a message part exceeds the maximum parse length")
)

Errors that occur during parsing.

Functions

func GenerateBoundary

func GenerateBoundary() string

GenerateBoundary will generate a random MIME boundary that is probably unique in most circumstances.

func GenerateSafeBoundary

func GenerateSafeBoundary(contents string) string

GenerateSafeBoundary will generate a random MIME boundary that is guaranteed to be safe with the given corpus of data. Use this when you want to generate a boundary for a known set of parts:

boundary := encoding.GenerateSafeBoundary(strings.Join("", parts))

using this is likely to be total overkill, but in case you're paranoid.

Types

type Buffer

type Buffer struct {
	header.Header
	// contains filtered or unexported fields
}

Buffer provides tools for constructing email messages. It can operate in either of two modes, depending on how you want to construct your message.

* Opaque mode. When you use the Buffer as an io.Writer by calling the Write() method, you have chosen to treat the email message as a collection of bytes.

* Multipart mode. When you use the Buffer to manipulate the parts of the message, such as calling the Add() method, you have chosen to treat the email message as a collection of sub-parts.

You may not use a Buffer in both modes. If you call the Write() method first, then any subsequent call to the Add() method will return ErrOpaqueBuffer. If you call the Add() method first, then any call to the Write() method will result in ErrMultipartBuffer being returned.

The BufferMode may be checked using the Mode() method.

Whatever the mode is, you may call either Opaque() or Multipart() to get the constructed message at the end. However, there are some caveats, so be sure to about them in the documentation of those methods.

Example (Multipart_buffer)
package main

import (
	"fmt"
	"os"

	"github.com/zostay/go-email/v2/message"
	"github.com/zostay/go-email/v2/message/transfer"
)

func main() {
	mm := &message.Buffer{}
	mm.SetSubject("Fancy message")
	mm.SetMediaType("multipart/mixed")

	txtPart := &message.Buffer{}
	txtPart.SetMediaType("text/plain")
	_, _ = fmt.Fprintln(txtPart, "Hello *World*!")

	htmlPart := &message.Buffer{}
	htmlPart.SetMediaType("text/html")
	_, _ = fmt.Fprintln(htmlPart, "Hello <b>World</b>!")

	mm.Add(message.MultipartAlternative(txtPart.Opaque(), htmlPart.Opaque()))

	imgAttach, _ := message.AttachmentFile(
		"image.jpg",
		"image/jpeg",
		transfer.Base64,
	)
	mm.Add(imgAttach)

	_, _ = mm.Opaque().WriteTo(os.Stdout)
}
Output:

Example (Opaque_buffer)
package main

import (
	"fmt"
	"os"

	"github.com/zostay/go-email/v2/message"
)

func main() {
	buf := &message.Buffer{}
	buf.SetSubject("Some spam for you inbox")
	_, _ = fmt.Fprintln(buf, "Hello World!")
	msg := buf.Opaque()
	_, _ = msg.WriteTo(os.Stdout)
}
Output:

func NewBlankBuffer added in v2.2.0

func NewBlankBuffer(part Part) *Buffer

NewBlankBuffer starts a Buffer from the given Part by cloning the header.Header and preparing it to hold the same contents (i.e., setting the BufferMode based upon the result of IsMultipart()). It does not copy the contents or parts within. See NewBuffer() for a complete clone.

func NewBuffer added in v2.2.0

func NewBuffer(part Part) (*Buffer, error)

NewBuffer returns a buffer copied from the given message.Part. It will have a message.BufferMode set to either message.ModeOpaque or message.ModeMultipart based upon the return value of the IsMultipart() of the part. This will walk through all parts in the message part tree and convert them all to buffers. This will read the contents of all the Opaque objects in the process.

This returns an error if there's an error while copying the data from an Opaque part to the Buffer.

func (*Buffer) Add

func (b *Buffer) Add(msgs ...Part)

Add will add one or more parts to the message. It will panic if you attempt to call this function after already calling Write() or using this object as an io.Writer.

func (*Buffer) GetHeader added in v2.1.0

func (b *Buffer) GetHeader() *header.Header

GetHeader returns the header associated with this Buffer.

func (*Buffer) GetParts added in v2.1.0

func (b *Buffer) GetParts() []Part

GetParts returns the parts set on this buffer. This will panic if Mode() is BufferUnset.

func (*Buffer) GetReader added in v2.1.0

func (b *Buffer) GetReader() io.Reader

GetReader returns the internal buffer as an io.Reader. This may be called multiple times. However, if the Mode() is BufferUnset, this will panic.

func (*Buffer) IsEncoded added in v2.1.0

func (b *Buffer) IsEncoded() bool

IsEncoded returns whether the bytes of this Buffer are already encoded or not. This will panic if Mode() is ModeUnset.

func (*Buffer) IsMultipart added in v2.1.0

func (b *Buffer) IsMultipart() bool

IsMultipart returns true if Mode() returns ModeMultipart. It returns false if Mode() returns ModeOpaque. It will panic otherwise.

func (*Buffer) Mode

func (b *Buffer) Mode() BufferMode

Mode returns a constant that indicates what mode the Buffer is in. Until a modification method is called, this will return ModeUnset. Once a modification method is called, it will return ModeOpaque if the Buffer has been used as an io.Writer or ModeMultipart if parts have been added to the Buffer.

func (*Buffer) Multipart

func (b *Buffer) Multipart() (*Multipart, error)

Multipart will return a Multipart message based upon the content written to the Buffer. This method will fail with an error if there's a problem. The behavior of this method depends on which mode the Buffer is in when called.

If you are just generating a message to output to a file or network socket (e.g., an SMTP connection), you probably do not want to call this method. Calling Opaque is generally preferable in that case. However, this is provided in cases when you really do want a Multipart for some reason.

Whenever you plan on calling this method, you should set the Content-Type header yourself to one of the multipart/* types (e.g., multipart/alternative if you are providing text and HTML forms of the same message or multipart/mixed if you are providing attachments).

If you do not provide that header yourself, this method will set it to DefaultMultipartContentType, which may not be what you want.

It will also check to see if the Content-type boundary is set and set it to something random using mime.GenerateBound() automatically. This boundary will then be used when joining the parts together during serialization.

If the BufferMode is ModeUnset, this method will panic.

If the BufferMode is ModeOpaque, the bytes that have been written to the buffer must be parsed in order to generate the returned *Multipart. In that case, the function will create an *Opaque and use the Parse() function to try to generate a *Multipart. The Parse() function will be called with the WithoutRecursion() option. Errors from Parse() will be passed back from this method. If Parse() returns nil or an *Opaque, this method will return nil. Otherwise, the *Multipart from Parse() will be returned. If an *Opaque is returned from Parse() without an error, this method will return nil with ErrParsesAsNotMultipart.

If the BufferMode is ModeMultipart, the Header and collected parts will be returned in the returned *Multipart.

After this method is called, the Buffer should be disposed of and no longer used.

func (*Buffer) Opaque

func (b *Buffer) Opaque() *Opaque

Opaque will return an Opaque message based upon the content written to the Buffer. The behavior of this method depends on which mode the Buffer is in.

This method will panic if the BufferMode is ModeUnset.

If the BufferMode is ModeOpaque, the Header and the bytes written to the internal buffer will be returned in the *Opaque. Opaque will return a MIME

If the BufferMode is ModeMultipart, the parts will be serialized into a byte buffer and that will be attached with the Header to the returned *Opaque object.

In this last case, you should set the Content-Type header yourself to one of the multipart/* types (e.g., multipart/alternative if you are providing text and HTML forms of the same message or multipart/mixed if you are providing attachments).

If you do not provide that header yourself, this method will set it to DefaultMultipartContentType, which may not be what you want.

It will also check to see if the Content-type boundary is set and set it to something random using mime.GenerateBound() automatically. This boundary will then be used when joining the parts together during serialization.

After this method is called, the Buffer should be disposed of and no longer used.

func (*Buffer) OpaqueAlreadyEncoded deprecated

func (b *Buffer) OpaqueAlreadyEncoded() *Opaque

OpaqueAlreadyEncoded works just like Opaque(), but marks the object as already having the Content-transfer-encoding applied. Use this when you write a message in encoded form.

NOTE: This does not perform any encoding! If you want the output to be automatically encoded, you actually want to call Opaque() and then WriteTo() on the returned object will perform encoding. This method is for indicating that you have already performed the required encoding.

After this method is called, the Buffer should be disposed of and no longer used.

Deprecated: Use SetEncoded to perform this task instead. This will ignore the encoded flag entirely.

func (*Buffer) SetEncoded added in v2.1.0

func (b *Buffer) SetEncoded(e bool)

SetEncoded sets the encoded flag for this Buffer. If this Buffer has a BufferMode of ModeMultipart, this setting is without meaning. If it is ModeOpaque, then whatever value this has will be set as the IsEncoded() flag of the returned Opaque message. We assume the data written to the io.Writer is decoded by default.

func (*Buffer) SetMultipart added in v2.1.0

func (b *Buffer) SetMultipart(capacity int)

SetMultipart sets the Mode of the buffer to ModeMultipart. This is useful during message transformation or when you want to pre-allocate the capacity of the internal slice used to hold parts. When calling this method, you need to pass the expected capacity of the multipart message. This will panic if the mode is already ModeOpaque.

func (*Buffer) SetOpaque added in v2.1.0

func (b *Buffer) SetOpaque()

SetOpaque sets the Mode of the buffer to ModeOpaque. This is useful during message transformation, especially if the message content is to be empty. This will panic if the mode is already ModeMultipart.

func (*Buffer) Write

func (b *Buffer) Write(p []byte) (int, error)

Write implements io.Writer so you can write the message to this buffer. This will panic if you attempt to call this method or use this object as an io.Writer after calling Add.

func (*Buffer) WriteTo added in v2.1.0

func (b *Buffer) WriteTo(w io.Writer) (int64, error)

WriteTo writes the buffer to the given writer. This will panic if Mode() is BufferUnset.

type BufferMode

type BufferMode int
const (
	// ModeUnset indicates that the Buffer has not yet been modified.
	ModeUnset BufferMode = iota

	// ModeOpaque indicates that the Buffer has been used as an io.Writer.
	ModeOpaque

	// ModeMultipart indicates that the Buffer has had the parts manipulated.
	ModeMultipart
)

type Generic

type Generic = Part

Generic is just an alias for Part, which is intended to convey additional semantics:

1. The message returned is not necessarily a sub-part of a message.

2. The returned message is guaranteed to either be a *Opaque or a *Multipart. Therefore, it is safe to use this in a type-switch and only look for either of those two objects.

func Parse

func Parse(r io.Reader, opts ...ParseOption) (Generic, error)

Parse will consume input from the given reader and return a Generic message containing the parsed content. Parse will proceed in two or three phases.

During the first phase, the given io.Reader will be read in chunks at a time, as defined by the WithChunkSize() option (or by the default, DefaultChunkSize). Each chunk will be checked for a double line break of some kind (e.g., "\r\n\r\n" or "\n\n" are the most common). Once found, that line break is used to determine what line break the message will use for breaking up the header into fields. The fields will be parsed from the accumulated header chunks using the bytes read in so far preceding the header break.

The last part of the final chunk read and the remainder of the io.Reader will then make up the body content of an *Opaque message.

If accumulated header chunks total larger than the WithMaxHeaderLength() option (or the default, DefaultMaxHeaderLength) while searching for the double line break, the Parse will fail with an error and return ErrLargeHeader. If this happens, the io.Reader may be in a partial read state.

If the first phase completes successfully, the second phase will begin. During the second phase, the *Opaque message created during the first phase may be transformed into a *Multipart, if the message seems to be a multipart message. The way this will proceed is determined by the WithMaxDepth() related options and also the WithMaxPartLength() option.

If the Content-type of the message is a multipart/* MIME type and the WithMaxDepth() option (or the default, DefaultMaxMultipartDepth) is less than or greater than 0, the body will be scanned to break it into parts according to the boundary parameter set on the Content-type. The parts must be smaller than the setting in WithMaxPartLength() option (or the default, DefaultMaxPartLength). If not, the parse will fail with ErrLargePart.

These newly broken up parts will each go through the two phase parsing process themselves. This continues until either the deepest multipart sub-part is parsed or the maximum depth is reached.

If the DecodeTransferEncoding() option is passed, a third phase of parsing will also be performed. The parts of the message that do not have sub-parts and have a Content-transfer-encoding header set, will be decoded.

This third phase is not the default behavior because one of those goals of this library is to try and preserve the original bytes as is. However, decoding the transfer encoding and then re-encoding it again is very likely to modify the original message. The modification will be trivial, but it won't preserve the original message for round-trip modification with minimal changes.

If the transfer encoding is decoded in this third phase, rendering the message with WriteTo() will perform new encoding of the data and write freshly encoded data to the destination writer. However, if you read the data using the io.Reader, you will receive un-encoded bytes.

Errors at any point in the process may lead to a completely failed parse, especially those involving ErrLargeHeader or ErrLargePart. However, whenever possible, the partially parsed message object will be returned.

The original io.Reader provided may or may not be completely read upon return. This is true whether an error has occurred or not. If you either read all the message body contents of all sub-parts or use the WriteTo() method on the returned message object, the io.Reader will be completely consumed.

Example
package main

import (
	"os"
	"strings"

	"github.com/zostay/go-email/v2/message"
)

func main() {
	r := strings.NewReader("Subject: test\n\nThis is a test.")
	msg, err := message.Parse(r)
	if err != nil {
		panic(err)
	}
	// snip end

	_, _ = msg.WriteTo(os.Stdout)
}
Output:

Example (Options)
package main

import (
	"io"
	"os"

	"github.com/zostay/go-email/v2/message"
)

func main() {
	var r io.Reader
	// snip start

	// This will only parse to the 5th layer deep.
	m, _ := message.Parse(r, message.WithMaxDepth(5)) //nolint:ineffassign

	// This will not parse even the first layer.
	// This always returns an *message.Opaque object.
	m, _ = message.Parse(r, message.WithoutMultipart()) //nolint:ineffassign
	// ^^^ same as WithMaxDepth(0)

	// This will parse the first layer, but no further. If the message is a
	// multipart message it will be *message.Multipart but all sub-parts are
	// guaranteed to be *message.Opaque. Otherwise, it may return *message.Opaque.
	m, _ = message.Parse(r, message.WithoutRecursion()) //nolint:ineffassign
	// ^^^ same as WithMaxDepth(1)

	// Or you can turn off all limits and get everything...
	m, _ = message.Parse(r, message.WithUnlimitedRecursion())
	// ^^^ ame as WithMaxDepth(-1)
	// snip end

	_, _ = m.WriteTo(os.Stdout)
}
Output:

type Multipart

type Multipart struct {
	// Header is the header for the message.
	header.Header
	// contains filtered or unexported fields
}

Multipart is a multipart MIME message. When building these methods the MIME type set in the Content-type header should always start with multipart/*.

func MultipartAlternative

func MultipartAlternative(parts ...Part) *Multipart

MultipartAlternative returns a Multipart with a Content-type header set to multipart/alternative and the given parts attached.

func MultipartMixed

func MultipartMixed(parts ...Part) *Multipart

MultipartMixed returns a Multipart with a Content-type header set to multipart/mixed and the given parts attached.

func (*Multipart) GetHeader

func (mm *Multipart) GetHeader() *header.Header

GetHeader returns the header for the message.

func (*Multipart) GetParts

func (mm *Multipart) GetParts() []Part

GetParts returns the sub-parts of this message or nil if there aren't any.

func (*Multipart) GetReader

func (mm *Multipart) GetReader() io.Reader

GetReader always returns nil and ErrMultipart.

func (*Multipart) IsEncoded

func (mm *Multipart) IsEncoded() bool

IsEncoded always returns false.

func (*Multipart) IsMultipart

func (mm *Multipart) IsMultipart() bool

IsMultipart always returns true.

func (*Multipart) WriteTo

func (mm *Multipart) WriteTo(w io.Writer) (int64, error)

WriteTo writes the Opaque header and parts to the destination io.Writer. This method will fail with an error if the given message does not have a Content-type boundary parameter set. May return an error on an IO error as well.

This may only be safely called one time because it will consume all the bytes from all the io.Reader objects associated with all the given Opaque objects within.

type Opaque

type Opaque struct {
	// Header will contain the header of the message. A top-level message must
	// have several headers to be correct. A message part should have one or
	// more headers as well.
	header.Header

	// Reader will contain the body content of the message. If the content is
	// zero bytes long, then Reader should be set to nil.
	io.Reader
	// contains filtered or unexported fields
}

Opaque is the base-level email message interface. It is simply a header and a message body, very similar to the net/mail message implementation.

func AttachmentFile

func AttachmentFile(fn, mt, te string) (*Opaque, error)

AttachmentFile is a constructor that will create an Opaque from the given filename and MIME type. This will read the given file path from the disk, make that filename the name of an attachment, and return it. It will return an error if there's a problem reading the file from the disk.

The last argument is optional and is the transfer encoding to use. Use transfer.None if you do not want to set a transfer encoding.

func (*Opaque) GetHeader

func (m *Opaque) GetHeader() *header.Header

GetHeader returns the header for the message.

func (*Opaque) GetParts

func (m *Opaque) GetParts() []Part

GetParts always returns nil and ErrNotMultipart.

func (*Opaque) GetReader

func (m *Opaque) GetReader() io.Reader

GetReader returns the reader containing the body of the message.

If IsEncoded() returns false, the data returned by reading this io.Reader may differ from the data that would be written via WriteTo(). This is because the data here will have been decoded, but WriteTo() will encode the data anew as it writes.

func (*Opaque) IsEncoded

func (m *Opaque) IsEncoded() bool

IsEncoded returns true if the Content-transfer-encoding has not been decoded for the bytes returned by the associated io.Reader. It will return false if that decoding has been performed.

Be aware that a false value here does not mean any actually changes to the bytes have been made. If the Content-type of this message is a "multipart/*" type, then any Content-transfer-encoding is ignored. If the Content-transfer-encoding is set to something like "8bit", the transfer encoding returns the bytes as-is and no transformation of the data is performed anyway.

However, if this returns true, then reading the data from io.Reader will return exactly the same bytes as would be written via WriteTo().

func (*Opaque) IsMultipart

func (m *Opaque) IsMultipart() bool

IsMultipart always returns false.

func (*Opaque) WriteTo

func (m *Opaque) WriteTo(w io.Writer) (int64, error)

WriteTo writes the Opaque header and body to the destination io.Writer.

If the bytes head in io.Reader have had the Content-transfer-encoding decoded (e.g., the message was parsed with the DecodeTransferEncoding() option or was created via a Buffer), then this will encode the data as it is being written.

This can only be safely called once as it will consume the io.Reader.

Example
package main

import (
	"bytes"
	"os"

	"github.com/zostay/go-email/v2/message"
)

func main() {
	buf := bytes.NewBufferString("Hello World")
	msg := &message.Opaque{Reader: buf}
	msg.SetSubject("A message to nowhere")
	_, _ = msg.WriteTo(os.Stdout)
}
Output:

type ParseOption

type ParseOption func(pr *parser)

ParseOption refers to options that may be passed to the Parse function to modify how the parser works.

func DecodeTransferEncoding

func DecodeTransferEncoding() ParseOption

DecodeTransferEncoding is a ParseOption that enables the decoding of Content-transfer-encoding. By default, Content-transfer-encoding will not be decoded, which allows for safer round-tripping of messages. However, if you want to display or process the message body, you will want to enable this.

func WithChunkSize

func WithChunkSize(chunkSize int) ParseOption

WithChunkSize is a ParseOption that controls how many bytes to read at a time while parsing an email message. The default chunk size is DefaultChunkSize.

func WithMaxDepth

func WithMaxDepth(maxDepth int) ParseOption

WithMaxDepth is a ParseOption that controls how deep the parser will go in recursively parsing a multipart message. This is set to DefaultMaxDepth by default.

func WithMaxHeaderLength

func WithMaxHeaderLength(n int) ParseOption

WithMaxHeaderLength is a ParseOption that sets the maximum size the buffer is allowed to reach before parsing exits with an ErrLargeHeader error. During parsing, the io.Reader will be read from a chunk at a time until the end of the header is found. This setting prevents bad input from resulting in an out of memory error. Setting this to a value less than or equal to 0 will result in there being no maximum length. The default value is DefaultMaxHeaderLength.

func WithMaxPartLength

func WithMaxPartLength(n int) ParseOption

WithMaxPartLength is a ParseOption that sets the maximum size the buffer is allowed to reach while scanning for message parts at any level. The parts are parsed out at each level of depth separately, so this must be large enough to accommodate the largest part at the top level being parsed. If the part gets too large, Parse will fail with an ErrLargePart error. There is, at this time, no way to disable this limit.

func WithUnlimitedRecursion

func WithUnlimitedRecursion() ParseOption

WithUnlimitedRecursion is a ParseOption that will allow the parser to parse sub-parts of any depth.

func WithoutMultipart

func WithoutMultipart() ParseOption

WithoutMultipart is a ParseOption that will not allow parsing of any multipart messages. The message returned from Parse() will always be *Opaque.

You should use this option if all you are interested in is the top-level headers. For large email messages, use of this option can grant extreme improvements to memory performance. This is because this option prevents any multipart processing, which means the header will be read, parsed, and stored in memory. However, only a single chunk of the body will have been read. The rest of the input io.Reader is left unread.

func WithoutRecursion

func WithoutRecursion() ParseOption

WithoutRecursion is a ParseOption that will only allow a single level of multipart parsing.

type Part

type Part interface {
	io.WriterTo

	// IsMultipart will return true if this Part is a branch with nested
	// parts. You may call the GetParts() method to process the parts only if
	// this returns true. If it returns false, this Part is a leaf and it
	// has no sub-parts. You may call GetReader() only when this method returns
	// false.
	//
	// It is okay to skip call this and just call the GetReader() or GetParts()
	// methods directly so long as you check for the errors they may return.
	IsMultipart() bool

	// IsEncoded will return true if this Part will return the original bytes
	// from the associated io.Reader returned from GetReader(). If it returns
	// false, then the bytes returned from that io.Reader will have had any
	// Content-transfer-encoding decoded first. This does not indicate whether
	// any Content-transfer-encoding header is present or whether the encoding
	// made any changes to the bytes (e.g., the Content-transfer-encoding is set
	// to 7bit, we keep the data as-is and no special decoding is performed by
	// default).
	//
	// This method must return false if IsMultipart() returns true. As transfer
	// encodings cannot be applied to parts with sub-parts, this method makes
	// no sense in that circumstance anyway.
	IsEncoded() bool

	// GetHeader is available on all Part objects.
	GetHeader() *header.Header

	// GetReader provides the content of the message, but only if IsMultipart()
	// returns false. This must return nil if IsMultipart() returns true.
	GetReader() io.Reader

	// GetParts provides the content of a multipart message with sub-parts. This
	// should only be called when IsMultipart() returns true. This must return
	// nil if IsMultipart() is false.
	GetParts() []Part
}

Part is an interface define the parts of a Multipart. Each Part is either a branch or a leaf.

A branch Part is one that has sub-parts. In this case, the IsMultipart() method will return true. The GetParts() method is available, but the GetReader() must not be called.

A leaf Part is one that contains content. In this case, the IsMultipart() method will return false. The GetParts() method must not be called on a leaf Part. However, the GetReader() method will return a reader for reading the content of the part.

It should be noted that it is possible for a Part to contain content that is a multipart MIME message when IsMultipart() returns false. If the sub-parts have been serialized such that the parts are not provided separately. This is perfectly legal.

Directories

Path Synopsis
Package header provides low-level and high-level tooling for dealing with email message headers.
Package header provides low-level and high-level tooling for dealing with email message headers.
encoding
Package encoding provides a replacement encoder and decoder for use with mime.CharsetEncoder and mime.CharsetDecoder.
Package encoding provides a replacement encoder and decoder for use with mime.CharsetEncoder and mime.CharsetDecoder.
field
Package field provides low-level functions and types for working with individual header fields.
Package field provides low-level functions and types for working with individual header fields.
param
Package param provides a tool for dealing with parameterized headers.
Package param provides a tool for dealing with parameterized headers.
Package transfer contains utilities related to encoding and decoding transfer encodings, which interpret the Content-transfer-encoding header to apply certain 8bit to 7bit encodings.
Package transfer contains utilities related to encoding and decoding transfer encodings, which interpret the Content-transfer-encoding header to apply certain 8bit to 7bit encodings.
Package walker provides utilities for iterating through the message and all its parts.
Package walker provides utilities for iterating through the message and all its parts.

Jump to

Keyboard shortcuts

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