An open API service indexing awesome lists of open source software.

https://github.com/rorycl/letters

Email parsing for go. This fork of github.com/mnako/letters focuses on modularity and speed
https://github.com/rorycl/letters

email golang mime parse parser rfc2045 rfc5322

Last synced: 5 months ago
JSON representation

Email parsing for go. This fork of github.com/mnako/letters focuses on modularity and speed

Awesome Lists containing this project

README

          

# Letters
Email parsing for go.

v0.1.3 : 01 May 2025 : allow Parser to be shared.

This is a fork of [mnako/letters](https://github.com/mnako/letters), a
minimalistic Golang library for parsing plain and MIME emails. Thanks to
@mnako and contributors, letters has great support for languages other
than English, text and transfer encodings.

This fork (at [#9d1a5, v0.2.3](https://github.com/mnako/letters/commit/db1b793c8119d8ed3d575f44df4391262829d1a5))
of letters focuses on extensibility through modularisation as set out in
[PR #124](https://github.com/mnako/letters/pull/124). This library also
provides improved performance and customisation through user-defined
funcs, for example for efficient inline and attachment file processing.

Future plans include making parsing errors, such as for invalid email
addresses, optionally non-fatal.

See [mailboxoperator](https://github.com/rorycl/mailboxoperator) for a
convenient way of iterating over emails in mailbox or maildir format. An
example client for mailboxoperator is
[mailfinder](https://github.com/rorycl/mailfinder), which also uses this
module.

## Quickstart

Install

```
go get github.com/rorycl/letters@latest
```

Parse an email:

```go
reader := someEmailIOReaderProvider() // see mailboxoperator
p := letters.NewParser()
parsedEmail, err := p.Parse(reader)

// &email.Email{
// Headers: email.Headers{
// Date: time.Time(time.Date(2019, 4, 1, 0, 55, 0, 0, time.UTC)),
// Sender: &mail.Address{
// Name: "อลิซ ผู้ส่งจดหมาย",
// Address: "alis.phusngcdhmay@example.com",
// },
// From: []*mail.Address{
// &mail.Address{
// Name: "อลิซ ผู้ส่งจดหมาย",
// Address: "alis.phusngcdhmay@example.com",
// },
// },
// To: []*mail.Address{
// &mail.Address{
// Name: "บ๊อบ ผู้รับ",
// Address: "bob.phurab@example.com",
// },
// },
// Cc: []*mail.Address{
// &mail.Address{
// Name: "แดน ผู้รับ",
// Address: "dan.phurab@example.com",
// },
// },
// MessageID: "Message-Id-1@example.com",
// InReplyTo: []string{
// "Message-Id-0@example.com",
// },
// References: []string{
// "Message-Id-0@example.com",
// },
// Subject: "📧 Test แพนแกรมภาษาไทย",
// Comments: "Message Header Comment",
// Keywords: []string{
// "Keyword 1",
// "Keyword 2",
// },
// ResentDate: time.Time(time.Date(2019, 4, 1, 0, 55, 0, 0, time.UTC)),
// ExtraHeaders: map[string][]string{
// "X-Clacks-Overhead": []string{
// "GNU Terry Pratchett",
// },
// },
// ContentInfo: &email.ContentInfo{
// Type: "multipart/mixed",
// TypeParams: map[string]string{
// "boundary": "MixedBoundaryString",
// "charset": "tis-620",
// },
// Disposition: "",
// DispositionParams: map[string]string(nil), // p0
// TransferEncoding: "base64",
// ID: "",
// Charset: "tis-620",
// },
// Received: nil,
// },
// Text: ""
// EnrichedText: "เป็นมนุษย์สุดประเสริฐเลิศคุณค่า ..."
// HTML: ""
// Files: []*email.File{
// &email.File{
// FileType: "inline",
// Name: "inline-jpg-image-without-disposition.jpg",
// ContentInfo: &email.ContentInfo{
// Type: "image/jpeg",
// TypeParams: map[string]string{
// "name": "inline-jpg-image-without-disposition.jpg",
// },
// Disposition: "",
// DispositionParams: map[string]string(nil), // p0
// TransferEncoding: "base64",
// ID: "",
// Charset: "tis-620",
// },
// Data: []byte{
// 239, 191, 189, 224, 184, 184, 239, 191, 189, ...
// },
// },
// },
// &email.File{
// FileType: "attachment",
// Name: "attached-pdf-filename.pdf",
// ContentInfo: &email.ContentInfo{
// Type: "application/pdf",
// TypeParams: map[string]string{
// "name": "attached-pdf-name.pdf",
// },
// Disposition: "attachment",
// DispositionParams: map[string]string{
// "filename": "attached-pdf-filename.pdf",
// },
// TransferEncoding: "base64",
// ID: "",
// Charset: "tis-620",
// },
// Data: []byte{
// 37, 80, 68, 70, 45, 49, 46, 13, 116, 114, 97, ...
// },
// },
// }
```

## Options

Various options are provided for customising the Parser, including:

```go
// skip content types
func WithSkipContentTypes(skipContentTypes []string) Opt
// provide a custom address processing function
func WithCustomAddressFunc(af func(string) (*mail.Address, error)) Opt
// provide a custom processing function for string lists of addresses
func WithCustomAddressesFunc(af func(list string) ([]*mail.Address, error)) Opt
// provide a custom date processing function
func WithCustomDateFunc(df func(string) (time.Time, error)) Opt
// provide a custom file processing function
func WithCustomFileFunc(ff func(*email.File) error) Opt
// save files to the stated directory (an example of WithCustomFileFunc)
func WithSaveFilesToDirectory(dir string) Opt
// only process headers
func WithHeadersOnly() Opt
// skip processing attachments
func WithoutAttachments() Opt
// show verbose processing info (currently a noop)
func WithVerbose() Opt
```

More than one option can be supplied.

The `WithoutAttachments` and `WithHeadersOnly` options determine if only part
of an email will be processed.

The `WithSkipContentTypes` allows the user to skip processing MIME
message parts with the supplied content-types.

The date and address "With" options allow the provision of custom funcs to
override the `net/mail` funcs normally used. For example it might be necessary
to extend the date parsing capabilities to deal with poorly formatted date
strings produced by older SMTP servers.

The `WithCustomFileFunc` allows the provision of a custom func for saving,
filtering and/or processing of inline or attached files without reading them
first into an `email.File.Data` []byte slice first, which is the default
behaviour. The `WithSaveFilesToDirectory` option is an example of such a custom
func.

As shown in the [parser/optspkg_test.go](parser/optspkg_test.go) package
test, `WithCustomFileFunc` can be used to, for example, only process
`image/jpeg` files. More examples are shown in
[parser/opts_test.go](parser/opts_test.go), for example:

```go
opt := parser.WithHeadersOnly() // the headers only option
p := letters.NewParser(opt, parser.WithVerbose()) // options can be chained
parsedEmail, err := p.Parse(emailReader)
if err != nil {
return fmt.Errorf("error while parsing email headers: %s", err)
}
```