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 ¶
- Constants
- Variables
- func CharsetDecoderToCharsetReader(decode func(string, []byte) (string, error)) func(string, io.Reader) (io.Reader, error)
- func DecodeAsIs(b []byte) ([]byte, error)
- func DecodeFromBase64(b []byte) ([]byte, error)
- func DecodeFromQuotedPrintable(b []byte) ([]byte, error)
- func DecodeToBase64(b []byte) ([]byte, error)
- func DecodeToQuotedPrintable(b []byte) ([]byte, error)
- func DefaultCharsetDecoder(charset string, b []byte) (string, error)
- func DefaultCharsetEncoder(charset, s string) ([]byte, error)
- func WithMaxDepth(d int) option
- type Decoder
- type Encoder
- type Header
- func (h *Header) HeaderContentDisposition() string
- func (h *Header) HeaderContentType() string
- func (h *Header) HeaderContentTypeBoundary() string
- func (h *Header) HeaderContentTypeCharset() string
- func (h *Header) HeaderContentTypeSubtype() string
- func (h *Header) HeaderContentTypeType() string
- func (h *Header) HeaderGetAddressList(n string) (addr.AddressList, error)
- func (h *Header) HeaderGetAllAddressLists(n string) (addr.AddressList, error)
- func (h *Header) HeaderGetDate() (time.Time, error)
- func (h *Header) HeaderGetMediaType(n string) (*MediaType, error)
- func (h *Header) HeaderSetAddressList(n string, addrs addr.AddressList)
- func (h *Header) HeaderSetContentDisposition(mt string) error
- func (h *Header) HeaderSetContentDispositionFilename(fn string) error
- func (h *Header) HeaderSetContentType(mt string) error
- func (h *Header) HeaderSetContentTypeBoundary(b string) error
- func (h *Header) HeaderSetContentTypeCharset(cs string) error
- func (h *Header) HeaderSetDate(d time.Time)
- func (h *Header) HeaderSetMediaType(n string, mt *MediaType) error
- type MediaType
- func (mt *MediaType) Boundary() string
- func (mt *MediaType) Charset() string
- func (mt *MediaType) Filename() string
- func (mt *MediaType) MediaType() string
- func (mt *MediaType) Parameter(n string) string
- func (mt *MediaType) Parameters() map[string]string
- func (mt *MediaType) String() string
- func (mt *MediaType) Subtype() string
- func (mt *MediaType) Type() string
- type Message
- func (m *Message) Bytes() []byte
- func (m *Message) ContentBinary() ([]byte, error)
- func (m *Message) ContentTransferEncoding() string
- func (m *Message) ContentUnicode() (string, error)
- func (m *Message) DecodeHeader() error
- func (m *Message) FillParts() error
- func (m *Message) HeaderContentDispositionFilename() string
- func (m *Message) InsertPart(ix int, p *Message)
- func (m *Message) SetContentBinary(b []byte) error
- func (m *Message) SetContentTransferEncoding(cte string) error
- func (m *Message) SetContentUnicode(s string) error
- func (m *Message) String() string
- func (m *Message) UpdateBody() error
- func (m *Message) WalkParts(pw PartWalker) error
- func (m *Message) WalkSingleParts(pw PartWalker) error
- type ParseError
- type PartWalker
- type TransferDecoder
- type TransferDecoderFunc
Examples ¶
Constants ¶
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" )
const ( // MaxMultipartDepth is the default depth the parser will recurse into a // message. DefaultMaxMultipartDepth = 10 )
Variables ¶
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 )
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 ¶
DecodeAsIs is the identity encoding used whenever no decoding is required for a particular transfer encoding.
func DecodeFromBase64 ¶
DecodeFromBase64 is used to output unencoded bytes from MIME BASE-64 encoded bytes.
func DecodeFromQuotedPrintable ¶
DecodeFromQuotedPrintable is used to output unencoded bytes from quoted-printable encoded bytes.
func DecodeToBase64 ¶
DecodeToBase64 is used to output MIME BASE-64 encoded bytes from unencoded bytes.
func DecodeToQuotedPrintable ¶
DecodeToQuotedPrintable is used to output quoted-printed encoded bytes from unencoded bytes.
func DefaultCharsetDecoder ¶
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 ¶
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 ¶
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 ¶
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 ¶
Header provides tools built on simple.Header to work with MIME headers.
func NewHeader ¶
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 ¶
HeaderContentDisposition is the value of the Content-dispotion header value.
func (*Header) HeaderContentType ¶
HeaderContentType retrieves only the full MIME type set in the Content-type header.
func (*Header) HeaderContentTypeBoundary ¶
HeaderContentTypeBoundary is the boundary set on the Content-type header for multipart messages.
func (*Header) HeaderContentTypeCharset ¶
HeaderContentTypeCharset retrieves the character set on the Content-type header or an empty string.
func (*Header) HeaderContentTypeSubtype ¶
HeaderContentTypeSubtype retrieves the second part, the subtype, of the MIME type set in the Content-type header.
func (*Header) HeaderContentTypeType ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
NewMediaTypeMap creates a new media type from the given values, using a map argument rather than a list of paired strings.
func ParseMediaType ¶
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 ¶
Boundary is a short name for
mt.Parameter("boundary")
This is useful for Content-type headers.
func (*MediaType) Charset ¶
Charset is a short name for
mt.Parameter("charset")
This is useful for Content-type headers.
func (*MediaType) Filename ¶
Filename is a short name for
mt.Parameter("filename")
This is useful for Content-disposition headers.
func (*MediaType) MediaType ¶
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 ¶
Parameter returns the value of the named parameter or the empty string.
func (*MediaType) Parameters ¶
Parameters returns the map of structured parameters to the media 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 ¶
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 ¶
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) ContentBinary ¶
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 ¶
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 ¶
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 ¶
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 ¶
FillParts performs the work of parsing the message body into preamble, sub-parts, and epilogue.
func (*Message) HeaderContentDispositionFilename ¶
HeaderContentDispositionFilename is the filename set in the Content-disposition header, if set. Otherwise, it returns an empty string.
func (*Message) InsertPart ¶
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 ¶
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 ¶
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 ¶
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) UpdateBody ¶
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 ¶
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 ¶
TransferDecoderFunc represents a function that writes a MIME message body according to a given Content-Transfer-Encoding.