Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebase branch for upstream changes through 1.0.2 #4

Closed
wants to merge 164 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
164 commits
Select commit Hold shift + click to select a range
6cbc57b
imap: rename NewSeqSet to ParseSeqSet
emersion Jun 7, 2017
fb92935
imap: rename NewBodySectionName to ParseBodySectionName, fixes #103
emersion Jun 7, 2017
2f65bf1
client: do not set channels to nil
emersion Jul 3, 2017
edb8332
client: make State and Mailbox getters
emersion Jul 4, 2017
57fba75
client: refactor basic tests
emersion Jul 8, 2017
f381780
client: refactor tests in any conn state
emersion Jul 8, 2017
34a1db0
client: refactoring tests when state is not authenticated
emersion Jul 8, 2017
4492538
client: refactor tests for authenticated state
emersion Jul 10, 2017
f467556
imap: add ParseString, fixes #127
emersion Jul 10, 2017
ee89083
Merge branch 'master' into client-tests
emersion Jul 10, 2017
1e86424
client: refactor tests for selected state
emersion Jul 10, 2017
3274c0f
Merge pull request #126 from emersion/client-tests
emersion Jul 10, 2017
0d0e01e
utf7: remove globals
emersion Jul 10, 2017
46edbe8
go fmt
emersion Jul 10, 2017
9a31eef
server: fix a data race in tests
emersion Jul 11, 2017
a81f6b0
client: change DialWithDialer{,TLS} doc wording, fixes #128
emersion Jul 12, 2017
5056a99
imap: Mime -> MIME, updates #102
emersion Jul 12, 2017
11ab44c
imap: Md5 -> MD5
emersion Jul 12, 2017
01105d8
server: Sasl -> SASL
emersion Jul 12, 2017
9c53323
client: redesigned response handling
emersion Jul 15, 2017
65f86d7
imap: rename ToNamedResp to ParseNamedResp
emersion Jul 15, 2017
dc58f30
Merge pull request #132 from emersion/client-resp
emersion Jul 15, 2017
b4d4b32
Replace various update channels with one unique channel
emersion Aug 15, 2017
de4d4d7
Block when sending to Client.Updates
emersion Aug 15, 2017
b4e3207
Recommend to use a separate goroutine when receiving from Client.Updates
emersion Aug 15, 2017
9e6d485
Add one type per update
emersion Aug 16, 2017
930b158
Update Client.Updates docs
emersion Aug 16, 2017
1cb024e
Merge pull request #137 from emersion/updates-chan
emersion Aug 16, 2017
395a1a3
server: close sending goroutine, fixes #139
emersion Sep 18, 2017
2ab3d85
Add PartSpecifier type
emersion Nov 6, 2017
f6ab790
imap: add FetchItem and StoreItem
emersion Nov 6, 2017
41db9ee
imap: add StatusItem
emersion Nov 7, 2017
2956d86
imap: remove private command constants
emersion Nov 7, 2017
424c957
imap: rename imap.StatusRespType constants
emersion Nov 7, 2017
711096f
imap: add StatusRespCode
emersion Nov 7, 2017
b609e4d
client: spec the ErrorLog to be safe to use from multiple goroutines
emersion Nov 7, 2017
b666f6d
imap: simplify Conn.Flush
emersion Nov 8, 2017
6049a33
client: add Client.SetState for extensions
emersion Nov 13, 2017
939ec39
utf7: explicitly set Encoding type to improve docs
emersion Nov 13, 2017
2c38908
Fix atoms containing forbidden chars
emersion Nov 30, 2017
4b27951
Call Handle and Upgrade with Conn, not *conn
emersion Dec 1, 2017
c2bacee
all: some simplification
ferhatelmas Dec 1, 2017
9e78220
Merge pull request #155 from ferhatelmas/simplify
emersion Dec 1, 2017
52a3756
Change Message.GetBody to accept a BodySectionName instead of a Fetch…
emersion Dec 7, 2017
b57ab5f
Merge branch 'v1' of github.com:emersion/go-imap into v1
emersion Dec 7, 2017
c798831
client: add Store example
emersion Dec 21, 2017
75afcb5
Do not print an error on unhandled response
emersion Dec 26, 2017
002ea0b
all: gofmt
ferhatelmas Jan 2, 2018
54b9f1d
Merge pull request #161 from ferhatelmas/gofmt
emersion Jan 2, 2018
5ec3173
read: fix crlf reading
ferhatelmas Jan 3, 2018
defba89
travis: use Go 1.9
emersion Jan 4, 2018
7299886
Merge branch 'v1' of github.com:emersion/go-imap into v1
emersion Jan 4, 2018
b50d955
Merge branch 'v1' into fix-crlf
emersion Jan 4, 2018
f33b00d
Merge pull request #162 from ferhatelmas/fix-crlf
emersion Jan 4, 2018
f2f093b
client: fix couple of formatting issues in tests
ferhatelmas Jan 5, 2018
6a14e56
Merge pull request #164 from ferhatelmas/formatting-issues-in-tests
emersion Jan 5, 2018
74aa5b9
travis: make docker based, instead of vm
ferhatelmas Jan 5, 2018
e6e3e8c
Merge pull request #165 from ferhatelmas/better-travis
emersion Jan 5, 2018
69665df
backend: improve Update
emersion Jan 14, 2018
7a88e0a
Merge branch 'v1' of github.com:emersion/go-imap into v1
emersion Jan 14, 2018
9e7e442
client: more strongly typed server updates
emersion Jan 14, 2018
658014e
added Clieint.Terminate()
Feb 16, 2018
db2527e
Merge pull request #174 from cseeger-epages/bug/issue-173-client-close
emersion Feb 16, 2018
b63c7c7
readme: remove Gitter chat
emersion Mar 19, 2018
dd43b8d
add test for login state
cuonglm May 25, 2018
84e8414
fix wrong login state condition
cuonglm May 25, 2018
e402a38
Merge pull request #184 from Gnouc/v1
emersion May 25, 2018
9cf8bad
Client
trueone Sep 28, 2018
2168c0b
Client: done buffer changed to 2
trueone Sep 28, 2018
f966f79
Merge pull request #198 from trueone/v1
emersion Sep 28, 2018
d328c75
backendutil: skip part header when using an index
crawshaw Oct 12, 2018
bcb1b06
backendutil: read body section parts left-to-right
crawshaw Oct 12, 2018
0f2c384
imap: quote parameter values
crawshaw Oct 12, 2018
36e8cd6
Merge pull request #199 from crawshaw/pr
emersion Oct 15, 2018
60dd8e9
Merge pull request #200 from crawshaw/pr2
emersion Oct 15, 2018
a4eaf4a
Add an example for Client.Search() usage
pabluk Oct 17, 2018
9747fff
Merge pull request #204 from pabluk/search-example
emersion Oct 18, 2018
744a287
Add go.mod
emersion Dec 14, 2018
5624370
ci: simplify build, use Go 1.10
emersion Dec 14, 2018
76534ea
readme: remove rant
emersion Jan 10, 2019
35e4e8b
Merge pull request #202 from crawshaw/pr3
emersion Jan 14, 2019
2a172f3
Fix function comments based on best practices from Effective Go
CodeLingoBot Feb 28, 2019
347e1d8
Accept LF as command separator instead of only CRLF
foxcpp Mar 1, 2019
1101813
Fix client data race.
Neopallium Mar 3, 2019
e0b3ebb
Fix data race with server continuation requests
Neopallium Mar 3, 2019
8244a93
Fix data races in Upgrade and SetDebug.
Neopallium Mar 6, 2019
c42a03c
Fix debug writer data race.
Neopallium Mar 6, 2019
ba2a706
Client - Detect 'connection reset by peer' errors.
Neopallium Mar 6, 2019
1876f71
Use WaitGroups instead of channels in Waiter.
Neopallium Mar 6, 2019
6028838
Cleanup greeting handling code.
Neopallium Mar 6, 2019
50b10f0
Fix data race in `client.(*Client).Authenticate()`
Neopallium Mar 6, 2019
76ca910
AuthReplyFunc - return `error`
Neopallium Mar 6, 2019
b06b466
Change auth replies from `string` to `[]byte`
Neopallium Mar 6, 2019
0e7ff7e
readme: add go-imap-sortthread
emersion Mar 31, 2019
b4a6ac9
commands: fix Fetch BODY[0] formatted as a string
emersion Mar 31, 2019
1f4311a
all: fix some lint issues
ferhatelmas Apr 7, 2019
3d1671d
Always close ch chan *imap.Message upon fetch
wucdbm Mar 31, 2019
d9e4a14
test fetch closes output channel when state is not SelectedState
wucdbm Mar 31, 2019
d834e22
readme: go-dkim has migrated to go-msgauth
emersion Apr 14, 2019
b7db4a2
ci: migrate to sr.ht
emersion Apr 14, 2019
e658bb1
Pass basic connection information to backend
foxcpp Apr 6, 2019
39617eb
Handle HEADER.FIELDS in backendutil.FetchBodySection (#240)
foxcpp May 4, 2019
4d5af3d
backendutil: Merge Match* functions (#241)
foxcpp May 4, 2019
652a96c
backendutil: Update to go-message 0.10 (#245)
foxcpp May 15, 2019
fba45c5
client: expose handler replies to third-parties
emersion May 19, 2019
83a1a4e
client: add a note about concurrency
emersion May 19, 2019
58ca724
Handle "STORE FLAGS seqset flag1 flag2" syntax
foxcpp May 21, 2019
05c0e77
Change order of defer statements in tests
foxcpp May 21, 2019
dabec6b
Check literal length
foxcpp May 21, 2019
c87c2e4
Use io.CopyN instead of io.Copy
foxcpp May 21, 2019
d64fc14
Use less restrictve Dialer interface in DialWithDialer (#249)
foxcpp May 21, 2019
41c815f
go fmt
emersion May 21, 2019
90e8f9c
client: prepopulate tls.Config.ServerName
emersion May 21, 2019
704298c
backendutil: Compare header field names in case-insensitive way
foxcpp May 24, 2019
e201086
Server-side (reader) support for non-synchronizing literals (#258)
foxcpp Jun 1, 2019
f26a148
backendutil: Use textproto directly (#256)
foxcpp Jun 2, 2019
ca8a17c
Update dependencies
emersion Jun 4, 2019
5511b75
Explicit separation for quoted strings and atoms (#261)
foxcpp Jun 6, 2019
c64d40c
server: Fix quoted capabilities in post-auth response
foxcpp Jun 6, 2019
d2e6783
Make MIMEType and MIMESubType string comparisons case insensitive (#237)
Jun 6, 2019
598f353
server: Add per-connection panic handler (#264)
foxcpp Jun 6, 2019
2bc4a8c
Revert "backendutil: Use textproto directly for Match"
foxcpp Jun 8, 2019
e1fe0ad
Client & server support for SASL-IR extension (#262)
foxcpp Jun 8, 2019
c5d9e14
client: add test case for failed APPEND
emersion Jul 12, 2017
00bb006
client: cancel pending literal writes after status response
emersion Jun 8, 2019
4ab9033
backendutil: Set Sender and Reply-To to From if empty
foxcpp Jun 9, 2019
39bdc54
backendutil: Decode headers in Match before comparsion
foxcpp Jun 9, 2019
1bdc3f8
backendutil: Still try to match if field decoding fails
foxcpp Jun 9, 2019
679123b
client: Use non-synchronizing literals when length is small (<4096) (…
foxcpp Jun 10, 2019
64d5d9a
backendutil: Handle BODY[1] for non-multipart messages
foxcpp Jun 11, 2019
853d87d
readme: drop go report card badge
emersion Jun 13, 2019
bd3fecb
Add go.sum
foxcpp Jun 18, 2019
8c4bfb4
readme: remove stability badge
emersion Jun 29, 2019
f7d07be
Don't send CAPABILITY statement unsolicited after
MFAshby Jul 6, 2019
918a14a
use correct header for content encoding
danielkucera Jul 31, 2019
09c1d69
Add basic comment stripping for date-time
quite Aug 15, 2019
ef6a293
fix detection of multipart in different case
danielkucera Aug 18, 2019
3a0a93d
client: add example for Client.Append
emersion Aug 29, 2019
4642503
Make SEARCH charset a RawString
Sep 15, 2019
7aeb464
commands: Properly handle EOF in Authenticate.Handle
foxcpp Oct 9, 2019
cfc6c58
Fix search larger and smaller edge case
drauschenbach Oct 3, 2019
63cf5f3
Provide a useful error message if written literal is smaller than exp…
foxcpp Dec 1, 2019
c8e5db3
client: Close the connection on mismatched literal length
foxcpp Dec 1, 2019
3021baf
Update dependencies
emersion Dec 6, 2019
e7ff51b
Fix issue with backendutils.UpdateFlags().
Neopallium Oct 1, 2019
dba5451
Server - allow single flag in STORE command.
Neopallium Oct 3, 2019
8e9ef4f
Server - Fix deadlocks when client connection closes early.
Neopallium Oct 4, 2019
4de1c25
Add tests for Recent flag.
Neopallium Oct 20, 2019
944dab8
Backendutils - Improve tests for UpdateFlags.
Neopallium Oct 20, 2019
12c89d0
Test FETCH with no mailbox selected.
Neopallium Oct 20, 2019
c2b755f
Test NOOP on selected mailbox.
Neopallium Oct 20, 2019
0b43785
Introduce BodyStructure.Filename
emersion Dec 8, 2019
05df69d
Improve BodyStructure docs
emersion Dec 8, 2019
6388a8c
Add Address.Address helper
emersion Dec 8, 2019
b051681
Simplify BodyStructure.Filename
emersion Dec 13, 2019
f1c9459
Introduce BodyStructure.Walk
emersion Dec 13, 2019
414e9a7
Add TryCreateFlag
emersion Dec 17, 2019
4b1f461
Fix SearchCriteria formatting of keywords flags.
Adirelle Dec 17, 2019
cce832e
Test STORE with keyword flags.
Adirelle Dec 17, 2019
55c8f18
Test APPEND with keyword flag.
Adirelle Dec 17, 2019
c79bafa
Search for ALL messages by default (#330)
arnisoph Jan 5, 2020
1207b14
add Push and Pop to memory backend
obukhov-sergey Apr 4, 2019
e952031
make Push accept uid
obukhov-sergey Apr 5, 2019
d6097b6
Moving to use go mod
aerwin3 Jul 12, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
image: alpine/edge
packages:
- go
# Required by codecov
- bash
- findutils
sources:
- https://github.com/emersion/go-imap
tasks:
- build: |
cd go-imap
go build -v ./...
- test: |
cd go-imap
go test -coverprofile=coverage.txt -covermode=atomic ./...
- upload-coverage: |
cd go-imap
export CODECOV_TOKEN=8c0f7014-fcfa-4ed9-8972-542eb5958fb3
curl -s https://codecov.io/bash | bash
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

65 changes: 28 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,34 @@
# go-imap

[![GoDoc](https://godoc.org/github.com/emersion/go-imap?status.svg)](https://godoc.org/github.com/emersion/go-imap)
[![GoDoc](https://godoc.org/github.com/mailgun/go-imap?status.svg)](https://godoc.org/github.com/mailgun/go-imap)
[![Build Status](https://travis-ci.org/emersion/go-imap.svg?branch=master)](https://travis-ci.org/emersion/go-imap)
[![Codecov](https://codecov.io/gh/emersion/go-imap/branch/master/graph/badge.svg)](https://codecov.io/gh/emersion/go-imap)
[![Go Report
Card](https://goreportcard.com/badge/github.com/emersion/go-imap)](https://goreportcard.com/report/github.com/emersion/go-imap)
Card](https://goreportcard.com/badge/github.com/mailgun/go-imap)](https://goreportcard.com/report/github.com/mailgun/go-imap)
[![Unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)](https://github.com/emersion/stability-badges#unstable)
[![Gitter chat](https://badges.gitter.im/goimap/Lobby.svg)](https://gitter.im/goimap/Lobby)
>>>>>>> cf144a2... Moving to use go mod

An [IMAP4rev1](https://tools.ietf.org/html/rfc3501) library written in Go. It
can be used to build a client and/or a server.

```bash
go get github.com/emersion/go-imap/...
go get github.com/mailgun/go-imap/...
>>>>>>> cf144a2... Moving to use go mod
```

## Why?

Other IMAP implementations in Go:
* Require to make [many type assertions or conversions](https://github.com/emersion/neutron/blob/ca635850e2223d6cfe818664ef901fa6e3c1d859/backend/imap/util.go#L110)
* Are not idiomatic or are [ugly](https://github.com/jordwest/imap-server/blob/master/conn/commands.go#L53)
* Are [not pleasant to use](https://github.com/emersion/neutron/blob/ca635850e2223d6cfe818664ef901fa6e3c1d859/backend/imap/messages.go#L228)
* Implement a server _xor_ a client, not both
* Don't implement unilateral updates (i.e. the server can't notify clients for
new messages)
* Do not have a good test coverage
* Don't handle encoding and charset automatically

## Usage

### Client [![GoDoc](https://godoc.org/github.com/emersion/go-imap/client?status.svg)](https://godoc.org/github.com/emersion/go-imap/client)
### Client [![GoDoc](https://godoc.org/github.com/mailgun/go-imap/client?status.svg)](https://godoc.org/github.com/mailgun/go-imap/client)

```go
package main

import (
"log"

"github.com/emersion/go-imap/client"
"github.com/emersion/go-imap"
"github.com/mailgun/go-imap/client"
"github.com/mailgun/go-imap"
)

func main() {
Expand Down Expand Up @@ -96,7 +86,7 @@ func main() {
messages := make(chan *imap.Message, 10)
done = make(chan error, 1)
go func() {
done <- c.Fetch(seqset, []string{imap.EnvelopeMsgAttr}, messages)
done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)
}()

log.Println("Last 4 messages:")
Expand All @@ -112,16 +102,16 @@ func main() {
}
```

### Server [![GoDoc](https://godoc.org/github.com/emersion/go-imap/server?status.svg)](https://godoc.org/github.com/emersion/go-imap/server)
### Server [![GoDoc](https://godoc.org/github.com/mailgun/go-imap/server?status.svg)](https://godoc.org/github.com/mailgun/go-imap/server)

```go
package main

import (
"log"

"github.com/emersion/go-imap/server"
"github.com/emersion/go-imap/backend/memory"
"github.com/mailgun/go-imap/server"
"github.com/mailgun/go-imap/backend/memory"
)

func main() {
Expand Down Expand Up @@ -149,34 +139,35 @@ You can now use `telnet localhost 1143` to manually connect to the server.
### Extensions

Commands defined in IMAP extensions are available in other packages. See [the
wiki](https://github.com/emersion/go-imap/wiki/Using-extensions#using-client-extensions)
wiki](https://github.com/mailgun/go-imap/wiki/Using-extensions#using-client-extensions)
to learn how to use them.

* [APPENDLIMIT](https://github.com/emersion/go-imap-appendlimit)
* [COMPRESS](https://github.com/emersion/go-imap-compress)
* [ENABLE](https://github.com/emersion/go-imap-enable)
* [APPENDLIMIT](https://github.com/mailgun/go-imap-appendlimit)
* [COMPRESS](https://github.com/mailgun/go-imap-compress)
* [ENABLE](https://github.com/mailgun/go-imap-enable)
* [ID](https://github.com/ProtonMail/go-imap-id)
* [IDLE](https://github.com/emersion/go-imap-idle)
* [MOVE](https://github.com/emersion/go-imap-move)
* [QUOTA](https://github.com/emersion/go-imap-quota)
* [SPECIAL-USE](https://github.com/emersion/go-imap-specialuse)
* [UNSELECT](https://github.com/emersion/go-imap-unselect)
* [UIDPLUS](https://github.com/emersion/go-imap-uidplus)
* [IDLE](https://github.com/mailgun/go-imap-idle)
* [MOVE](https://github.com/mailgun/go-imap-move)
* [QUOTA](https://github.com/mailgun/go-imap-quota)
* [SORT and THREAD](https://github.com/mailgun/go-imap-sortthread)
* [SPECIAL-USE](https://github.com/mailgun/go-imap-specialuse)
* [UNSELECT](https://github.com/mailgun/go-imap-unselect)
* [UIDPLUS](https://github.com/mailgun/go-imap-uidplus)

### Server backends

* [Memory](https://github.com/emersion/go-imap/tree/master/backend/memory) (for testing)
* [Multi](https://github.com/emersion/go-imap-multi)
* [PGP](https://github.com/emersion/go-imap-pgp)
* [Proxy](https://github.com/emersion/go-imap-proxy)
* [Memory](https://github.com/mailgun/go-imap/tree/master/backend/memory) (for testing)
* [Multi](https://github.com/mailgun/go-imap-multi)
* [PGP](https://github.com/mailgun/go-imap-pgp)
* [Proxy](https://github.com/mailgun/go-imap-proxy)

### Related projects

* [go-message](https://github.com/emersion/go-message) - parsing and formatting MIME and mail messages
* [go-msgauth](https://github.com/emersion/go-msgauth) - handle DKIM, DMARC and Authentication-Results
* [go-pgpmail](https://github.com/emersion/go-pgpmail) - decrypting and encrypting mails with OpenPGP
* [go-sasl](https://github.com/emersion/go-sasl) - sending and receiving SASL authentications
* [go-smtp](https://github.com/emersion/go-smtp) - building SMTP clients and servers
* [go-dkim](https://github.com/emersion/go-dkim) - creating and verifying DKIM signatures

## License

Expand Down
8 changes: 6 additions & 2 deletions backend/backend.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Package backend defines an IMAP server backend interface.
package backend

import "errors"
import (
"errors"

"github.com/mailgun/go-imap"
)

// ErrInvalidCredentials is returned by Backend.Login when a username or a
// password is incorrect.
Expand All @@ -12,5 +16,5 @@ var ErrInvalidCredentials = errors.New("Invalid credentials")
type Backend interface {
// Login authenticates a user. If the username or the password is incorrect,
// it returns ErrInvalidCredentials.
Login(username, password string) (User, error)
Login(connInfo *imap.ConnInfo, username, password string) (User, error)
}
40 changes: 40 additions & 0 deletions backend/backendutil/backendutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,55 @@ var testDate, _ = time.Parse(time.RFC1123Z, "Sat, 18 Jun 2016 12:00:00 +0900")

const testHeaderString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" +
"Date: Sat, 18 Jun 2016 12:00:00 +0900\r\n" +
"Date: Sat, 19 Jun 2016 12:00:00 +0900\r\n" +
"From: Mitsuha Miyamizu <mitsuha.miyamizu@example.org>\r\n" +
"Reply-To: Mitsuha Miyamizu <mitsuha.miyamizu+replyto@example.org>\r\n" +
"Message-Id: 42@example.org\r\n" +
"Subject: Your Name.\r\n" +
"To: Taki Tachibana <taki.tachibana@example.org>\r\n" +
"\r\n"

const testHeaderFromToString = "From: Mitsuha Miyamizu <mitsuha.miyamizu@example.org>\r\n" +
"To: Taki Tachibana <taki.tachibana@example.org>\r\n" +
"\r\n"

const testHeaderDateString = "Date: Sat, 18 Jun 2016 12:00:00 +0900\r\n" +
"Date: Sat, 19 Jun 2016 12:00:00 +0900\r\n" +
"\r\n"

const testHeaderNoFromToString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" +
"Date: Sat, 18 Jun 2016 12:00:00 +0900\r\n" +
"Date: Sat, 19 Jun 2016 12:00:00 +0900\r\n" +
"Reply-To: Mitsuha Miyamizu <mitsuha.miyamizu+replyto@example.org>\r\n" +
"Message-Id: 42@example.org\r\n" +
"Subject: Your Name.\r\n" +
"\r\n"

const testAltHeaderString = "Content-Type: multipart/alternative; boundary=b2\r\n" +
"\r\n"

const testTextHeaderString = "Content-Disposition: inline\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n"

const testTextContentTypeString = "Content-Type: text/plain\r\n" +
"\r\n"

const testTextNoContentTypeString = "Content-Disposition: inline\r\n" +
"\r\n"

const testTextBodyString = "What's your name?"

const testTextString = testTextHeaderString + testTextBodyString

const testHTMLHeaderString = "Content-Disposition: inline\r\n" +
"Content-Type: text/html\r\n" +
"\r\n"

const testHTMLBodyString = "<div>What's <i>your</i> name?</div>"

const testHTMLString = testHTMLHeaderString + testHTMLBodyString

const testAttachmentHeaderString = "Content-Disposition: attachment; filename=note.txt\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n"
Expand All @@ -31,7 +66,12 @@ const testAttachmentBodyString = "My name is Mitsuha."
const testAttachmentString = testAttachmentHeaderString + testAttachmentBodyString

const testBodyString = "--message-boundary\r\n" +
testAltHeaderString +
"\r\n--b2\r\n" +
testTextString +
"\r\n--b2\r\n" +
testHTMLString +
"\r\n--b2--\r\n" +
"\r\n--message-boundary\r\n" +
testAttachmentString +
"\r\n--message-boundary--\r\n"
Expand Down
75 changes: 64 additions & 11 deletions backend/backendutil/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,43 @@ import (
"bytes"
"errors"
"io"
"mime"
nettextproto "net/textproto"
"strings"

"github.com/emersion/go-imap"
"github.com/emersion/go-message"
"github.com/emersion/go-message/textproto"
"github.com/mailgun/go-imap"
)

var errNoSuchPart = errors.New("backendutil: no such message body part")

func multipartReader(header textproto.Header, body io.Reader) *textproto.MultipartReader {
contentType := header.Get("Content-Type")
if !strings.HasPrefix(strings.ToLower(contentType), "multipart/") {
return nil
}

_, params, err := mime.ParseMediaType(contentType)
if err != nil {
return nil
}

return textproto.NewMultipartReader(body, params["boundary"])
}

// FetchBodySection extracts a body section from a message.
func FetchBodySection(e *message.Entity, section *imap.BodySectionName) (imap.Literal, error) {
func FetchBodySection(header textproto.Header, body io.Reader, section *imap.BodySectionName) (imap.Literal, error) {
// First, find the requested part using the provided path
for i := len(section.Path) - 1; i >= 0; i-- {
for i := 0; i < len(section.Path); i++ {
n := section.Path[i]

mr := e.MultipartReader()
mr := multipartReader(header, body)
if mr == nil {
// First part of non-multipart message refers to the message itself.
// See RFC 3501, Page 55.
if len(section.Path) == 1 && section.Path[0] == 1 {
break
}
return nil, errNoSuchPart
}

Expand All @@ -31,7 +53,9 @@ func FetchBodySection(e *message.Entity, section *imap.BodySectionName) (imap.Li
}

if j == n {
e = p
body = p
header = p.Header

break
}
}
Expand All @@ -40,22 +64,51 @@ func FetchBodySection(e *message.Entity, section *imap.BodySectionName) (imap.Li
// Then, write the requested data to a buffer
b := new(bytes.Buffer)

resHeader := header
if section.Fields != nil {
// Copy header so we will not change value passed to us.
resHeader = header.Copy()

if section.NotFields {
for _, fieldName := range section.Fields {
resHeader.Del(fieldName)
}
} else {
fieldsMap := make(map[string]struct{}, len(section.Fields))
for _, field := range section.Fields {
fieldsMap[nettextproto.CanonicalMIMEHeaderKey(field)] = struct{}{}
}

for field := resHeader.Fields(); field.Next(); {
if _, ok := fieldsMap[field.Key()]; !ok {
field.Del()
}
}
}
}

// Write the header
mw, err := message.CreateWriter(b, e.Header)
err := textproto.WriteHeader(b, resHeader)
if err != nil {
return nil, err
}
defer mw.Close()

// If the header hasn't been requested, discard it
if section.Specifier == imap.TextSpecifier {
switch section.Specifier {
case imap.TextSpecifier:
// The header hasn't been requested. Discard it.
b.Reset()
case imap.EntireSpecifier:
if len(section.Path) > 0 {
// When selecting a specific part by index, IMAP servers
// return only the text, not the associated MIME header.
b.Reset()
}
}

// Write the body, if requested
switch section.Specifier {
case imap.EntireSpecifier, imap.TextSpecifier:
if _, err := io.Copy(mw, e.Body); err != nil {
if _, err := io.Copy(b, body); err != nil {
return nil, err
}
}
Expand Down
Loading