mime

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2022 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package mime provides tools for parsing and processing MIME email messages. If you are just looking to work with the primary headers, you might prefer to use the parse in "github.com/zostay/go-email/pkg/email/simple".

This provides tools for reading the headers and bodies of subparts and the header and messages of those subparts and so on. This will also perform email decoding for base64, quotedprintable, and charset encodings.

Index

Examples

Constants

View Source
const (
	// ContentType is the name to give the Content-type header.
	ContentType = "Content-type"

	// CTCharset is the name to give the charset parameter of the Content-type
	// header.
	CTCharset = "charset"

	// CTBoundary is the name to give teh Content-type boundary parameter.
	CTBoundary = "boundary"

	// ContentDisposition is the name to give the Content-disposition header.
	ContentDisposition = "Content-disposition"

	// CDFilename is the name to give the Content-disposition filename parameter.
	CDFilename = "filename"
)
View Source
const (
	// MaxMultipartDepth is the default depth the parser will recurse into a
	// message.
	DefaultMaxMultipartDepth = 10
)

Variables

View Source
var (
	// CharsetEncoder is the Encoder used for outputting unicode strings as
	// bytes in the output format. You may replace this with a custom encoder if
	// you like or to make use of an encoder that is able to handle a wide
	// variety of encodings, you can import the encoding package:
	//  import _ "github.com/zostay/go-email/pkg/encoding"
	CharsetEncoder Encoder = DefaultCharsetEncoder

	// CharsetDecoder is the Decoder used for transforming input characters into
	// unicode for use in the decoded fields of MIME messages. You may replace
	// this with a customer decoder you prefer or to make use of a decoder that
	// supports a broad range of encodings, you can import the encoding package:
	//  import _ "github.com/zostay/go-email/pkg/encoding"
	CharsetDecoder Decoder = DefaultCharsetDecoder
)
View Source
var (
	// TransferDecoders contains a map of transfer decoder objects for handling
	// all standard transfer encodings. This will contain values for 7bit, 8bit,
	// binary, base64, and quoted-printable transfer encodings.
	TransferDecoders map[string]TransferDecoder
)

Functions

func CharsetDecoderToCharsetReader

func CharsetDecoderToCharsetReader(decode func(string, []byte) (string, error)) func(string, io.Reader) (io.Reader, error)

CharsetDecoderToCharsetReader transforms a CharsetDecoder defined here into the interface used by mime.WordDecoder.

func DecodeAsIs

func DecodeAsIs(b []byte) ([]byte, error)

DecodeAsIs is the identity encoding used whenever no decoding is required for a particular transfer encoding.

func DecodeFromBase64

func DecodeFromBase64(b []byte) ([]byte, error)

DecodeFromBase64 is used to output unencoded bytes from MIME BASE-64 encoded bytes.

func DecodeFromQuotedPrintable

func DecodeFromQuotedPrintable(b []byte) ([]byte, error)

DecodeFromQuotedPrintable is used to output unencoded bytes from quoted-printable encoded bytes.

func DecodeToBase64

func DecodeToBase64(b []byte) ([]byte, error)

DecodeToBase64 is used to output MIME BASE-64 encoded bytes from unencoded bytes.

func DecodeToQuotedPrintable

func DecodeToQuotedPrintable(b []byte) ([]byte, error)

DecodeToQuotedPrintable is used to output quoted-printed encoded bytes from unencoded bytes.

func DefaultCharsetDecoder

func DefaultCharsetDecoder(charset string, b []byte) (string, error)

DefaultCharsetDecoder is the default decoder. It is able to handle us-ascii, iso-8859-1 (a.k.a. latin1), and utf-8 only. Anything else will result in an error.

When us-ascii is input, any NUL or 8-bit character (i.e., bytes greater than 0x7f) will be translated into unicode.ReplacementChar.

When utf-8 is input, the bytes will be read in and transformed into runes such that only valid unicode bytes will be permitted in. Errors will be brought in as unicode.ReplacementChar.

func DefaultCharsetEncoder

func DefaultCharsetEncoder(charset, s string) ([]byte, error)

DefaultCharsetEncoder is the default encoder. It is able to handle us-ascii and utf-8 only. Anything else will result in an error.

When outputting us-ascii, ios-8859-1 (a.k.a. latin1), any utf-8 character present that does not fit in us-ascii will be replaced with "\x1a", which is the ASCII SUB character.

func WithMaxDepth

func WithMaxDepth(d int) option

WithMaxDepth sets the maximum depth the parse is allowed to descend recursively within subparts. This value is saved as part of the object and future calls to FillParts will obey it. If this option is not passed, it will use DefaultMaxMultipartDepth.

Types

type Decoder

type Decoder func(charset string, b []byte) (string, error)

Decoder represents the character decoding function used by the mime package for transforming parsed data supplied in arbitrary text encodings. This will be decoded into native unicode.

The decoder should only permit a valid transformation from the source format into unicode. Any byte present in the input that is invalid for the source character encoding should be replaced with the unicode.ReplacementChar.

If the source charset is not supported, bytes should be returned as nil and an error should be returned.

type Encoder

type Encoder func(charset, s string) ([]byte, error)

Encoder represents the character encoding function used by the mime package to transform data supplied in native unicode format to be written out in the character encoding indicated by the charset of the message.

The encoder should attempt to clean up and only output text that is valid in the target encoding. If no encoding is present, then us-ascii should be assumed.

If the target charset is not supported, bytes should be returned as nil and an error should be returned.

type Header struct {
	simple.Header
}

Header provides tools built on simple.Header to work with MIME headers.

func NewHeader

func NewHeader(lb string, hs ...interface{}) (*Header, error)

NewHeader will build a new MIME header. The arguments in the latter part are provided as name/value pairs. The names must be provided as strings. The values may be provided as string, []byte, time.Time, MediaType, addr.Mailbox, addr.Address, addr.MailboxList, addr.AddressList, or a fmt.Stringer.

If one of the header arguments comes in as an unexpected object or with an odd length, this will return an error.

If one of the header arguments includes a character illegal for use in a header, this will return an error.

On success, it will return a constructed header object.

func (*Header) HeaderContentDisposition

func (h *Header) HeaderContentDisposition() string

HeaderContentDisposition is the value of the Content-dispotion header value.

func (*Header) HeaderContentType

func (h *Header) HeaderContentType() string

HeaderContentType retrieves only the full MIME type set in the Content-type header.

func (*Header) HeaderContentTypeBoundary

func (h *Header) HeaderContentTypeBoundary() string

HeaderContentTypeBoundary is the boundary set on the Content-type header for multipart messages.

func (*Header) HeaderContentTypeCharset

func (h *Header) HeaderContentTypeCharset() string

HeaderContentTypeCharset retrieves the character set on the Content-type header or an empty string.

func (*Header) HeaderContentTypeSubtype

func (h *Header) HeaderContentTypeSubtype() string

HeaderContentTypeSubtype retrieves the second part, the subtype, of the MIME type set in the Content-type header.

func (*Header) HeaderContentTypeType

func (h *Header) HeaderContentTypeType() string

HeaderContentTypeType retrieves the first part, the type, of the MIME type set in the Content-Type header.

func (*Header) HeaderGetAddressList

func (h *Header) HeaderGetAddressList(n string) (addr.AddressList, error)

HeaderGetAddressList returns addresses for a header. If the header is not set or empty, it will return nil and no error. If the header has a value, but cannot be parsed as an address list, it will return nil and an error. If the header can be parsed as an email list, the email addresses will be returned.

This only returns the addresses for the first occurence of a header, as the email address headers are only permitted a single time in email.

func (*Header) HeaderGetAllAddressLists

func (h *Header) HeaderGetAllAddressLists(n string) (addr.AddressList, error)

HeaderGetAllAddressLists handles address headers that have multiple header entries, such as Delivered-To. This will return an address list for all the headers as a single AddressList.

func (*Header) HeaderGetDate

func (h *Header) HeaderGetDate() (time.Time, error)

HeaderGetDate parses and returns the date in the email. This will read the header named "Date". As this header is always required, it will return the time.Time zero value and an error if this method is called and no value is present. If the date header is present, it will returned the parsed value or an error if the date cannot be parsed.

func (*Header) HeaderGetMediaType

func (h *Header) HeaderGetMediaType(n string) (*MediaType, error)

HeaderGetMediaType retrieves a MediaType object for the named header. Returns an error if the given header cannot be parsed into a MediaType object. Returns nil and no error if the header is not set.

func (*Header) HeaderSetAddressList

func (h *Header) HeaderSetAddressList(n string, addrs addr.AddressList)

HeaderSetAddressList will update an address list header with the given address list.

func (*Header) HeaderSetContentDisposition

func (h *Header) HeaderSetContentDisposition(mt string) error

HeaderSetContentDisposition allows you to modify just the media-type of the Content-disposition header. The parameters will remain unchanged.

If the existing Content-disposition header cannot be parsed for some reason, setting this value will replace the entire value with this media-type.

func (*Header) HeaderSetContentDispositionFilename

func (h *Header) HeaderSetContentDispositionFilename(fn string) error

HeaderSetContentDispositionFilename updates the filename string set in the Content-disposition header.

If no Content-disposition header has been set yet or the value set cannot be parsed, this returns an error.

func (*Header) HeaderSetContentType

func (h *Header) HeaderSetContentType(mt string) error

HeaderSetContentType allows you to modify just the media-type of the Content-type header. The parameters will remain unchanged.

If the existing Content-type header cannot be parsed for some reason, setting this value will replace the entire value with this MIME-type.

func (*Header) HeaderSetContentTypeBoundary

func (h *Header) HeaderSetContentTypeBoundary(b string) error

HeaderSetContentTypeBoundary updates the boundary string set in the Content-type header.

If no Content-type header has been set yet or the value set cannot be parsed, this returns an error.

Beware that this does not update the boundaries used in any associated message body, so if there are existing boundaries, you need to update those separately.

func (*Header) HeaderSetContentTypeCharset

func (h *Header) HeaderSetContentTypeCharset(cs string) error

HeaderSetContentTypeCharset modifies the charset on the Content-type header.

If no Content-type header has been set yet or the value set cannot be parsed, this returns an error.

func (*Header) HeaderSetDate

func (h *Header) HeaderSetDate(d time.Time)

HeaderSetDate takes a time.Time input and sets the header field named "Date" to an RFC5322 formatted date from that input. This uses the built-in RFC1123Z format.

func (*Header) HeaderSetMediaType

func (h *Header) HeaderSetMediaType(n string, mt *MediaType) error

HeaderSetMediaType sets the named header to the given MediaType object.

type MediaType

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

MediaType represents a Content-type or Content-disposition header. This object is intended to be read-only.

func NewMediaType

func NewMediaType(mt string, ps ...string) (*MediaType, error)

NewMediaType creates a new media type from the given values. Returns nil and an error if an odd number of parameters is given.

func NewMediaTypeMap

func NewMediaTypeMap(mt string, ps map[string]string) *MediaType

NewMediaTypeMap creates a new media type from the given values, using a map argument rather than a list of paired strings.

func ParseMediaType

func ParseMediaType(v string) (*MediaType, error)

ParseMediaType parses a structured media type header using mime.ParseMediaType and returns a pointer to a MediaType object. It returns an error if there's a problem parsing it.

func (*MediaType) Boundary

func (mt *MediaType) Boundary() string

Boundary is a short name for

mt.Parameter("boundary")

This is useful for Content-type headers.

func (*MediaType) Charset

func (mt *MediaType) Charset() string

Charset is a short name for

mt.Parameter("charset")

This is useful for Content-type headers.

func (*MediaType) Filename

func (mt *MediaType) Filename() string

Filename is a short name for

mt.Parameter("filename")

This is useful for Content-disposition headers.

func (*MediaType) MediaType

func (mt *MediaType) MediaType() string

MediaType returns the media type word. For Content-Type headers, this is the MIME Type. For Content-Disposition headers, this will name the type for the content, such as "attachment" or "inline".

func (*MediaType) Parameter

func (mt *MediaType) Parameter(n string) string

Parameter returns the value of the named parameter or the empty string.

func (*MediaType) Parameters

func (mt *MediaType) Parameters() map[string]string

Parameters returns the map of structured parameters to the media type.

func (*MediaType) String

func (mt *MediaType) String() string

String returnes the formatted representation of the media type object.

func (*MediaType) Subtype

func (mt *MediaType) Subtype() string

Subtype returns the subtype of the media type. This is only relevant for the Content-type header. Returns an empty string if it doesn't appear to be a MIME type.

func (*MediaType) Type

func (mt *MediaType) Type() string

Type returns the type of the media type. This is only relevant for the Content-type header. Returns an empty string if it doesn't appear to be a MIME type.

type Message

type Message struct {
	Header         // MIME email header within
	email.Body     // basic message body within
	MaxDepth   int // maximum depth permitted for subparts during parsing

	Preamble []byte     // preamble before MIME parts
	Parts    []*Message // the MIME sub-parts
	Epilogue []byte     // epilogue after MIME parts
	// contains filtered or unexported fields
}

Message represents a MIME message.

func NewMessage

func NewMessage(boundary string) *Message

NewMessage will create a message with the selected boundary. The header and body will be empty, so it won't really be a legal message yet. This does not check to make sure the boundary is sane. You will also need to set this boundary on the Content-Type header if you want to actually have multiple parts. If you aren't going to have multiple parts, you can safely set the boundary to an empty string.

func Parse

func Parse(m []byte, o ...option) (*Message, error)

Parse parses the given bytes as an email message and returns the message object. As much of the message as can be parsed will be returned even if an error is returned.

Options may be passed to modify the construction and parsing of the object.

func (*Message) Bytes

func (m *Message) Bytes() []byte

Bytes outputs the messages as a slice of bytes.

func (*Message) ContentBinary

func (m *Message) ContentBinary() ([]byte, error)

ContentBinary is for retrieving a MIME single part body after having the transfer encoding decoded. No charset handling will be performed. If this is a multipart body, a nil slice is returned with a nil error. If an error occurs decoding the transfer encoding, a nil slice is returned with an error.

func (*Message) ContentTransferEncoding

func (m *Message) ContentTransferEncoding() string

ContentTransferEncoding is just an alias for:

cte := strings.ToLower(m.HeaderGet("Content-transfer-encoding"))

It returns the transfer encoding used for the message body. For MIME-compliant messages, this should always be one of the following values:

7bit
8bit
binary
base64
quoted-printable

Occassionally, you will see other oddball values, of course.

func (*Message) ContentUnicode

func (m *Message) ContentUnicode() (string, error)

ContentUnicode is for retrieving a MIME single part body after having the transfer encoding decoded and any charsets decoded into Go's native unicode handling. If the message is multipart, it returns an empty string with no error. If there is an error decoding the transfer encoding or converting to unicode, an empty string is returned with an error.

func (*Message) DecodeHeader

func (m *Message) DecodeHeader() error

DecodeHeader scans through the headers and looks for MIME word encoded field values. When they are found, these are decoded into native unicode.

func (*Message) FillParts

func (m *Message) FillParts() error

FillParts performs the work of parsing the message body into preamble, sub-parts, and epilogue.

func (*Message) HeaderContentDispositionFilename

func (m *Message) HeaderContentDispositionFilename() string

HeaderContentDispositionFilename is the filename set in the Content-disposition header, if set. Otherwise, it returns an empty string.

func (*Message) InsertPart

func (m *Message) InsertPart(ix int, p *Message)

InsertPart will attach a MIME message to this message at the specified point. Inserting at 0 will make it the first part. Using a negative index will make it the last. Using a value greater than or equal to the length of Parts will also insert it as the last.

func (*Message) SetContentBinary

func (m *Message) SetContentBinary(b []byte) error

SetContentBinary replaces the MIME message body with the given bytes. This method performs actions based on teh current state of the Content-transfer-encoding header. You must set that header as desired before calling this method.

The given bytes will be transformed according to Content-transfer-encoding header (if any).

If the body was previously a multipart message, this will also clear the Preamble, Parts, and Epilog.

This method returns ane error and won't make any changes to the message if an error occurs with the transfer encoding.

func (*Message) SetContentTransferEncoding

func (m *Message) SetContentTransferEncoding(cte string) error

SetContentTransferEncoding updates the transfer encoding for the message and then it will rewrite the content to adhere to the new encoding. It willr return an error if there's a problem decoding or re-encoding the content onthe way. If an error is returned, the Content-transfer-encoding will have remain at its original value.

func (*Message) SetContentUnicode

func (m *Message) SetContentUnicode(s string) error

SetContentUnicode replaces the MIME message body with the given unicode string. This method performs actions based on the current state of the Content-type and Content-transfer-encoding headers. You must set those as desired before calling this method.

The given string will be encoded from Go's native unicode into the destination charset, as specified by the Content-type header. After this, the Content-transfer-encoding will be applied to transform the body to that encoding (if any).

If the body was previously a multipart message, this will also clear the Preamble, Parts, and Epilogue.

This method returns an error and won't make any changes to the message if an error occurs either with the transfer encoding or the character set encoding.

func (*Message) String

func (m *Message) String() string

String outputs the message as a string.

func (*Message) UpdateBody

func (m *Message) UpdateBody() error

UpdateBody will reconstruct the basic message whenever the higher level elements are adjusted, preserving the original byte-for-byte as much as possible.

Whenever changes are made to the sub-parts, header, or other parts of the body, this method must be called prior to writing the message to output. This will recursively call UpdateBody on all sub-parts.

When this method is called, it will also check to see if the boundary between parts has changed. If so, it will update the boundaries between MIME parts. This can trigger an error if the boundary is missing or contains a space. In that case an error will be returned.

If an error occurs, the body will not have been updated. It is possible that some of the sub-parts will have had their body updated.

func (*Message) WalkParts

func (m *Message) WalkParts(pw PartWalker) error

WalkParts executes the given function for every part. This does a depth first descent into nested parts. If the given function returns an error, the descent will stop and exit with the error. This includes the top-level messages as well.

If you just want the leaf parts (the single/non-multipart MIME bits that are the usual containers of text, HTML, or attachements), you should try WalkSingleParts.

func (*Message) WalkSingleParts

func (m *Message) WalkSingleParts(pw PartWalker) error

WalkSingleParts performs the same operation as WalkParts, but it only executes the given PartWalker when the part is a leaf, i.e., a single part with no sub-parts, i.e., not a multipart part. Got it? The parts are walked in the same order they appear in the message. If this is a simple message with now parts, the main message itself will be the part you see.

As with WalkSingleParts, this will exit the function if the PartWalker returns an error without continuing the traversal.

Example
package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/zostay/go-email/pkg/email/mime"
)

var fileCount = 0

func isUnsafeExt(c rune) bool {
	return !unicode.IsLetter(c) && !unicode.IsDigit(c)
}

func outputSafeFilename(fn string) string {
	safeExt := filepath.Ext(fn)
	if strings.IndexFunc(safeExt, isUnsafeExt) > -1 {
		safeExt = ".wasnotsafe" // CHECK INPUT YOU CRAZY PERSON
	}
	fileCount++
	return fmt.Sprintf("%d%s", fileCount, safeExt)
}

func saveAttachment(_, _ int, m *mime.Message) error {
	if fn := m.HeaderContentDispositionFilename(); fn != "" {
		of := outputSafeFilename(fn)
		b, _ := m.ContentUnicode()
		_ = ioutil.WriteFile(of, []byte(b), 0644)
	}
	return nil
}

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

	m, err := mime.Parse(msg)
	if err != nil {
		panic(err)
	}

	_ = m.WalkSingleParts(saveAttachment)
}
Output:

type ParseError

type ParseError struct {
	Errs []error // the list of errors that occurred during parsing
}

ParseError is returned when one or more errors occur while parsing an email message. It collects all the errors and returns them as a group.

func (*ParseError) Error

func (err *ParseError) Error() string

Error returns the list of errors encounted while parsing an email message.

type PartWalker

type PartWalker func(depth, i int, part *Message) error

PartWalker is the callback used to iterate through parts in WalkParts and WalkSingleParts.

The first argument is the depth level of the part being passed, which can be helpful at understanding where you are in the document. The top-level message is depth 0, sub-parts of it is depth 1, sub-parts of those are level 2, etc.

The second argument is the index within that level. At level 0 there will only ever be a single index, 0. Sub-parts are indexed starting from 0.

The third argument is a pointer to the Message found.

The function should return an error to immediately stop the process of iteration.

type TransferDecoder

type TransferDecoder struct {
	To   TransferDecoderFunc // write unencoded bytes out in encoded form
	From TransferDecoderFunc // write encoded bytes out in unencoded form
}

TransferDecoder represents the pair of decoders for use when encoding/decoding the transfer encoding used for the body of a MIME message.

func SelectTransferDecoder

func SelectTransferDecoder(cte string) (TransferDecoder, error)

SelectTransferDecoder returns a transfer decoder to use for the given Content-Transfer-Encoding header value. Falls back to a basic as-is encoder if no encoding matches.

type TransferDecoderFunc

type TransferDecoderFunc func([]byte) ([]byte, error)

TransferDecoderFunc represents a function that writes a MIME message body according to a given Content-Transfer-Encoding.

Jump to

Keyboard shortcuts

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