Update vendor/
This commit is contained in:
32
Gopkg.lock
generated
32
Gopkg.lock
generated
@@ -27,17 +27,28 @@
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
name = "github.com/emersion/go-imap"
|
||||
packages = [
|
||||
".",
|
||||
"backend",
|
||||
"backend/backendutil",
|
||||
"commands",
|
||||
"responses",
|
||||
"server",
|
||||
"utf7"
|
||||
]
|
||||
revision = "840b16b212bff35b595e708937c6d60861cfab49"
|
||||
version = "v0.9"
|
||||
revision = "e402a386e2001febf508c43e85d1a1383dca9e3c"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/emersion/go-message"
|
||||
packages = [
|
||||
".",
|
||||
"charset",
|
||||
"mail"
|
||||
]
|
||||
revision = "f7e2be8074d097a816a1f5bd9d502adc46275d4a"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -51,6 +62,12 @@
|
||||
packages = ["."]
|
||||
revision = "592cbda523faa1cbe7089c2997534a4a5a22986a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/emersion/go-textwrapper"
|
||||
packages = ["."]
|
||||
revision = "d0e65e56babe3f687ff94c1d764ca0e6aa7723ee"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/luksen/maildir"
|
||||
@@ -85,7 +102,7 @@
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context"]
|
||||
revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9"
|
||||
revision = "e514e69ffb8bc3c76a71ae40de0118d794855992"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -97,13 +114,18 @@
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "a200a19cb90b19de298170992778b1fda7217bd6"
|
||||
revision = "7138fd3d9dc8335c567ca206f4333fb75eb05d56"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"encoding",
|
||||
"encoding/charmap",
|
||||
"encoding/internal",
|
||||
"encoding/internal/identifier",
|
||||
"encoding/japanese",
|
||||
"encoding/simplifiedchinese",
|
||||
"encoding/traditionalchinese",
|
||||
"internal/gen",
|
||||
"transform",
|
||||
"unicode/cldr"
|
||||
@@ -120,6 +142,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "3c83cbb5b80f3005d42707c0c6e4f1b301a0429c8b83a837816d41ddeb9dd3ef"
|
||||
inputs-digest = "08b3e1cf0695997a6bb64a34c8f6d941f605f7354a813e70606352318c6cfb5f"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@@ -24,6 +24,10 @@
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/emersion/go-imap"
|
||||
branch = "v1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
3
vendor/github.com/emersion/go-imap/.travis.yml
generated
vendored
3
vendor/github.com/emersion/go-imap/.travis.yml
generated
vendored
@@ -1,5 +1,6 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
script: bash <(curl -sL https://gist.github.com/emersion/49d4dda535497002639626bd9e16480c/raw/codecov-go.sh)
|
||||
after_script: bash <(curl -s https://codecov.io/bash)
|
||||
|
3
vendor/github.com/emersion/go-imap/README.md
generated
vendored
3
vendor/github.com/emersion/go-imap/README.md
generated
vendored
@@ -6,7 +6,6 @@
|
||||
[](https://goreportcard.com/report/github.com/emersion/go-imap)
|
||||
[](https://github.com/emersion/stability-badges#unstable)
|
||||
[](https://gitter.im/goimap/Lobby)
|
||||
|
||||
An [IMAP4rev1](https://tools.ietf.org/html/rfc3501) library written in Go. It
|
||||
can be used to build a client and/or a server.
|
||||
@@ -96,7 +95,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:")
|
||||
|
2
vendor/github.com/emersion/go-imap/backend/backendutil/backendutil.go
generated
vendored
Normal file
2
vendor/github.com/emersion/go-imap/backend/backendutil/backendutil.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package backendutil provides utility functions to implement IMAP backends.
|
||||
package backendutil
|
68
vendor/github.com/emersion/go-imap/backend/backendutil/body.go
generated
vendored
Normal file
68
vendor/github.com/emersion/go-imap/backend/backendutil/body.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
var errNoSuchPart = errors.New("backendutil: no such message body part")
|
||||
|
||||
// FetchBodySection extracts a body section from a message.
|
||||
func FetchBodySection(e *message.Entity, section *imap.BodySectionName) (imap.Literal, error) {
|
||||
// First, find the requested part using the provided path
|
||||
for i := len(section.Path) - 1; i >= 0; i-- {
|
||||
n := section.Path[i]
|
||||
|
||||
mr := e.MultipartReader()
|
||||
if mr == nil {
|
||||
return nil, errNoSuchPart
|
||||
}
|
||||
|
||||
for j := 1; j <= n; j++ {
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
return nil, errNoSuchPart
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if j == n {
|
||||
e = p
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then, write the requested data to a buffer
|
||||
b := new(bytes.Buffer)
|
||||
|
||||
// Write the header
|
||||
mw, err := message.CreateWriter(b, e.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer mw.Close()
|
||||
|
||||
// If the header hasn't been requested, discard it
|
||||
if section.Specifier == imap.TextSpecifier {
|
||||
b.Reset()
|
||||
}
|
||||
|
||||
// Write the body, if requested
|
||||
switch section.Specifier {
|
||||
case imap.EntireSpecifier, imap.TextSpecifier:
|
||||
if _, err := io.Copy(mw, e.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var l imap.Literal = b
|
||||
if section.Partial != nil {
|
||||
l = bytes.NewReader(section.ExtractPartial(b.Bytes()))
|
||||
}
|
||||
return l, nil
|
||||
}
|
60
vendor/github.com/emersion/go-imap/backend/backendutil/bodystructure.go
generated
vendored
Normal file
60
vendor/github.com/emersion/go-imap/backend/backendutil/bodystructure.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// FetchBodyStructure computes a message's body structure from its content.
|
||||
func FetchBodyStructure(e *message.Entity, extended bool) (*imap.BodyStructure, error) {
|
||||
bs := new(imap.BodyStructure)
|
||||
|
||||
mediaType, mediaParams, _ := e.Header.ContentType()
|
||||
typeParts := strings.SplitN(mediaType, "/", 2)
|
||||
bs.MIMEType = typeParts[0]
|
||||
if len(typeParts) == 2 {
|
||||
bs.MIMESubType = typeParts[1]
|
||||
}
|
||||
bs.Params = mediaParams
|
||||
|
||||
bs.Id = e.Header.Get("Content-Id")
|
||||
bs.Description = e.Header.Get("Content-Description")
|
||||
bs.Encoding = e.Header.Get("Content-Encoding")
|
||||
// TODO: bs.Size
|
||||
|
||||
if mr := e.MultipartReader(); mr != nil {
|
||||
var parts []*imap.BodyStructure
|
||||
for {
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbs, err := FetchBodyStructure(p, extended)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts = append(parts, pbs)
|
||||
}
|
||||
bs.Parts = parts
|
||||
}
|
||||
|
||||
// TODO: bs.Envelope, bs.BodyStructure
|
||||
// TODO: bs.Lines
|
||||
|
||||
if extended {
|
||||
bs.Extended = true
|
||||
|
||||
bs.Disposition, bs.DispositionParams, _ = e.Header.ContentDisposition()
|
||||
|
||||
// TODO: bs.Language, bs.Location
|
||||
// TODO: bs.MD5
|
||||
}
|
||||
|
||||
return bs, nil
|
||||
}
|
50
vendor/github.com/emersion/go-imap/backend/backendutil/envelope.go
generated
vendored
Normal file
50
vendor/github.com/emersion/go-imap/backend/backendutil/envelope.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func headerAddressList(h mail.Header, key string) ([]*imap.Address, error) {
|
||||
addrs, err := h.AddressList(key)
|
||||
|
||||
list := make([]*imap.Address, len(addrs))
|
||||
for i, a := range addrs {
|
||||
parts := strings.SplitN(a.Address, "@", 2)
|
||||
mailbox := parts[0]
|
||||
var hostname string
|
||||
if len(parts) == 2 {
|
||||
hostname = parts[1]
|
||||
}
|
||||
|
||||
list[i] = &imap.Address{
|
||||
PersonalName: a.Name,
|
||||
MailboxName: mailbox,
|
||||
HostName: hostname,
|
||||
}
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
// FetchEnvelope returns a message's envelope from its header.
|
||||
func FetchEnvelope(h message.Header) (*imap.Envelope, error) {
|
||||
mh := mail.Header{h}
|
||||
|
||||
env := new(imap.Envelope)
|
||||
env.Date, _ = mh.Date()
|
||||
env.Subject, _ = mh.Subject()
|
||||
env.From, _ = headerAddressList(mh, "From")
|
||||
env.Sender, _ = headerAddressList(mh, "Sender")
|
||||
env.ReplyTo, _ = headerAddressList(mh, "Reply-To")
|
||||
env.To, _ = headerAddressList(mh, "To")
|
||||
env.Cc, _ = headerAddressList(mh, "Cc")
|
||||
env.Bcc, _ = headerAddressList(mh, "Bcc")
|
||||
env.InReplyTo = mh.Get("In-Reply-To")
|
||||
env.MessageId = mh.Get("Message-Id")
|
||||
|
||||
return env, nil
|
||||
}
|
40
vendor/github.com/emersion/go-imap/backend/backendutil/flags.go
generated
vendored
Normal file
40
vendor/github.com/emersion/go-imap/backend/backendutil/flags.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// UpdateFlags executes a flag operation on the flag set current.
|
||||
func UpdateFlags(current []string, op imap.FlagsOp, flags []string) []string {
|
||||
switch op {
|
||||
case imap.SetFlags:
|
||||
// TODO: keep \Recent if it is present
|
||||
return flags
|
||||
case imap.AddFlags:
|
||||
// Check for duplicates
|
||||
for _, flag := range current {
|
||||
for i, addFlag := range flags {
|
||||
if addFlag == flag {
|
||||
flags = append(flags[:i], flags[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return append(current, flags...)
|
||||
case imap.RemoveFlags:
|
||||
// Iterate through flags from the last one to the first one, to be able to
|
||||
// delete some of them.
|
||||
for i := len(current) - 1; i >= 0; i-- {
|
||||
flag := current[i]
|
||||
|
||||
for _, removeFlag := range flags {
|
||||
if removeFlag == flag {
|
||||
current = append(current[:i], current[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
return current
|
||||
}
|
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
225
vendor/github.com/emersion/go-imap/backend/backendutil/search.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
package backendutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/mail"
|
||||
)
|
||||
|
||||
func matchString(s, substr string) bool {
|
||||
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||||
}
|
||||
|
||||
func bufferBody(e *message.Entity) (*bytes.Buffer, error) {
|
||||
b := new(bytes.Buffer)
|
||||
if _, err := io.Copy(b, e.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Body = b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func matchBody(e *message.Entity, substr string) (bool, error) {
|
||||
if s, ok := e.Body.(fmt.Stringer); ok {
|
||||
return matchString(s.String(), substr), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return matchString(b.String(), substr), nil
|
||||
}
|
||||
|
||||
type lengther interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
func bodyLen(e *message.Entity) (int, error) {
|
||||
if l, ok := e.Body.(lengther); ok {
|
||||
return l.Len(), nil
|
||||
}
|
||||
|
||||
b, err := bufferBody(e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.Len(), nil
|
||||
}
|
||||
|
||||
// Match returns true if a message matches the provided criteria. Sequence
|
||||
// number, UID, flag and internal date contrainsts are not checked.
|
||||
func Match(e *message.Entity, c *imap.SearchCriteria) (bool, error) {
|
||||
// TODO: support encoded header fields for Bcc, Cc, From, To
|
||||
// TODO: add header size for Larger and Smaller
|
||||
|
||||
h := mail.Header{e.Header}
|
||||
|
||||
if !c.SentBefore.IsZero() || !c.SentSince.IsZero() {
|
||||
t, err := h.Date()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
t = t.Round(24 * time.Hour)
|
||||
|
||||
if !c.SentBefore.IsZero() && !t.Before(c.SentBefore) {
|
||||
return false, nil
|
||||
}
|
||||
if !c.SentSince.IsZero() && !t.After(c.SentSince) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for key, wantValues := range c.Header {
|
||||
values, ok := e.Header[key]
|
||||
for _, wantValue := range wantValues {
|
||||
if wantValue == "" && !ok {
|
||||
return false, nil
|
||||
}
|
||||
if wantValue != "" {
|
||||
ok := false
|
||||
for _, v := range values {
|
||||
if matchString(v, wantValue) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, body := range c.Body {
|
||||
if ok, err := matchBody(e, body); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, text := range c.Text {
|
||||
// TODO: also match header fields
|
||||
if ok, err := matchBody(e, text); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Larger > 0 || c.Smaller > 0 {
|
||||
n, err := bodyLen(e)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if c.Larger > 0 && uint32(n) < c.Larger {
|
||||
return false, nil
|
||||
}
|
||||
if c.Smaller > 0 && uint32(n) > c.Smaller {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
ok, err := Match(e, not)
|
||||
if err != nil || ok {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
ok1, err := Match(e, or[0])
|
||||
if err != nil {
|
||||
return ok1, err
|
||||
}
|
||||
|
||||
ok2, err := Match(e, or[1])
|
||||
if err != nil || (!ok1 && !ok2) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func matchFlags(flags map[string]bool, c *imap.SearchCriteria) bool {
|
||||
for _, f := range c.WithFlags {
|
||||
if !flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, f := range c.WithoutFlags {
|
||||
if flags[f] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if matchFlags(flags, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !matchFlags(flags, or[0]) && !matchFlags(flags, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchFlags returns true if a flag list matches the provided criteria.
|
||||
func MatchFlags(flags []string, c *imap.SearchCriteria) bool {
|
||||
flagsMap := make(map[string]bool)
|
||||
for _, f := range flags {
|
||||
flagsMap[f] = true
|
||||
}
|
||||
|
||||
return matchFlags(flagsMap, c)
|
||||
}
|
||||
|
||||
// MatchSeqNumAndUid returns true if a sequence number and a UID matches the
|
||||
// provided criteria.
|
||||
func MatchSeqNumAndUid(seqNum uint32, uid uint32, c *imap.SearchCriteria) bool {
|
||||
if c.SeqNum != nil && !c.SeqNum.Contains(seqNum) {
|
||||
return false
|
||||
}
|
||||
if c.Uid != nil && !c.Uid.Contains(uid) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchSeqNumAndUid(seqNum, uid, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchSeqNumAndUid(seqNum, uid, or[0]) && !MatchSeqNumAndUid(seqNum, uid, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchDate returns true if a date matches the provided criteria.
|
||||
func MatchDate(date time.Time, c *imap.SearchCriteria) bool {
|
||||
date = date.Round(24 * time.Hour)
|
||||
if !c.Since.IsZero() && !date.After(c.Since) {
|
||||
return false
|
||||
}
|
||||
if !c.Before.IsZero() && !date.Before(c.Before) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
if MatchDate(date, not) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, or := range c.Or {
|
||||
if !MatchDate(date, or[0]) && !MatchDate(date, or[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
12
vendor/github.com/emersion/go-imap/backend/mailbox.go
generated
vendored
12
vendor/github.com/emersion/go-imap/backend/mailbox.go
generated
vendored
@@ -15,11 +15,11 @@ type Mailbox interface {
|
||||
// Info returns this mailbox info.
|
||||
Info() (*imap.MailboxInfo, error)
|
||||
|
||||
// Status returns this mailbox status. The fields Name, Flags and
|
||||
// PermanentFlags in the returned MailboxStatus must be always populated. This
|
||||
// function does not affect the state of any messages in the mailbox. See RFC
|
||||
// 3501 section 6.3.10 for a list of items that can be requested.
|
||||
Status(items []string) (*imap.MailboxStatus, error)
|
||||
// Status returns this mailbox status. The fields Name, Flags, PermanentFlags
|
||||
// and UnseenSeqNum in the returned MailboxStatus must be always populated.
|
||||
// This function does not affect the state of any messages in the mailbox. See
|
||||
// RFC 3501 section 6.3.10 for a list of items that can be requested.
|
||||
Status(items []imap.StatusItem) (*imap.MailboxStatus, error)
|
||||
|
||||
// SetSubscribed adds or removes the mailbox to the server's set of "active"
|
||||
// or "subscribed" mailboxes.
|
||||
@@ -38,7 +38,7 @@ type Mailbox interface {
|
||||
// 3501 section 6.4.5 for a list of items that can be requested.
|
||||
//
|
||||
// Messages must be sent to ch. When the function returns, ch must be closed.
|
||||
ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch chan<- *imap.Message) error
|
||||
ListMessages(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error
|
||||
|
||||
// SearchMessages searches messages. The returned list must contain UIDs if
|
||||
// uid is set to true, or sequence numbers otherwise.
|
||||
|
95
vendor/github.com/emersion/go-imap/backend/updates.go
generated
vendored
95
vendor/github.com/emersion/go-imap/backend/updates.go
generated
vendored
@@ -6,35 +6,47 @@ import (
|
||||
|
||||
// Update contains user and mailbox information about an unilateral backend
|
||||
// update.
|
||||
type Update struct {
|
||||
type Update interface {
|
||||
// The user targeted by this update. If empty, all connected users will
|
||||
// be notified.
|
||||
Username string
|
||||
Username() string
|
||||
// The mailbox targeted by this update. If empty, the update targets all
|
||||
// mailboxes.
|
||||
Mailbox string
|
||||
Mailbox() string
|
||||
// Done returns a channel that is closed when the update has been broadcast to
|
||||
// all clients.
|
||||
Done() chan struct{}
|
||||
}
|
||||
|
||||
// A channel that will be closed once the update has been processed.
|
||||
// NewUpdate creates a new update.
|
||||
func NewUpdate(username, mailbox string) Update {
|
||||
return &update{
|
||||
username: username,
|
||||
mailbox: mailbox,
|
||||
}
|
||||
}
|
||||
|
||||
type update struct {
|
||||
username string
|
||||
mailbox string
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Done returns a channel that is closed when the update has been broadcast to
|
||||
// all clients.
|
||||
func (u *Update) Done() <-chan struct{} {
|
||||
func (u *update) Username() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u *update) Mailbox() string {
|
||||
return u.mailbox
|
||||
}
|
||||
|
||||
func (u *update) Done() chan struct{} {
|
||||
if u.done == nil {
|
||||
u.done = make(chan struct{})
|
||||
}
|
||||
return u.done
|
||||
}
|
||||
|
||||
// DoneUpdate marks an update as done.
|
||||
// TODO: remove this function
|
||||
func DoneUpdate(u *Update) {
|
||||
if u.done != nil {
|
||||
close(u.done)
|
||||
}
|
||||
}
|
||||
|
||||
// StatusUpdate is a status update. See RFC 3501 section 7.1 for a list of
|
||||
// status responses.
|
||||
type StatusUpdate struct {
|
||||
@@ -60,50 +72,21 @@ type ExpungeUpdate struct {
|
||||
SeqNum uint32
|
||||
}
|
||||
|
||||
// Updater is a Backend that implements Updater is able to send unilateral
|
||||
// backend updates. Backends not implementing this interface don't correctly
|
||||
// send unilateral updates, for instance if a user logs in from two connections
|
||||
// and deletes a message from one of them, the over is not aware that such a
|
||||
// mesage has been deleted. More importantly, backends implementing Updater can
|
||||
// notify the user for external updates such as new message notifications.
|
||||
type Updater interface {
|
||||
// BackendUpdater is a Backend that implements Updater is able to send
|
||||
// unilateral backend updates. Backends not implementing this interface don't
|
||||
// correctly send unilateral updates, for instance if a user logs in from two
|
||||
// connections and deletes a message from one of them, the over is not aware
|
||||
// that such a mesage has been deleted. More importantly, backends implementing
|
||||
// Updater can notify the user for external updates such as new message
|
||||
// notifications.
|
||||
type BackendUpdater interface {
|
||||
// Updates returns a set of channels where updates are sent to.
|
||||
Updates() <-chan interface{}
|
||||
Updates() <-chan Update
|
||||
}
|
||||
|
||||
// UpdaterMailbox is a Mailbox that implements UpdaterMailbox is able to poll
|
||||
// updates for new messages or message status updates during a period of
|
||||
// inactivity.
|
||||
type UpdaterMailbox interface {
|
||||
// MailboxPoller is a Mailbox that is able to poll updates for new messages or
|
||||
// message status updates during a period of inactivity.
|
||||
type MailboxPoller interface {
|
||||
// Poll requests mailbox updates.
|
||||
Poll() error
|
||||
}
|
||||
|
||||
// WaitUpdates returns a channel that's closed when all provided updates have
|
||||
// been dispatched to all clients. It panics if one of the provided value is
|
||||
// not an update.
|
||||
func WaitUpdates(updates ...interface{}) <-chan struct{} {
|
||||
done := make(chan struct{})
|
||||
|
||||
var chs []<-chan struct{}
|
||||
for _, u := range updates {
|
||||
uu, ok := u.(interface {
|
||||
Done() <-chan struct{}
|
||||
})
|
||||
if !ok {
|
||||
panic("imap: cannot wait for update: provided value is not a valid update")
|
||||
}
|
||||
|
||||
chs = append(chs, uu.Done())
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Wait for all updates to be sent
|
||||
for _, ch := range chs {
|
||||
<-ch
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
return done
|
||||
}
|
||||
|
32
vendor/github.com/emersion/go-imap/command.go
generated
vendored
32
vendor/github.com/emersion/go-imap/command.go
generated
vendored
@@ -5,38 +5,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IMAP4rev1 commands.
|
||||
const (
|
||||
Capability string = "CAPABILITY"
|
||||
Noop = "NOOP"
|
||||
Logout = "LOGOUT"
|
||||
StartTLS = "STARTTLS"
|
||||
|
||||
Authenticate = "AUTHENTICATE"
|
||||
Login = "LOGIN"
|
||||
|
||||
Select = "SELECT"
|
||||
Examine = "EXAMINE"
|
||||
Create = "CREATE"
|
||||
Delete = "DELETE"
|
||||
Rename = "RENAME"
|
||||
Subscribe = "SUBSCRIBE"
|
||||
Unsubscribe = "UNSUBSCRIBE"
|
||||
List = "LIST"
|
||||
Lsub = "LSUB"
|
||||
Status = "STATUS"
|
||||
Append = "APPEND"
|
||||
|
||||
Check = "CHECK"
|
||||
Close = "CLOSE"
|
||||
Expunge = "EXPUNGE"
|
||||
Search = "SEARCH"
|
||||
Fetch = "FETCH"
|
||||
Store = "STORE"
|
||||
Copy = "COPY"
|
||||
Uid = "UID"
|
||||
)
|
||||
|
||||
// A value that can be converted to a command.
|
||||
type Commander interface {
|
||||
Command() *Command
|
||||
|
18
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
18
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
@@ -19,13 +19,13 @@ type Append struct {
|
||||
func (cmd *Append) Command() *imap.Command {
|
||||
var args []interface{}
|
||||
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
args = append(args, mailbox)
|
||||
|
||||
if cmd.Flags != nil {
|
||||
flags := make([]interface{}, len(cmd.Flags))
|
||||
for i, flag := range cmd.Flags {
|
||||
flags[i] = flag
|
||||
flags[i] = imap.Atom(flag)
|
||||
}
|
||||
args = append(args, flags)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func (cmd *Append) Command() *imap.Command {
|
||||
args = append(args, cmd.Message)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Append,
|
||||
Name: "APPEND",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,9 @@ func (cmd *Append) Parse(fields []interface{}) (err error) {
|
||||
}
|
||||
|
||||
// Parse mailbox name
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
@@ -81,11 +81,9 @@ func (cmd *Append) Parse(fields []interface{}) (err error) {
|
||||
|
||||
// Parse date
|
||||
if len(fields) > 0 {
|
||||
date, ok := fields[0].(string)
|
||||
if !ok {
|
||||
if date, ok := fields[0].(string); !ok {
|
||||
return errors.New("Date must be a string")
|
||||
}
|
||||
if cmd.Date, err = time.Parse(imap.DateTimeLayout, date); err != nil {
|
||||
} else if cmd.Date, err = time.Parse(imap.DateTimeLayout, date); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
4
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
4
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
@@ -27,7 +27,7 @@ type Authenticate struct {
|
||||
|
||||
func (cmd *Authenticate) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Authenticate,
|
||||
Name: "AUTHENTICATE",
|
||||
Arguments: []interface{}{cmd.Mechanism},
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (cmd *Authenticate) Handle(mechanisms map[string]sasl.Server, conn Authenti
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(challenge)
|
||||
cont := &imap.ContinuationResp{Info: encoded}
|
||||
cont := &imap.ContinuationReq{Info: encoded}
|
||||
if err := conn.WriteResp(cont); err != nil {
|
||||
return err
|
||||
}
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
@@ -9,7 +9,7 @@ type Capability struct{}
|
||||
|
||||
func (c *Capability) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Capability,
|
||||
Name: "CAPABILITY",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
@@ -9,7 +9,7 @@ type Check struct{}
|
||||
|
||||
func (cmd *Check) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Check,
|
||||
Name: "CHECK",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
@@ -9,7 +9,7 @@ type Close struct{}
|
||||
|
||||
func (cmd *Close) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Close,
|
||||
Name: "CLOSE",
|
||||
}
|
||||
}
|
||||
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
@@ -14,10 +14,10 @@ type Copy struct {
|
||||
}
|
||||
|
||||
func (cmd *Copy) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Copy,
|
||||
Name: "COPY",
|
||||
Arguments: []interface{}{cmd.SeqSet, mailbox},
|
||||
}
|
||||
}
|
||||
@@ -29,15 +29,15 @@ func (cmd *Copy) Parse(fields []interface{}) error {
|
||||
|
||||
if seqSet, ok := fields[0].(string); !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
} else if seqSet, err := imap.NewSeqSet(seqSet); err != nil {
|
||||
} else if seqSet, err := imap.ParseSeqSet(seqSet); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.SeqSet = seqSet
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
10
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
10
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
@@ -13,10 +13,10 @@ type Create struct {
|
||||
}
|
||||
|
||||
func (cmd *Create) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Create,
|
||||
Name: "CREATE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ func (cmd *Create) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
10
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
10
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
@@ -13,10 +13,10 @@ type Delete struct {
|
||||
}
|
||||
|
||||
func (cmd *Delete) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Delete,
|
||||
Name: "DELETE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ func (cmd *Delete) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
@@ -8,7 +8,7 @@ import (
|
||||
type Expunge struct{}
|
||||
|
||||
func (cmd *Expunge) Command() *imap.Command {
|
||||
return &imap.Command{Name: imap.Expunge}
|
||||
return &imap.Command{Name: "EXPUNGE"}
|
||||
}
|
||||
|
||||
func (cmd *Expunge) Parse(fields []interface{}) error {
|
||||
|
37
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
37
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
@@ -10,21 +10,21 @@ import (
|
||||
// Fetch is a FETCH command, as defined in RFC 3501 section 6.4.5.
|
||||
type Fetch struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Items []string
|
||||
Items []imap.FetchItem
|
||||
}
|
||||
|
||||
func (cmd *Fetch) Command() *imap.Command {
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, item := range cmd.Items {
|
||||
if section, err := imap.NewBodySectionName(item); err == nil {
|
||||
if section, err := imap.ParseBodySectionName(item); err == nil {
|
||||
items[i] = section
|
||||
} else {
|
||||
items[i] = item
|
||||
items[i] = string(item)
|
||||
}
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Fetch,
|
||||
Name: "FETCH",
|
||||
Arguments: []interface{}{cmd.SeqSet, items},
|
||||
}
|
||||
}
|
||||
@@ -34,33 +34,22 @@ func (cmd *Fetch) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
seqset, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Sequence set must be a string")
|
||||
}
|
||||
|
||||
var err error
|
||||
if cmd.SeqSet, err = imap.NewSeqSet(seqset); err != nil {
|
||||
if seqset, ok := fields[0].(string); !ok {
|
||||
return errors.New("Sequence set must be an atom")
|
||||
} else if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch items := fields[1].(type) {
|
||||
case string: // A macro or a single item
|
||||
switch strings.ToUpper(items) {
|
||||
case "ALL":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE"}
|
||||
case "FAST":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE"}
|
||||
case "FULL":
|
||||
cmd.Items = []string{"FLAGS", "INTERNALDATE", "RFC822.SIZE", "ENVELOPE", "BODY"}
|
||||
default:
|
||||
cmd.Items = []string{strings.ToUpper(items)}
|
||||
}
|
||||
cmd.Items = imap.FetchItem(strings.ToUpper(items)).Expand()
|
||||
case []interface{}: // A list of items
|
||||
cmd.Items = make([]string, len(items))
|
||||
for i, v := range items {
|
||||
item, _ := v.(string)
|
||||
cmd.Items[i] = strings.ToUpper(item)
|
||||
cmd.Items = make([]imap.FetchItem, 0, len(items))
|
||||
for _, v := range items {
|
||||
itemStr, _ := v.(string)
|
||||
item := imap.FetchItem(strings.ToUpper(itemStr))
|
||||
cmd.Items = append(cmd.Items, item.Expand()...)
|
||||
}
|
||||
default:
|
||||
return errors.New("Items must be either a string or a list")
|
||||
|
23
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
23
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
@@ -17,13 +17,14 @@ type List struct {
|
||||
}
|
||||
|
||||
func (cmd *List) Command() *imap.Command {
|
||||
name := imap.List
|
||||
name := "LIST"
|
||||
if cmd.Subscribed {
|
||||
name = imap.Lsub
|
||||
name = "LSUB"
|
||||
}
|
||||
|
||||
ref, _ := utf7.Encoder.String(cmd.Reference)
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
ref, _ := enc.String(cmd.Reference)
|
||||
mailbox, _ := enc.String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
@@ -36,18 +37,20 @@ func (cmd *List) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Reference name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
dec := utf7.Encoding.NewDecoder()
|
||||
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := dec.String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
// TODO: canonical mailbox path
|
||||
cmd.Reference = imap.CanonicalMailboxName(mailbox)
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := dec.String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
@@ -14,7 +14,7 @@ type Login struct {
|
||||
|
||||
func (cmd *Login) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Login,
|
||||
Name: "LOGIN",
|
||||
Arguments: []interface{}{cmd.Username, cmd.Password},
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,12 @@ func (cmd *Login) Parse(fields []interface{}) error {
|
||||
return errors.New("Not enough arguments")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if cmd.Username, ok = fields[0].(string); !ok {
|
||||
return errors.New("Username is not a string")
|
||||
var err error
|
||||
if cmd.Username, err = imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Password, ok = fields[1].(string); !ok {
|
||||
return errors.New("Password is not a string")
|
||||
if cmd.Password, err = imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
@@ -9,7 +9,7 @@ type Logout struct{}
|
||||
|
||||
func (c *Logout) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Logout,
|
||||
Name: "LOGOUT",
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
@@ -9,7 +9,7 @@ type Noop struct{}
|
||||
|
||||
func (c *Noop) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Noop,
|
||||
Name: "NOOP",
|
||||
}
|
||||
}
|
||||
|
||||
|
21
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
21
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
@@ -14,11 +14,12 @@ type Rename struct {
|
||||
}
|
||||
|
||||
func (cmd *Rename) Command() *imap.Command {
|
||||
existingName, _ := utf7.Encoder.String(cmd.Existing)
|
||||
newName, _ := utf7.Encoder.String(cmd.New)
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
existingName, _ := enc.String(cmd.Existing)
|
||||
newName, _ := enc.String(cmd.New)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Rename,
|
||||
Name: "RENAME",
|
||||
Arguments: []interface{}{existingName, newName},
|
||||
}
|
||||
}
|
||||
@@ -28,17 +29,19 @@ func (cmd *Rename) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if existingName, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if existingName, err := utf7.Decoder.String(existingName); err != nil {
|
||||
dec := utf7.Encoding.NewDecoder()
|
||||
|
||||
if existingName, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if existingName, err := dec.String(existingName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Existing = imap.CanonicalMailboxName(existingName)
|
||||
}
|
||||
|
||||
if newName, ok := fields[1].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if newName, err := utf7.Decoder.String(newName); err != nil {
|
||||
if newName, err := imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
} else if newName, err := dec.String(newName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.New = imap.CanonicalMailboxName(newName)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
@@ -22,7 +22,7 @@ func (cmd *Search) Command() *imap.Command {
|
||||
args = append(args, cmd.Criteria.Format()...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Search,
|
||||
Name: "SEARCH",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
12
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
12
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
@@ -15,12 +15,12 @@ type Select struct {
|
||||
}
|
||||
|
||||
func (cmd *Select) Command() *imap.Command {
|
||||
name := imap.Select
|
||||
name := "SELECT"
|
||||
if cmd.ReadOnly {
|
||||
name = imap.Examine
|
||||
name = "EXAMINE"
|
||||
}
|
||||
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
@@ -33,9 +33,9 @@ func (cmd *Select) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
@@ -9,7 +9,7 @@ type StartTLS struct{}
|
||||
|
||||
func (cmd *StartTLS) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.StartTLS,
|
||||
Name: "STARTTLS",
|
||||
}
|
||||
}
|
||||
|
||||
|
31
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
31
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
@@ -11,19 +11,19 @@ import (
|
||||
// Status is a STATUS command, as defined in RFC 3501 section 6.3.10.
|
||||
type Status struct {
|
||||
Mailbox string
|
||||
Items []string
|
||||
Items []imap.StatusItem
|
||||
}
|
||||
|
||||
func (cmd *Status) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, f := range cmd.Items {
|
||||
items[i] = f
|
||||
for i, item := range cmd.Items {
|
||||
items[i] = string(item)
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Status,
|
||||
Name: "STATUS",
|
||||
Arguments: []interface{}{mailbox, items},
|
||||
}
|
||||
}
|
||||
@@ -33,21 +33,24 @@ func (cmd *Status) Parse(fields []interface{}) error {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if mailbox, ok := fields[0].(string); !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
} else if mailbox, err := utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if mailbox, err := utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.Mailbox = imap.CanonicalMailboxName(mailbox)
|
||||
}
|
||||
|
||||
if items, ok := fields[1].([]interface{}); !ok {
|
||||
return errors.New("Items must be a list")
|
||||
items, ok := fields[1].([]interface{})
|
||||
if !ok {
|
||||
return errors.New("STATUS command parameter is not a list")
|
||||
}
|
||||
cmd.Items = make([]imap.StatusItem, len(items))
|
||||
for i, f := range items {
|
||||
if s, ok := f.(string); !ok {
|
||||
return errors.New("Got a non-string field in a STATUS command parameter")
|
||||
} else {
|
||||
cmd.Items = make([]string, len(items))
|
||||
for i, v := range items {
|
||||
item, _ := v.(string)
|
||||
cmd.Items[i] = strings.ToUpper(item)
|
||||
cmd.Items[i] = imap.StatusItem(strings.ToUpper(s))
|
||||
}
|
||||
}
|
||||
|
||||
|
20
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
20
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
@@ -10,18 +10,18 @@ import (
|
||||
// Store is a STORE command, as defined in RFC 3501 section 6.4.6.
|
||||
type Store struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Item string
|
||||
Item imap.StoreItem
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (cmd *Store) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: imap.Store,
|
||||
Arguments: []interface{}{cmd.SeqSet, cmd.Item, cmd.Value},
|
||||
Name: "STORE",
|
||||
Arguments: []interface{}{cmd.SeqSet, string(cmd.Item), cmd.Value},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Store) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Store) Parse(fields []interface{}) error {
|
||||
if len(fields) < 3 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
@@ -30,16 +30,18 @@ func (cmd *Store) Parse(fields []interface{}) (err error) {
|
||||
if !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
}
|
||||
if cmd.SeqSet, err = imap.NewSeqSet(seqset); err != nil {
|
||||
var err error
|
||||
if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.Item, ok = fields[1].(string); !ok {
|
||||
if item, ok := fields[1].(string); !ok {
|
||||
return errors.New("Item name must be a string")
|
||||
} else {
|
||||
cmd.Item = imap.StoreItem(strings.ToUpper(item))
|
||||
}
|
||||
cmd.Item = strings.ToUpper(cmd.Item)
|
||||
|
||||
// TODO: could be fields[2:] according to RFC 3501 page 91 "store-att-flags"
|
||||
cmd.Value = fields[2]
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
38
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
38
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
@@ -13,29 +13,25 @@ type Subscribe struct {
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Subscribe,
|
||||
Name: "SUBSCRIBE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Subscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enogh arguments")
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
mailbox, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
}
|
||||
|
||||
if cmd.Mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if cmd.Mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// An UNSUBSCRIBE command.
|
||||
@@ -45,27 +41,23 @@ type Unsubscribe struct {
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoder.String(cmd.Mailbox)
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Unsubscribe,
|
||||
Name: "UNSUBSCRIBE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Parse(fields []interface{}) (err error) {
|
||||
func (cmd *Unsubscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enogh arguments")
|
||||
}
|
||||
|
||||
mailbox, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
}
|
||||
|
||||
if cmd.Mailbox, err = utf7.Decoder.String(mailbox); err != nil {
|
||||
if mailbox, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if cmd.Mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
2
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
2
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
@@ -20,7 +20,7 @@ func (cmd *Uid) Command() *imap.Command {
|
||||
args = append(args, inner.Arguments...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: imap.Uid,
|
||||
Name: "UID",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
5
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
5
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
@@ -154,10 +154,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
|
||||
// Flush writes any buffered data to the underlying connection.
|
||||
func (c *Conn) Flush() error {
|
||||
if err := c.Writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return c.Writer.Flush()
|
||||
}
|
||||
|
||||
// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
|
||||
|
121
vendor/github.com/emersion/go-imap/handle.go
generated
vendored
121
vendor/github.com/emersion/go-imap/handle.go
generated
vendored
@@ -1,121 +0,0 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A response that can be either accepted or rejected by a handler.
|
||||
type RespHandle struct {
|
||||
Resp interface{}
|
||||
Accepts chan bool
|
||||
}
|
||||
|
||||
// Accept this response. This means that the handler will process it.
|
||||
func (h *RespHandle) Accept() {
|
||||
h.Accepts <- true
|
||||
}
|
||||
|
||||
// Reject this response. The handler cannot process it.
|
||||
func (h *RespHandle) Reject() {
|
||||
h.Accepts <- false
|
||||
}
|
||||
|
||||
// Accept this response if it has the specified name. If not, reject it.
|
||||
func (h *RespHandle) AcceptNamedResp(name string) (fields []interface{}, accepted bool) {
|
||||
res, ok := h.Resp.(*Resp)
|
||||
if !ok || len(res.Fields) == 0 {
|
||||
h.Reject()
|
||||
return
|
||||
}
|
||||
|
||||
n, ok := res.Fields[0].(string)
|
||||
if !ok || n != name {
|
||||
h.Reject()
|
||||
return
|
||||
}
|
||||
|
||||
h.Accept()
|
||||
|
||||
fields = res.Fields[1:]
|
||||
accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
// Delivers responses to handlers.
|
||||
type RespHandler chan *RespHandle
|
||||
|
||||
// Handles responses from a handler.
|
||||
type RespHandlerFrom interface {
|
||||
HandleFrom(hdlr RespHandler) error
|
||||
}
|
||||
|
||||
// A RespHandlerFrom that forwards responses to multiple RespHandler.
|
||||
type MultiRespHandler struct {
|
||||
handlers []RespHandler
|
||||
locker sync.Locker
|
||||
}
|
||||
|
||||
func NewMultiRespHandler() *MultiRespHandler {
|
||||
return &MultiRespHandler{
|
||||
locker: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) HandleFrom(ch RespHandler) error {
|
||||
for rh := range ch {
|
||||
mh.locker.Lock()
|
||||
|
||||
accepted := false
|
||||
for i := len(mh.handlers) - 1; i >= 0; i-- {
|
||||
hdlr := mh.handlers[i]
|
||||
|
||||
rh := &RespHandle{
|
||||
Resp: rh.Resp,
|
||||
Accepts: make(chan bool),
|
||||
}
|
||||
|
||||
hdlr <- rh
|
||||
if accepted = <-rh.Accepts; accepted {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
mh.locker.Unlock()
|
||||
|
||||
if accepted {
|
||||
rh.Accept()
|
||||
} else {
|
||||
rh.Reject()
|
||||
}
|
||||
}
|
||||
|
||||
mh.locker.Lock()
|
||||
for _, hdlr := range mh.handlers {
|
||||
close(hdlr)
|
||||
}
|
||||
mh.handlers = nil
|
||||
mh.locker.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) Add(hdlr RespHandler) {
|
||||
if hdlr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
mh.locker.Lock()
|
||||
mh.handlers = append(mh.handlers, hdlr)
|
||||
mh.locker.Unlock()
|
||||
}
|
||||
|
||||
func (mh *MultiRespHandler) Del(hdlr RespHandler) {
|
||||
mh.locker.Lock()
|
||||
for i, h := range mh.handlers {
|
||||
if h == hdlr {
|
||||
close(hdlr)
|
||||
mh.handlers = append(mh.handlers[:i], mh.handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
mh.locker.Unlock()
|
||||
}
|
82
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
82
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
@@ -2,9 +2,60 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A StatusItem is a mailbox status data item that can be retrieved with a
|
||||
// STATUS command. See RFC 3501 section 6.3.10.
|
||||
type StatusItem string
|
||||
|
||||
const (
|
||||
StatusMessages StatusItem = "MESSAGES"
|
||||
StatusRecent = "RECENT"
|
||||
StatusUidNext = "UIDNEXT"
|
||||
StatusUidValidity = "UIDVALIDITY"
|
||||
StatusUnseen = "UNSEEN"
|
||||
)
|
||||
|
||||
// A FetchItem is a message data item that can be fetched.
|
||||
type FetchItem string
|
||||
|
||||
// List of items that can be fetched.
|
||||
const (
|
||||
// Macros
|
||||
FetchAll FetchItem = "ALL"
|
||||
FetchFast = "FAST"
|
||||
FetchFull = "FULL"
|
||||
|
||||
// Items
|
||||
FetchBody = "BODY"
|
||||
FetchBodyStructure = "BODYSTRUCTURE"
|
||||
FetchEnvelope = "ENVELOPE"
|
||||
FetchFlags = "FLAGS"
|
||||
FetchInternalDate = "INTERNALDATE"
|
||||
FetchRFC822 = "RFC822"
|
||||
FetchRFC822Header = "RFC822.HEADER"
|
||||
FetchRFC822Size = "RFC822.SIZE"
|
||||
FetchRFC822Text = "RFC822.TEXT"
|
||||
FetchUid = "UID"
|
||||
)
|
||||
|
||||
// Expand expands the item if it's a macro.
|
||||
func (item FetchItem) Expand() []FetchItem {
|
||||
switch item {
|
||||
case FetchAll:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size, FetchEnvelope}
|
||||
case FetchFast:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size}
|
||||
case FetchFull:
|
||||
return []FetchItem{FetchFlags, FetchInternalDate, FetchRFC822Size, FetchEnvelope, FetchBody}
|
||||
default:
|
||||
return []FetchItem{item}
|
||||
}
|
||||
}
|
||||
|
||||
// FlagsOp is an operation that will be applied on message flags.
|
||||
type FlagsOp string
|
||||
|
||||
@@ -17,9 +68,36 @@ const (
|
||||
RemoveFlags = "-FLAGS"
|
||||
)
|
||||
|
||||
// SilentOp can be appended to a FlagsOp to prevent the operation from
|
||||
// silentOp can be appended to a FlagsOp to prevent the operation from
|
||||
// triggering unilateral message updates.
|
||||
const SilentOp = ".SILENT"
|
||||
const silentOp = ".SILENT"
|
||||
|
||||
// A StoreItem is a message data item that can be updated.
|
||||
type StoreItem string
|
||||
|
||||
// FormatFlagsOp returns the StoreItem that executes the flags operation op.
|
||||
func FormatFlagsOp(op FlagsOp, silent bool) StoreItem {
|
||||
s := string(op)
|
||||
if silent {
|
||||
s += silentOp
|
||||
}
|
||||
return StoreItem(s)
|
||||
}
|
||||
|
||||
// ParseFlagsOp parses a flags operation from StoreItem.
|
||||
func ParseFlagsOp(item StoreItem) (op FlagsOp, silent bool, err error) {
|
||||
itemStr := string(item)
|
||||
silent = strings.HasSuffix(itemStr, silentOp)
|
||||
if silent {
|
||||
itemStr = strings.TrimSuffix(itemStr, silentOp)
|
||||
}
|
||||
op = FlagsOp(itemStr)
|
||||
|
||||
if op != SetFlags && op != AddFlags && op != RemoveFlags {
|
||||
err = errors.New("Unsupported STORE operation")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CharsetReader, if non-nil, defines a function to generate charset-conversion
|
||||
// readers, converting from the provided charset into UTF-8. Charsets are always
|
||||
|
84
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
84
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
@@ -2,6 +2,7 @@ package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -53,22 +54,36 @@ func (info *MailboxInfo) Parse(fields []interface{}) error {
|
||||
return errors.New("Mailbox info needs at least 3 fields")
|
||||
}
|
||||
|
||||
info.Attributes, _ = ParseStringList(fields[0])
|
||||
var err error
|
||||
if info.Attributes, err = ParseStringList(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info.Delimiter, _ = fields[1].(string)
|
||||
var ok bool
|
||||
if info.Delimiter, ok = fields[1].(string); !ok {
|
||||
return errors.New("Mailbox delimiter must be a string")
|
||||
}
|
||||
|
||||
name, _ := fields[2].(string)
|
||||
info.Name, _ = utf7.Decoder.String(name)
|
||||
info.Name = CanonicalMailboxName(info.Name)
|
||||
if name, err := ParseString(fields[2]); err != nil {
|
||||
return err
|
||||
} else if name, err := utf7.Encoding.NewDecoder().String(name); err != nil {
|
||||
return err
|
||||
} else {
|
||||
info.Name = CanonicalMailboxName(name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format mailbox info to fields.
|
||||
func (info *MailboxInfo) Format() []interface{} {
|
||||
name, _ := utf7.Encoder.String(info.Name)
|
||||
name, _ := utf7.Encoding.NewEncoder().String(info.Name)
|
||||
attrs := make([]interface{}, len(info.Attributes))
|
||||
for i, attr := range info.Attributes {
|
||||
attrs[i] = Atom(attr)
|
||||
}
|
||||
// Thunderbird doesn't understand delimiters if not quoted
|
||||
return []interface{}{FormatStringList(info.Attributes), Quoted(info.Delimiter), name}
|
||||
return []interface{}{attrs, Quoted(info.Delimiter), name}
|
||||
}
|
||||
|
||||
// TODO: optimize this
|
||||
@@ -125,19 +140,6 @@ func (info *MailboxInfo) Match(reference, pattern string) bool {
|
||||
return info.match(name, pattern)
|
||||
}
|
||||
|
||||
// Mailbox status items.
|
||||
const (
|
||||
MailboxFlags = "FLAGS"
|
||||
MailboxPermanentFlags = "PERMANENTFLAGS"
|
||||
|
||||
// Defined in RFC 3501 section 6.3.10.
|
||||
MailboxMessages = "MESSAGES"
|
||||
MailboxRecent = "RECENT"
|
||||
MailboxUnseen = "UNSEEN"
|
||||
MailboxUidNext = "UIDNEXT"
|
||||
MailboxUidValidity = "UIDVALIDITY"
|
||||
)
|
||||
|
||||
// A mailbox status.
|
||||
type MailboxStatus struct {
|
||||
// The mailbox name.
|
||||
@@ -147,7 +149,7 @@ type MailboxStatus struct {
|
||||
// The mailbox items that are currently filled in. This map's values
|
||||
// should not be used directly, they must only be used by libraries
|
||||
// implementing extensions of the IMAP protocol.
|
||||
Items map[string]interface{}
|
||||
Items map[StatusItem]interface{}
|
||||
|
||||
// The Items map may be accessed in different goroutines. Protect
|
||||
// concurrent writes.
|
||||
@@ -157,6 +159,8 @@ type MailboxStatus struct {
|
||||
Flags []string
|
||||
// The mailbox permanent flags.
|
||||
PermanentFlags []string
|
||||
// The sequence number of the first unseen message in the mailbox.
|
||||
UnseenSeqNum uint32
|
||||
|
||||
// The number of messages in this mailbox.
|
||||
Messages uint32
|
||||
@@ -172,10 +176,10 @@ type MailboxStatus struct {
|
||||
}
|
||||
|
||||
// Create a new mailbox status that will contain the specified items.
|
||||
func NewMailboxStatus(name string, items []string) *MailboxStatus {
|
||||
func NewMailboxStatus(name string, items []StatusItem) *MailboxStatus {
|
||||
status := &MailboxStatus{
|
||||
Name: name,
|
||||
Items: make(map[string]interface{}),
|
||||
Items: make(map[StatusItem]interface{}),
|
||||
}
|
||||
|
||||
for _, k := range items {
|
||||
@@ -186,30 +190,30 @@ func NewMailboxStatus(name string, items []string) *MailboxStatus {
|
||||
}
|
||||
|
||||
func (status *MailboxStatus) Parse(fields []interface{}) error {
|
||||
status.Items = make(map[string]interface{})
|
||||
status.Items = make(map[StatusItem]interface{})
|
||||
|
||||
var k string
|
||||
var k StatusItem
|
||||
for i, f := range fields {
|
||||
if i%2 == 0 {
|
||||
var ok bool
|
||||
if k, ok = f.(string); !ok {
|
||||
return errors.New("Key is not a string")
|
||||
if kstr, ok := f.(string); !ok {
|
||||
return fmt.Errorf("cannot parse mailbox status: key is not a string, but a %T", f)
|
||||
} else {
|
||||
k = StatusItem(strings.ToUpper(kstr))
|
||||
}
|
||||
k = strings.ToUpper(k)
|
||||
} else {
|
||||
status.Items[k] = nil
|
||||
|
||||
var err error
|
||||
switch k {
|
||||
case MailboxMessages:
|
||||
case StatusMessages:
|
||||
status.Messages, err = ParseNumber(f)
|
||||
case MailboxRecent:
|
||||
case StatusRecent:
|
||||
status.Recent, err = ParseNumber(f)
|
||||
case MailboxUnseen:
|
||||
case StatusUnseen:
|
||||
status.Unseen, err = ParseNumber(f)
|
||||
case MailboxUidNext:
|
||||
case StatusUidNext:
|
||||
status.UidNext, err = ParseNumber(f)
|
||||
case MailboxUidValidity:
|
||||
case StatusUidValidity:
|
||||
status.UidValidity, err = ParseNumber(f)
|
||||
default:
|
||||
status.Items[k] = f
|
||||
@@ -228,19 +232,19 @@ func (status *MailboxStatus) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
for k, v := range status.Items {
|
||||
switch k {
|
||||
case MailboxMessages:
|
||||
case StatusMessages:
|
||||
v = status.Messages
|
||||
case MailboxRecent:
|
||||
case StatusRecent:
|
||||
v = status.Recent
|
||||
case MailboxUnseen:
|
||||
case StatusUnseen:
|
||||
v = status.Unseen
|
||||
case MailboxUidNext:
|
||||
case StatusUidNext:
|
||||
v = status.UidNext
|
||||
case MailboxUidValidity:
|
||||
case StatusUidValidity:
|
||||
v = status.UidValidity
|
||||
}
|
||||
|
||||
fields = append(fields, k, v)
|
||||
fields = append(fields, string(k), v)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
303
vendor/github.com/emersion/go-imap/message.go
generated
vendored
303
vendor/github.com/emersion/go-imap/message.go
generated
vendored
@@ -30,30 +30,13 @@ var flags = []string{
|
||||
RecentFlag,
|
||||
}
|
||||
|
||||
// Message attributes that can be fetched, defined in RFC 3501 section 6.4.5.
|
||||
// Attributes that fetches the message contents are defined with
|
||||
// BodySectionName.
|
||||
const (
|
||||
// Non-extensible form of BODYSTRUCTURE.
|
||||
BodyMsgAttr = "BODY"
|
||||
// MIME body structure of the message.
|
||||
BodyStructureMsgAttr = "BODYSTRUCTURE"
|
||||
// The envelope structure of the message.
|
||||
EnvelopeMsgAttr = "ENVELOPE"
|
||||
// The flags that are set for the message.
|
||||
FlagsMsgAttr = "FLAGS"
|
||||
// The internal date of the message.
|
||||
InternalDateMsgAttr = "INTERNALDATE"
|
||||
// The RFC 822 size of the message.
|
||||
SizeMsgAttr = "RFC822.SIZE"
|
||||
// The unique identifier for the message.
|
||||
UidMsgAttr = "UID"
|
||||
)
|
||||
// A PartSpecifier specifies which parts of the MIME entity should be returned.
|
||||
type PartSpecifier string
|
||||
|
||||
// Part specifiers described in RFC 3501 page 55.
|
||||
const (
|
||||
// Refers to the entire part, including headers.
|
||||
EntireSpecifier = ""
|
||||
EntireSpecifier PartSpecifier = ""
|
||||
// Refers to the header of the part. Must include the final CRLF delimiting
|
||||
// the header and the body.
|
||||
HeaderSpecifier = "HEADER"
|
||||
@@ -61,7 +44,7 @@ const (
|
||||
TextSpecifier = "TEXT"
|
||||
// Refers to the MIME Internet Message Body header. Must include the final
|
||||
// CRLF delimiting the header and the body.
|
||||
MimeSpecifier = "MIME"
|
||||
MIMESpecifier = "MIME"
|
||||
)
|
||||
|
||||
// Returns the canonical form of a flag. Flags are case-insensitive.
|
||||
@@ -83,9 +66,9 @@ func ParseParamList(fields []interface{}) (map[string]string, error) {
|
||||
|
||||
var k string
|
||||
for i, f := range fields {
|
||||
p, ok := f.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("Parameter list contains a non-string")
|
||||
p, err := ParseString(f)
|
||||
if err != nil {
|
||||
return nil, errors.New("Parameter list contains a non-string: " + err.Error())
|
||||
}
|
||||
|
||||
if i%2 == 0 {
|
||||
@@ -158,7 +141,7 @@ type Message struct {
|
||||
// The mailbox items that are currently filled in. This map's values
|
||||
// should not be used directly, they must only be used by libraries
|
||||
// implementing extensions of the IMAP protocol.
|
||||
Items map[string]interface{}
|
||||
Items map[FetchItem]interface{}
|
||||
|
||||
// The message envelope.
|
||||
Envelope *Envelope
|
||||
@@ -178,14 +161,14 @@ type Message struct {
|
||||
// The order in which items were requested. This order must be preserved
|
||||
// because some bad IMAP clients (looking at you, Outlook!) refuse responses
|
||||
// containing items in a different order.
|
||||
itemsOrder []string
|
||||
itemsOrder []FetchItem
|
||||
}
|
||||
|
||||
// Create a new empty message that will contain the specified items.
|
||||
func NewMessage(seqNum uint32, items []string) *Message {
|
||||
func NewMessage(seqNum uint32, items []FetchItem) *Message {
|
||||
msg := &Message{
|
||||
SeqNum: seqNum,
|
||||
Items: make(map[string]interface{}),
|
||||
Items: make(map[FetchItem]interface{}),
|
||||
Body: make(map[*BodySectionName]Literal),
|
||||
itemsOrder: items,
|
||||
}
|
||||
@@ -199,65 +182,65 @@ func NewMessage(seqNum uint32, items []string) *Message {
|
||||
|
||||
// Parse a message from fields.
|
||||
func (m *Message) Parse(fields []interface{}) error {
|
||||
m.Items = make(map[string]interface{})
|
||||
m.Items = make(map[FetchItem]interface{})
|
||||
m.Body = map[*BodySectionName]Literal{}
|
||||
m.itemsOrder = nil
|
||||
|
||||
var k string
|
||||
var k FetchItem
|
||||
for i, f := range fields {
|
||||
if i%2 == 0 { // It's a key
|
||||
var ok bool
|
||||
if k, ok = f.(string); !ok {
|
||||
return errors.New("Key is not a string")
|
||||
if kstr, ok := f.(string); !ok {
|
||||
return fmt.Errorf("cannot parse message: key is not a string, but a %T", f)
|
||||
} else {
|
||||
k = FetchItem(strings.ToUpper(kstr))
|
||||
}
|
||||
k = strings.ToUpper(k)
|
||||
} else { // It's a value
|
||||
m.Items[k] = nil
|
||||
m.itemsOrder = append(m.itemsOrder, k)
|
||||
|
||||
switch k {
|
||||
case BodyMsgAttr, BodyStructureMsgAttr:
|
||||
case FetchBody, FetchBodyStructure:
|
||||
bs, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("BODYSTRUCTURE is not a list")
|
||||
return fmt.Errorf("cannot parse message: BODYSTRUCTURE is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.BodyStructure = &BodyStructure{Extended: k == BodyStructureMsgAttr}
|
||||
m.BodyStructure = &BodyStructure{Extended: k == FetchBodyStructure}
|
||||
if err := m.BodyStructure.Parse(bs); err != nil {
|
||||
return err
|
||||
}
|
||||
case EnvelopeMsgAttr:
|
||||
case FetchEnvelope:
|
||||
env, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("ENVELOPE is not a list")
|
||||
return fmt.Errorf("cannot parse message: ENVELOPE is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.Envelope = &Envelope{}
|
||||
if err := m.Envelope.Parse(env); err != nil {
|
||||
return err
|
||||
}
|
||||
case FlagsMsgAttr:
|
||||
case FetchFlags:
|
||||
flags, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("FLAGS is not a list")
|
||||
return fmt.Errorf("cannot parse message: FLAGS is not a list, but a %T", f)
|
||||
}
|
||||
|
||||
m.Flags = make([]string, len(flags))
|
||||
for i, flag := range flags {
|
||||
s, _ := flag.(string)
|
||||
s, _ := ParseString(flag)
|
||||
m.Flags[i] = CanonicalFlag(s)
|
||||
}
|
||||
case InternalDateMsgAttr:
|
||||
case FetchInternalDate:
|
||||
date, _ := f.(string)
|
||||
m.InternalDate, _ = time.Parse(DateTimeLayout, date)
|
||||
case SizeMsgAttr:
|
||||
case FetchRFC822Size:
|
||||
m.Size, _ = ParseNumber(f)
|
||||
case UidMsgAttr:
|
||||
case FetchUid:
|
||||
m.Uid, _ = ParseNumber(f)
|
||||
default:
|
||||
// Likely to be a section of the body
|
||||
// First check that the section name is correct
|
||||
if section, err := NewBodySectionName(k); err != nil {
|
||||
if section, err := ParseBodySectionName(k); err != nil {
|
||||
// Not a section name, maybe an attribute defined in an IMAP extension
|
||||
m.Items[k] = f
|
||||
} else {
|
||||
@@ -270,24 +253,28 @@ func (m *Message) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) formatItem(k string) []interface{} {
|
||||
func (m *Message) formatItem(k FetchItem) []interface{} {
|
||||
v := m.Items[k]
|
||||
var kk interface{} = k
|
||||
var kk interface{} = string(k)
|
||||
|
||||
switch strings.ToUpper(k) {
|
||||
case BodyMsgAttr, BodyStructureMsgAttr:
|
||||
switch k {
|
||||
case FetchBody, FetchBodyStructure:
|
||||
// Extension data is only returned with the BODYSTRUCTURE fetch
|
||||
m.BodyStructure.Extended = k == BodyStructureMsgAttr
|
||||
m.BodyStructure.Extended = k == FetchBodyStructure
|
||||
v = m.BodyStructure.Format()
|
||||
case EnvelopeMsgAttr:
|
||||
case FetchEnvelope:
|
||||
v = m.Envelope.Format()
|
||||
case FlagsMsgAttr:
|
||||
v = FormatStringList(m.Flags)
|
||||
case InternalDateMsgAttr:
|
||||
case FetchFlags:
|
||||
flags := make([]interface{}, len(m.Flags))
|
||||
for i, flag := range m.Flags {
|
||||
flags[i] = Atom(flag)
|
||||
}
|
||||
v = flags
|
||||
case FetchInternalDate:
|
||||
v = m.InternalDate
|
||||
case SizeMsgAttr:
|
||||
case FetchRFC822Size:
|
||||
v = m.Size
|
||||
case UidMsgAttr:
|
||||
case FetchUid:
|
||||
v = m.Uid
|
||||
default:
|
||||
for section, literal := range m.Body {
|
||||
@@ -307,7 +294,7 @@ func (m *Message) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
|
||||
// First send ordered items
|
||||
processed := make(map[string]bool)
|
||||
processed := make(map[FetchItem]bool)
|
||||
for _, k := range m.itemsOrder {
|
||||
if _, ok := m.Items[k]; ok {
|
||||
fields = append(fields, m.formatItem(k)...)
|
||||
@@ -326,9 +313,11 @@ func (m *Message) Format() []interface{} {
|
||||
}
|
||||
|
||||
// Get the body section with the specified name. Returns nil if it's not found.
|
||||
func (m *Message) GetBody(s string) Literal {
|
||||
for section, body := range m.Body {
|
||||
if section.value == s {
|
||||
func (m *Message) GetBody(section *BodySectionName) Literal {
|
||||
section = section.resp()
|
||||
|
||||
for s, body := range m.Body {
|
||||
if section.Equal(s) {
|
||||
return body
|
||||
}
|
||||
}
|
||||
@@ -338,7 +327,7 @@ func (m *Message) GetBody(s string) Literal {
|
||||
// A body section name.
|
||||
// See RFC 3501 page 55.
|
||||
type BodySectionName struct {
|
||||
*BodyPartName
|
||||
BodyPartName
|
||||
|
||||
// If set to true, do not implicitly set the \Seen flag.
|
||||
Peek bool
|
||||
@@ -347,11 +336,11 @@ type BodySectionName struct {
|
||||
// octets desired.
|
||||
Partial []int
|
||||
|
||||
value string
|
||||
value FetchItem
|
||||
}
|
||||
|
||||
func (section *BodySectionName) parse(s string) (err error) {
|
||||
section.value = s
|
||||
func (section *BodySectionName) parse(s string) error {
|
||||
section.value = FetchItem(s)
|
||||
|
||||
if s == "RFC822" {
|
||||
s = "BODY[]"
|
||||
@@ -385,14 +374,13 @@ func (section *BodySectionName) parse(s string) (err error) {
|
||||
|
||||
b := bytes.NewBufferString(part + string(cr) + string(lf))
|
||||
r := NewReader(b)
|
||||
var fields []interface{}
|
||||
if fields, err = r.ReadFields(); err != nil {
|
||||
return
|
||||
fields, err := r.ReadFields()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
section.BodyPartName = &BodyPartName{}
|
||||
if err = section.BodyPartName.parse(fields); err != nil {
|
||||
return
|
||||
if err := section.BodyPartName.parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(partial) > 0 {
|
||||
@@ -420,17 +408,17 @@ func (section *BodySectionName) parse(s string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (section *BodySectionName) String() (s string) {
|
||||
func (section *BodySectionName) FetchItem() FetchItem {
|
||||
if section.value != "" {
|
||||
return section.value
|
||||
}
|
||||
|
||||
s = "BODY"
|
||||
s := "BODY"
|
||||
if section.Peek {
|
||||
s += ".PEEK"
|
||||
}
|
||||
|
||||
s += "[" + section.BodyPartName.String() + "]"
|
||||
s += "[" + section.BodyPartName.string() + "]"
|
||||
|
||||
if len(section.Partial) > 0 {
|
||||
s += "<"
|
||||
@@ -444,30 +432,39 @@ func (section *BodySectionName) String() (s string) {
|
||||
s += ">"
|
||||
}
|
||||
|
||||
return
|
||||
return FetchItem(s)
|
||||
}
|
||||
|
||||
// Equal checks whether two sections are equal.
|
||||
func (section *BodySectionName) Equal(other *BodySectionName) bool {
|
||||
if section.Peek != other.Peek {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) != len(other.Partial) {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) > 0 && section.Partial[0] != other.Partial[0] {
|
||||
return false
|
||||
}
|
||||
if len(section.Partial) > 1 && section.Partial[1] != other.Partial[1] {
|
||||
return false
|
||||
}
|
||||
return section.BodyPartName.Equal(&other.BodyPartName)
|
||||
}
|
||||
|
||||
func (section *BodySectionName) resp() *BodySectionName {
|
||||
var reset bool
|
||||
|
||||
if section.Peek != false {
|
||||
section.Peek = false
|
||||
reset = true
|
||||
resp := *section // Copy section
|
||||
if resp.Peek != false {
|
||||
resp.Peek = false
|
||||
}
|
||||
if len(resp.Partial) == 2 {
|
||||
resp.Partial = []int{resp.Partial[0]}
|
||||
}
|
||||
resp.value = ""
|
||||
return &resp
|
||||
}
|
||||
|
||||
if len(section.Partial) == 2 {
|
||||
section.Partial = []int{section.Partial[0]}
|
||||
reset = true
|
||||
}
|
||||
|
||||
if reset && !strings.HasPrefix(section.value, "RFC822") {
|
||||
section.value = "" // Reset cached value
|
||||
}
|
||||
|
||||
return section
|
||||
}
|
||||
|
||||
// Returns a subset of the specified bytes matching the partial requested in the
|
||||
// ExtractPartial returns a subset of the specified bytes matching the partial requested in the
|
||||
// section name.
|
||||
func (section *BodySectionName) ExtractPartial(b []byte) []byte {
|
||||
if len(section.Partial) != 2 {
|
||||
@@ -486,17 +483,17 @@ func (section *BodySectionName) ExtractPartial(b []byte) []byte {
|
||||
return b[from:to]
|
||||
}
|
||||
|
||||
// Parse a body section name.
|
||||
func NewBodySectionName(s string) (section *BodySectionName, err error) {
|
||||
section = &BodySectionName{}
|
||||
err = section.parse(s)
|
||||
return
|
||||
// ParseBodySectionName parses a body section name.
|
||||
func ParseBodySectionName(s FetchItem) (*BodySectionName, error) {
|
||||
section := new(BodySectionName)
|
||||
err := section.parse(string(s))
|
||||
return section, err
|
||||
}
|
||||
|
||||
// A body part name.
|
||||
type BodyPartName struct {
|
||||
// The specifier of the requested part.
|
||||
Specifier string
|
||||
Specifier PartSpecifier
|
||||
// The part path. Parts indexes start at 1.
|
||||
Path []int
|
||||
// If Specifier is HEADER, contains header fields that will/won't be returned,
|
||||
@@ -521,11 +518,13 @@ func (part *BodyPartName) parse(fields []interface{}) error {
|
||||
path := strings.Split(strings.ToUpper(name), ".")
|
||||
|
||||
end := 0
|
||||
loop:
|
||||
for i, node := range path {
|
||||
if node == "" || node == HeaderSpecifier || node == MimeSpecifier || node == TextSpecifier {
|
||||
part.Specifier = node
|
||||
switch PartSpecifier(node) {
|
||||
case EntireSpecifier, HeaderSpecifier, MIMESpecifier, TextSpecifier:
|
||||
part.Specifier = PartSpecifier(node)
|
||||
end = i + 1
|
||||
break
|
||||
break loop
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(node)
|
||||
@@ -560,14 +559,14 @@ func (part *BodyPartName) parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (part *BodyPartName) String() (s string) {
|
||||
func (part *BodyPartName) string() string {
|
||||
path := make([]string, len(part.Path))
|
||||
for i, index := range part.Path {
|
||||
path[i] = strconv.Itoa(index)
|
||||
}
|
||||
|
||||
if part.Specifier != "" {
|
||||
path = append(path, part.Specifier)
|
||||
if part.Specifier != EntireSpecifier {
|
||||
path = append(path, string(part.Specifier))
|
||||
}
|
||||
|
||||
if part.Specifier == HeaderSpecifier && len(part.Fields) > 0 {
|
||||
@@ -578,13 +577,47 @@ func (part *BodyPartName) String() (s string) {
|
||||
}
|
||||
}
|
||||
|
||||
s = strings.Join(path, ".")
|
||||
s := strings.Join(path, ".")
|
||||
|
||||
if len(part.Fields) > 0 {
|
||||
s += " (" + strings.Join(part.Fields, " ") + ")"
|
||||
}
|
||||
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
// Equal checks whether two body part names are equal.
|
||||
func (part *BodyPartName) Equal(other *BodyPartName) bool {
|
||||
if part.Specifier != other.Specifier {
|
||||
return false
|
||||
}
|
||||
if part.NotFields != other.NotFields {
|
||||
return false
|
||||
}
|
||||
if len(part.Path) != len(other.Path) {
|
||||
return false
|
||||
}
|
||||
for i, node := range part.Path {
|
||||
if node != other.Path[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(part.Fields) != len(other.Fields) {
|
||||
return false
|
||||
}
|
||||
for _, field := range part.Fields {
|
||||
found := false
|
||||
for _, f := range other.Fields {
|
||||
if strings.EqualFold(field, f) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// An address.
|
||||
@@ -605,17 +638,17 @@ func (addr *Address) Parse(fields []interface{}) error {
|
||||
return errors.New("Address doesn't contain 4 fields")
|
||||
}
|
||||
|
||||
if f, ok := fields[0].(string); ok {
|
||||
addr.PersonalName, _ = decodeHeader(f)
|
||||
if s, err := ParseString(fields[0]); err == nil {
|
||||
addr.PersonalName, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[1].(string); ok {
|
||||
addr.AtDomainList = f
|
||||
if s, err := ParseString(fields[1]); err == nil {
|
||||
addr.AtDomainList, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[2].(string); ok {
|
||||
addr.MailboxName = f
|
||||
if s, err := ParseString(fields[2]); err == nil {
|
||||
addr.MailboxName, _ = decodeHeader(s)
|
||||
}
|
||||
if f, ok := fields[3].(string); ok {
|
||||
addr.HostName = f
|
||||
if s, err := ParseString(fields[3]); err == nil {
|
||||
addr.HostName, _ = decodeHeader(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -702,7 +735,7 @@ func (e *Envelope) Parse(fields []interface{}) error {
|
||||
if date, ok := fields[0].(string); ok {
|
||||
e.Date, _ = parseMessageDateTime(date)
|
||||
}
|
||||
if subject, ok := fields[1].(string); ok {
|
||||
if subject, err := ParseString(fields[1]); err == nil {
|
||||
e.Subject, _ = decodeHeader(subject)
|
||||
}
|
||||
if from, ok := fields[2].([]interface{}); ok {
|
||||
@@ -755,9 +788,9 @@ type BodyStructure struct {
|
||||
// Basic fields
|
||||
|
||||
// The MIME type.
|
||||
MimeType string
|
||||
MIMEType string
|
||||
// The MIME subtype.
|
||||
MimeSubType string
|
||||
MIMESubType string
|
||||
// The MIME parameters.
|
||||
Params map[string]string
|
||||
|
||||
@@ -796,7 +829,7 @@ type BodyStructure struct {
|
||||
Location []string
|
||||
|
||||
// The MD5 checksum.
|
||||
Md5 string
|
||||
MD5 string
|
||||
}
|
||||
|
||||
func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
@@ -809,7 +842,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
|
||||
switch fields[0].(type) {
|
||||
case []interface{}: // A multipart body part
|
||||
bs.MimeType = "multipart"
|
||||
bs.MIMEType = "multipart"
|
||||
|
||||
end := 0
|
||||
for i, fi := range fields {
|
||||
@@ -829,7 +862,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
bs.MimeSubType, _ = fields[end].(string)
|
||||
bs.MIMESubType, _ = fields[end].(string)
|
||||
end++
|
||||
|
||||
// GMail seems to return only 3 extension data fields. Parse as many fields
|
||||
@@ -873,14 +906,14 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
return errors.New("Non-multipart body part doesn't have 7 fields")
|
||||
}
|
||||
|
||||
bs.MimeType, _ = fields[0].(string)
|
||||
bs.MimeSubType, _ = fields[1].(string)
|
||||
bs.MIMEType, _ = fields[0].(string)
|
||||
bs.MIMESubType, _ = fields[1].(string)
|
||||
|
||||
params, _ := fields[2].([]interface{})
|
||||
bs.Params, _ = parseHeaderParamList(params)
|
||||
|
||||
bs.Id, _ = fields[3].(string)
|
||||
if desc, ok := fields[4].(string); ok {
|
||||
if desc, err := ParseString(fields[4]); err == nil {
|
||||
bs.Description, _ = decodeHeader(desc)
|
||||
}
|
||||
bs.Encoding, _ = fields[5].(string)
|
||||
@@ -889,7 +922,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
end := 7
|
||||
|
||||
// Type-specific fields
|
||||
if bs.MimeType == "message" && bs.MimeSubType == "rfc822" {
|
||||
if bs.MIMEType == "message" && bs.MIMESubType == "rfc822" {
|
||||
if len(fields)-end < 3 {
|
||||
return errors.New("Missing type-specific fields for message/rfc822")
|
||||
}
|
||||
@@ -906,7 +939,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
|
||||
end += 3
|
||||
}
|
||||
if bs.MimeType == "text" {
|
||||
if bs.MIMEType == "text" {
|
||||
if len(fields)-end < 1 {
|
||||
return errors.New("Missing type-specific fields for text/*")
|
||||
}
|
||||
@@ -920,7 +953,7 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
if len(fields) > end {
|
||||
bs.Extended = true // Contains extension data
|
||||
|
||||
bs.Md5, _ = fields[end].(string)
|
||||
bs.MD5, _ = fields[end].(string)
|
||||
end++
|
||||
}
|
||||
if len(fields) > end {
|
||||
@@ -956,12 +989,12 @@ func (bs *BodyStructure) Parse(fields []interface{}) error {
|
||||
}
|
||||
|
||||
func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
if bs.MimeType == "multipart" {
|
||||
if bs.MIMEType == "multipart" {
|
||||
for _, part := range bs.Parts {
|
||||
fields = append(fields, part.Format())
|
||||
}
|
||||
|
||||
fields = append(fields, bs.MimeSubType)
|
||||
fields = append(fields, bs.MIMESubType)
|
||||
|
||||
if bs.Extended {
|
||||
extended := make([]interface{}, 4)
|
||||
@@ -986,8 +1019,8 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
}
|
||||
} else {
|
||||
fields = make([]interface{}, 7)
|
||||
fields[0] = bs.MimeType
|
||||
fields[1] = bs.MimeSubType
|
||||
fields[0] = bs.MIMEType
|
||||
fields[1] = bs.MIMESubType
|
||||
fields[2] = formatHeaderParamList(bs.Params)
|
||||
|
||||
if bs.Id != "" {
|
||||
@@ -1003,7 +1036,7 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
fields[6] = bs.Size
|
||||
|
||||
// Type-specific fields
|
||||
if bs.MimeType == "message" && bs.MimeSubType == "rfc822" {
|
||||
if bs.MIMEType == "message" && bs.MIMESubType == "rfc822" {
|
||||
var env interface{}
|
||||
if bs.Envelope != nil {
|
||||
env = bs.Envelope.Format()
|
||||
@@ -1016,7 +1049,7 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
|
||||
fields = append(fields, env, bsbs, bs.Lines)
|
||||
}
|
||||
if bs.MimeType == "text" {
|
||||
if bs.MIMEType == "text" {
|
||||
fields = append(fields, bs.Lines)
|
||||
}
|
||||
|
||||
@@ -1024,8 +1057,8 @@ func (bs *BodyStructure) Format() (fields []interface{}) {
|
||||
if bs.Extended {
|
||||
extended := make([]interface{}, 4)
|
||||
|
||||
if bs.Md5 != "" {
|
||||
extended[0] = bs.Md5
|
||||
if bs.MD5 != "" {
|
||||
extended[0] = bs.MD5
|
||||
}
|
||||
if bs.Disposition != "" {
|
||||
extended[1] = []interface{}{
|
||||
|
51
vendor/github.com/emersion/go-imap/read.go
generated
vendored
51
vendor/github.com/emersion/go-imap/read.go
generated
vendored
@@ -62,7 +62,7 @@ type reader interface {
|
||||
StringReader
|
||||
}
|
||||
|
||||
// Convert a field to a number.
|
||||
// ParseNumber parses a number.
|
||||
func ParseNumber(f interface{}) (uint32, error) {
|
||||
// Useful for tests
|
||||
if n, ok := f.(uint32); ok {
|
||||
@@ -71,7 +71,7 @@ func ParseNumber(f interface{}) (uint32, error) {
|
||||
|
||||
s, ok := f.(string)
|
||||
if !ok {
|
||||
return 0, newParseError("number is not a string")
|
||||
return 0, newParseError("expected a number, got a non-atom")
|
||||
}
|
||||
|
||||
nbr, err := strconv.ParseUint(s, 10, 32)
|
||||
@@ -82,18 +82,44 @@ func ParseNumber(f interface{}) (uint32, error) {
|
||||
return uint32(nbr), nil
|
||||
}
|
||||
|
||||
// ParseString parses a string, which is either a literal, a quoted string or an
|
||||
// atom.
|
||||
func ParseString(f interface{}) (string, error) {
|
||||
if s, ok := f.(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Useful for tests
|
||||
if q, ok := f.(Quoted); ok {
|
||||
return string(q), nil
|
||||
}
|
||||
if a, ok := f.(Atom); ok {
|
||||
return string(a), nil
|
||||
}
|
||||
|
||||
if l, ok := f.(Literal); ok {
|
||||
b := make([]byte, l.Len())
|
||||
if _, err := io.ReadFull(l, b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
return "", newParseError("expected a string")
|
||||
}
|
||||
|
||||
// Convert a field list to a string list.
|
||||
func ParseStringList(f interface{}) ([]string, error) {
|
||||
fields, ok := f.([]interface{})
|
||||
if !ok {
|
||||
return nil, newParseError("string list is not a list")
|
||||
return nil, newParseError("expected a string list, got a non-list")
|
||||
}
|
||||
|
||||
list := make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
var ok bool
|
||||
if list[i], ok = f.(string); !ok {
|
||||
return nil, newParseError("string list contains a non-string")
|
||||
var err error
|
||||
if list[i], err = ParseString(f); err != nil {
|
||||
return nil, newParseError("cannot parse string in string list: " + err.Error())
|
||||
}
|
||||
}
|
||||
return list, nil
|
||||
@@ -121,7 +147,7 @@ func (r *Reader) ReadSp() error {
|
||||
return err
|
||||
}
|
||||
if char != sp {
|
||||
return newParseError("not a space")
|
||||
return newParseError("expected a space")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -134,6 +160,7 @@ func (r *Reader) ReadCrlf() (err error) {
|
||||
}
|
||||
if char != cr {
|
||||
err = newParseError("line doesn't end with a CR")
|
||||
return
|
||||
}
|
||||
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
@@ -305,6 +332,9 @@ func (r *Reader) ReadFields() (fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
if char == cr || char == listEnd || char == respCodeEnd {
|
||||
if char == cr {
|
||||
r.UnreadRune()
|
||||
}
|
||||
return
|
||||
}
|
||||
if char == listStart {
|
||||
@@ -354,7 +384,7 @@ func (r *Reader) ReadLine() (fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadRespCode() (code string, fields []interface{}, err error) {
|
||||
func (r *Reader) ReadRespCode() (code StatusRespCode, fields []interface{}, err error) {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -376,15 +406,16 @@ func (r *Reader) ReadRespCode() (code string, fields []interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
code, ok := fields[0].(string)
|
||||
codeStr, ok := fields[0].(string)
|
||||
if !ok {
|
||||
err = newParseError("response code doesn't start with a string atom")
|
||||
return
|
||||
}
|
||||
if code == "" {
|
||||
if codeStr == "" {
|
||||
err = newParseError("response code is empty")
|
||||
return
|
||||
}
|
||||
code = StatusRespCode(strings.ToUpper(codeStr))
|
||||
|
||||
fields = fields[1:]
|
||||
|
||||
|
247
vendor/github.com/emersion/go-imap/response.go
generated
vendored
247
vendor/github.com/emersion/go-imap/response.go
generated
vendored
@@ -1,17 +1,104 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A value that can be converted to a Resp.
|
||||
type Responser interface {
|
||||
Response() *Resp
|
||||
// Resp is an IMAP response. It is either a *DataResp, a
|
||||
// *ContinuationReq or a *StatusResp.
|
||||
type Resp interface {
|
||||
resp()
|
||||
}
|
||||
|
||||
// A response.
|
||||
// See RFC 3501 section 2.2.2
|
||||
type Resp struct {
|
||||
// ReadResp reads a single response from a Reader.
|
||||
func ReadResp(r *Reader) (Resp, error) {
|
||||
atom, err := r.ReadAtom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag, ok := atom.(string)
|
||||
if !ok {
|
||||
return nil, newParseError("response tag is not an atom")
|
||||
}
|
||||
|
||||
if tag == "+" {
|
||||
if err := r.ReadSp(); err != nil {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
resp := &ContinuationReq{}
|
||||
resp.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if err := r.ReadSp(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Can be either data or status
|
||||
// Try to parse a status
|
||||
var fields []interface{}
|
||||
if atom, err := r.ReadAtom(); err == nil {
|
||||
fields = append(fields, atom)
|
||||
|
||||
if err := r.ReadSp(); err == nil {
|
||||
if name, ok := atom.(string); ok {
|
||||
status := StatusRespType(name)
|
||||
switch status {
|
||||
case StatusRespOk, StatusRespNo, StatusRespBad, StatusRespPreauth, StatusRespBye:
|
||||
resp := &StatusResp{
|
||||
Tag: tag,
|
||||
Type: status,
|
||||
}
|
||||
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.UnreadRune()
|
||||
|
||||
if char == '[' {
|
||||
// Contains code & arguments
|
||||
resp.Code, resp.Arguments, err = r.ReadRespCode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
// Not a status so it's data
|
||||
resp := &DataResp{Tag: tag}
|
||||
|
||||
var remaining []interface{}
|
||||
remaining, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Fields = append(fields, remaining...)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DataResp is an IMAP response containing data.
|
||||
type DataResp struct {
|
||||
// The response tag. Can be either "" for untagged responses, "+" for continuation
|
||||
// requests or a previous command's tag.
|
||||
Tag string
|
||||
@@ -19,10 +106,20 @@ type Resp struct {
|
||||
Fields []interface{}
|
||||
}
|
||||
|
||||
func (r *Resp) WriteTo(w *Writer) error {
|
||||
tag := r.Tag
|
||||
// NewUntaggedResp creates a new untagged response.
|
||||
func NewUntaggedResp(fields []interface{}) *DataResp {
|
||||
return &DataResp{
|
||||
Tag: "*",
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *DataResp) resp() {}
|
||||
|
||||
func (r *DataResp) WriteTo(w *Writer) error {
|
||||
tag := Atom(r.Tag)
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
tag = Atom("*")
|
||||
}
|
||||
|
||||
fields := []interface{}{tag}
|
||||
@@ -30,21 +127,15 @@ func (r *Resp) WriteTo(w *Writer) error {
|
||||
return w.writeLine(fields...)
|
||||
}
|
||||
|
||||
// Create a new untagged response.
|
||||
func NewUntaggedResp(fields []interface{}) *Resp {
|
||||
return &Resp{
|
||||
Tag: "*",
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
// A continuation request.
|
||||
type ContinuationResp struct {
|
||||
// ContinuationReq is a continuation request response.
|
||||
type ContinuationReq struct {
|
||||
// The info message sent with the continuation request.
|
||||
Info string
|
||||
}
|
||||
|
||||
func (r *ContinuationResp) WriteTo(w *Writer) error {
|
||||
func (r *ContinuationReq) resp() {}
|
||||
|
||||
func (r *ContinuationReq) WriteTo(w *Writer) error {
|
||||
if err := w.writeString("+"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -58,99 +149,33 @@ func (r *ContinuationResp) WriteTo(w *Writer) error {
|
||||
return w.writeCrlf()
|
||||
}
|
||||
|
||||
// Read a single response from a Reader. Returns either a continuation request,
|
||||
// a status response or a raw response.
|
||||
func ReadResp(r *Reader) (out interface{}, err error) {
|
||||
atom, err := r.ReadAtom()
|
||||
if err != nil {
|
||||
// ParseNamedResp attempts to parse a named data response.
|
||||
func ParseNamedResp(resp Resp) (name string, fields []interface{}, ok bool) {
|
||||
data, ok := resp.(*DataResp)
|
||||
if !ok || len(data.Fields) == 0 {
|
||||
return
|
||||
}
|
||||
tag, ok := atom.(string)
|
||||
|
||||
// Some responses (namely EXISTS and RECENT) are formatted like so:
|
||||
// [num] [name] [...]
|
||||
// Which is fucking stupid. But we handle that here by checking if the
|
||||
// response name is a number and then rearranging it.
|
||||
if len(data.Fields) > 1 {
|
||||
name, ok := data.Fields[1].(string)
|
||||
if ok {
|
||||
if _, err := ParseNumber(data.Fields[0]); err == nil {
|
||||
fields := []interface{}{data.Fields[0]}
|
||||
fields = append(fields, data.Fields[2:]...)
|
||||
return strings.ToUpper(name), fields, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IMAP commands are formatted like this:
|
||||
// [name] [...]
|
||||
name, ok = data.Fields[0].(string)
|
||||
if !ok {
|
||||
err = errors.New("Response tag is not an atom")
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "+" {
|
||||
if err := r.ReadSp(); err != nil {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
res := &ContinuationResp{}
|
||||
res.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = res
|
||||
return
|
||||
}
|
||||
|
||||
if err = r.ReadSp(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Can be either data or status
|
||||
// Try to parse a status
|
||||
isStatus := false
|
||||
var fields []interface{}
|
||||
|
||||
if atom, err = r.ReadAtom(); err == nil {
|
||||
fields = append(fields, atom)
|
||||
|
||||
if err = r.ReadSp(); err == nil {
|
||||
if name, ok := atom.(string); ok {
|
||||
status := StatusRespType(name)
|
||||
if status == StatusOk || status == StatusNo || status == StatusBad || status == StatusPreauth || status == StatusBye {
|
||||
isStatus = true
|
||||
|
||||
res := &StatusResp{
|
||||
Tag: tag,
|
||||
Type: status,
|
||||
}
|
||||
|
||||
var char rune
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
r.UnreadRune()
|
||||
|
||||
if char == '[' {
|
||||
// Contains code & arguments
|
||||
res.Code, res.Arguments, err = r.ReadRespCode()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.Info, err = r.ReadInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = res
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
} else {
|
||||
r.UnreadRune()
|
||||
}
|
||||
|
||||
if !isStatus {
|
||||
// Not a status so it's data
|
||||
res := &Resp{Tag: tag}
|
||||
|
||||
var remaining []interface{}
|
||||
remaining, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res.Fields = append(fields, remaining...)
|
||||
out = res
|
||||
}
|
||||
|
||||
return
|
||||
return strings.ToUpper(name), data.Fields[1:], true
|
||||
}
|
||||
|
60
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
60
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
@@ -14,59 +14,45 @@ type Authenticate struct {
|
||||
Writer *imap.Writer
|
||||
}
|
||||
|
||||
func (r *Authenticate) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
w := r.Writer
|
||||
|
||||
// Cancel auth if an error occurs
|
||||
defer (func() {
|
||||
if err != nil {
|
||||
w.Write([]byte("*\r\n"))
|
||||
w.Flush()
|
||||
func (r *Authenticate) writeLine(l string) error {
|
||||
if _, err := r.Writer.Write([]byte(l + "\r\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Writer.Flush()
|
||||
}
|
||||
})()
|
||||
|
||||
for h := range hdlr {
|
||||
cont, ok := h.Resp.(*imap.ContinuationResp)
|
||||
func (r *Authenticate) cancel() error {
|
||||
return r.writeLine("*")
|
||||
}
|
||||
|
||||
func (r *Authenticate) Handle(resp imap.Resp) error {
|
||||
cont, ok := resp.(*imap.ContinuationReq)
|
||||
if !ok {
|
||||
h.Reject()
|
||||
continue
|
||||
return ErrUnhandled
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
// Empty challenge, send initial response as stated in RFC 2222 section 5.1
|
||||
if cont.Info == "" && r.InitialResponse != nil {
|
||||
encoded := base64.StdEncoding.EncodeToString(r.InitialResponse)
|
||||
if _, err = w.Write([]byte(encoded + "\r\n")); err != nil {
|
||||
return
|
||||
if err := r.writeLine(encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.InitialResponse = nil
|
||||
continue
|
||||
return nil
|
||||
}
|
||||
|
||||
var challenge []byte
|
||||
challenge, err = base64.StdEncoding.DecodeString(cont.Info)
|
||||
challenge, err := base64.StdEncoding.DecodeString(cont.Info)
|
||||
if err != nil {
|
||||
return
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
var res []byte
|
||||
res, err = r.Mechanism.Next(challenge)
|
||||
reply, err := r.Mechanism.Next(challenge)
|
||||
if err != nil {
|
||||
return
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(res)
|
||||
if _, err = w.Write([]byte(encoded + "\r\n")); err != nil {
|
||||
return
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
encoded := base64.StdEncoding.EncodeToString(reply)
|
||||
return r.writeLine(encoded)
|
||||
}
|
||||
|
23
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
23
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
@@ -10,30 +10,11 @@ type Capability struct {
|
||||
Caps []string
|
||||
}
|
||||
|
||||
func (r *Capability) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
for h := range hdlr {
|
||||
caps, ok := h.AcceptNamedResp(imap.Capability)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
r.Caps = make([]string, len(caps))
|
||||
for i, c := range caps {
|
||||
r.Caps[i], _ = c.(string)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Capability) WriteTo(w *imap.Writer) error {
|
||||
fields := []interface{}{imap.Capability}
|
||||
fields := []interface{}{"CAPABILITY"}
|
||||
for _, cap := range r.Caps {
|
||||
fields = append(fields, cap)
|
||||
}
|
||||
|
||||
res := &imap.Resp{Fields: fields}
|
||||
return res.WriteTo(w)
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
|
36
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
36
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
@@ -4,39 +4,37 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const expungeName = "EXPUNGE"
|
||||
|
||||
// An EXPUNGE response.
|
||||
// See RFC 3501 section 7.4.1
|
||||
type Expunge struct {
|
||||
SeqNums chan uint32
|
||||
}
|
||||
|
||||
func (r *Expunge) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.SeqNums)
|
||||
|
||||
for h := range hdlr {
|
||||
res, ok := h.Resp.(*imap.Resp)
|
||||
if !ok || len(res.Fields) < 3 {
|
||||
h.Reject()
|
||||
continue
|
||||
func (r *Expunge) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != expungeName {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
seqNum, err := imap.ParseNumber(fields[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name, ok := res.Fields[1].(string); !ok || name != imap.Expunge {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
seqNum, _ := imap.ParseNumber(res.Fields[0])
|
||||
r.SeqNums <- seqNum
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Expunge) WriteTo(w *imap.Writer) error {
|
||||
for seqNum := range r.SeqNums {
|
||||
res := imap.NewUntaggedResp([]interface{}{seqNum, imap.Expunge})
|
||||
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp([]interface{}{seqNum, expungeName})
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
44
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
44
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
@@ -4,49 +4,41 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const fetchName = "FETCH"
|
||||
|
||||
// A FETCH response.
|
||||
// See RFC 3501 section 7.4.2
|
||||
type Fetch struct {
|
||||
Messages chan *imap.Message
|
||||
}
|
||||
|
||||
func (r *Fetch) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.Messages)
|
||||
|
||||
for h := range hdlr {
|
||||
res, ok := h.Resp.(*imap.Resp)
|
||||
if !ok || len(res.Fields) < 3 {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
if name, ok := res.Fields[1].(string); !ok || name != imap.Fetch {
|
||||
h.Reject()
|
||||
continue
|
||||
}
|
||||
h.Accept()
|
||||
|
||||
seqNum, _ := imap.ParseNumber(res.Fields[0])
|
||||
fields, _ := res.Fields[2].([]interface{})
|
||||
|
||||
msg := &imap.Message{
|
||||
SeqNum: seqNum,
|
||||
func (r *Fetch) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != fetchName {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 1 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
if err := msg.Parse(fields); err != nil {
|
||||
seqNum, err := imap.ParseNumber(fields[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgFields, _ := fields[1].([]interface{})
|
||||
msg := &imap.Message{SeqNum: seqNum}
|
||||
if err := msg.Parse(msgFields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Messages <- msg
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Fetch) WriteTo(w *imap.Writer) error {
|
||||
for msg := range r.Messages {
|
||||
res := imap.NewUntaggedResp([]interface{}{msg.SeqNum, imap.Fetch, msg.Format()})
|
||||
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp([]interface{}{msg.SeqNum, fetchName, msg.Format()})
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
32
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
32
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
@@ -4,6 +4,11 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const (
|
||||
listName = "LIST"
|
||||
lsubName = "LSUB"
|
||||
)
|
||||
|
||||
// A LIST response.
|
||||
// If Subscribed is set to true, LSUB will be used instead.
|
||||
// See RFC 3501 section 7.2.2
|
||||
@@ -14,21 +19,16 @@ type List struct {
|
||||
|
||||
func (r *List) Name() string {
|
||||
if r.Subscribed {
|
||||
return imap.Lsub
|
||||
return lsubName
|
||||
} else {
|
||||
return imap.List
|
||||
return listName
|
||||
}
|
||||
}
|
||||
|
||||
func (r *List) HandleFrom(hdlr imap.RespHandler) error {
|
||||
defer close(r.Mailboxes)
|
||||
|
||||
name := r.Name()
|
||||
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(name)
|
||||
if !ok {
|
||||
continue
|
||||
func (r *List) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != r.Name() {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
mbox := &imap.MailboxInfo{}
|
||||
@@ -37,20 +37,18 @@ func (r *List) HandleFrom(hdlr imap.RespHandler) error {
|
||||
}
|
||||
|
||||
r.Mailboxes <- mbox
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *List) WriteTo(w *imap.Writer) error {
|
||||
name := r.Name()
|
||||
respName := r.Name()
|
||||
|
||||
for mbox := range r.Mailboxes {
|
||||
fields := []interface{}{name}
|
||||
fields := []interface{}{respName}
|
||||
fields = append(fields, mbox.Format()...)
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
26
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
26
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
@@ -1,2 +1,28 @@
|
||||
// IMAP responses defined in RFC 3501.
|
||||
package responses
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// ErrUnhandled is used when a response hasn't been handled.
|
||||
var ErrUnhandled = errors.New("imap: unhandled response")
|
||||
|
||||
var errNotEnoughFields = errors.New("imap: not enough fields in response")
|
||||
|
||||
// Handler handles responses.
|
||||
type Handler interface {
|
||||
// Handle processes a response. If the response cannot be processed,
|
||||
// ErrUnhandledResp must be returned.
|
||||
Handle(resp imap.Resp) error
|
||||
}
|
||||
|
||||
// HandlerFunc is a function that handles responses.
|
||||
type HandlerFunc func(resp imap.Resp) error
|
||||
|
||||
// Handle implements Handler.
|
||||
func (f HandlerFunc) Handle(resp imap.Resp) error {
|
||||
return f(resp)
|
||||
}
|
||||
|
28
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
28
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
@@ -4,34 +4,38 @@ import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
const searchName = "SEARCH"
|
||||
|
||||
// A SEARCH response.
|
||||
// See RFC 3501 section 7.2.5
|
||||
type Search struct {
|
||||
Ids []uint32
|
||||
}
|
||||
|
||||
func (r *Search) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(imap.Search)
|
||||
if !ok {
|
||||
continue
|
||||
func (r *Search) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != searchName {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
for _, f := range fields {
|
||||
id, _ := imap.ParseNumber(f)
|
||||
r.Ids = append(r.Ids, id)
|
||||
r.Ids = make([]uint32, len(fields))
|
||||
for i, f := range fields {
|
||||
if id, err := imap.ParseNumber(f); err != nil {
|
||||
return err
|
||||
} else {
|
||||
r.Ids[i] = id
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Search) WriteTo(w *imap.Writer) (err error) {
|
||||
fields := []interface{}{imap.Search}
|
||||
fields := []interface{}{searchName}
|
||||
for _, id := range r.Ids {
|
||||
fields = append(fields, id)
|
||||
}
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
return res.WriteTo(w)
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
return resp.WriteTo(w)
|
||||
}
|
||||
|
167
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
167
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
@@ -11,135 +11,132 @@ type Select struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (r *Select) HandleFrom(hdlr imap.RespHandler) (err error) {
|
||||
func (r *Select) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{}
|
||||
r.Mailbox = &imap.MailboxStatus{Items: make(map[imap.StatusItem]interface{})}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
|
||||
mbox.Items = make(map[string]interface{})
|
||||
for h := range hdlr {
|
||||
switch res := h.Resp.(type) {
|
||||
case *imap.Resp:
|
||||
fields, ok := h.AcceptNamedResp(imap.MailboxFlags)
|
||||
if !ok {
|
||||
break
|
||||
switch resp := resp.(type) {
|
||||
case *imap.DataResp:
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != "FLAGS" {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 1 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
flags, _ := fields[0].([]interface{})
|
||||
mbox.Flags, _ = imap.ParseStringList(flags)
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxFlags] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case *imap.StatusResp:
|
||||
if len(res.Arguments) < 1 {
|
||||
h.Accepts <- false
|
||||
break
|
||||
if len(resp.Arguments) < 1 {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
accepted := true
|
||||
switch res.Code {
|
||||
case imap.MailboxUnseen:
|
||||
mbox.Unseen, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUnseen] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxPermanentFlags:
|
||||
flags, _ := res.Arguments[0].([]interface{})
|
||||
var item imap.StatusItem
|
||||
switch resp.Code {
|
||||
case "UNSEEN":
|
||||
mbox.UnseenSeqNum, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
case "PERMANENTFLAGS":
|
||||
flags, _ := resp.Arguments[0].([]interface{})
|
||||
mbox.PermanentFlags, _ = imap.ParseStringList(flags)
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxPermanentFlags] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxUidNext:
|
||||
mbox.UidNext, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUidNext] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case imap.MailboxUidValidity:
|
||||
mbox.UidValidity, _ = imap.ParseNumber(res.Arguments[0])
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[imap.MailboxUidValidity] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
case "UIDNEXT":
|
||||
mbox.UidNext, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
item = imap.StatusUidNext
|
||||
case "UIDVALIDITY":
|
||||
mbox.UidValidity, _ = imap.ParseNumber(resp.Arguments[0])
|
||||
item = imap.StatusUidValidity
|
||||
default:
|
||||
accepted = false
|
||||
}
|
||||
h.Accepts <- accepted
|
||||
}
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
return
|
||||
if item != "" {
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[item] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
}
|
||||
default:
|
||||
return ErrUnhandled
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Select) WriteTo(w *imap.Writer) (err error) {
|
||||
status := r.Mailbox
|
||||
func (r *Select) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
|
||||
for k := range r.Mailbox.Items {
|
||||
switch k {
|
||||
case imap.MailboxFlags:
|
||||
flags := make([]interface{}, len(status.Flags))
|
||||
for i, f := range status.Flags {
|
||||
flags[i] = f
|
||||
if mbox.Flags != nil {
|
||||
flags := make([]interface{}, len(mbox.Flags))
|
||||
for i, f := range mbox.Flags {
|
||||
flags[i] = imap.Atom(f)
|
||||
}
|
||||
res := imap.NewUntaggedResp([]interface{}{"FLAGS", flags})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.MailboxPermanentFlags:
|
||||
flags := make([]interface{}, len(status.PermanentFlags))
|
||||
for i, f := range status.PermanentFlags {
|
||||
flags[i] = f
|
||||
}
|
||||
|
||||
if mbox.PermanentFlags != nil {
|
||||
flags := make([]interface{}, len(mbox.PermanentFlags))
|
||||
for i, f := range mbox.PermanentFlags {
|
||||
flags[i] = imap.Atom(f)
|
||||
}
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodePermanentFlags,
|
||||
Arguments: []interface{}{flags},
|
||||
Info: "Flags permitted.",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.MailboxMessages:
|
||||
res := imap.NewUntaggedResp([]interface{}{status.Messages, "EXISTS"})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxRecent:
|
||||
res := imap.NewUntaggedResp([]interface{}{status.Recent, "RECENT"})
|
||||
if err = res.WriteTo(w); err != nil {
|
||||
return
|
||||
}
|
||||
case imap.MailboxUnseen:
|
||||
|
||||
if mbox.UnseenSeqNum > 0 {
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUnseen,
|
||||
Arguments: []interface{}{status.Unseen},
|
||||
Info: fmt.Sprintf("Message %d is first unseen", status.Unseen),
|
||||
Arguments: []interface{}{mbox.UnseenSeqNum},
|
||||
Info: fmt.Sprintf("Message %d is first unseen", mbox.UnseenSeqNum),
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.MailboxUidNext:
|
||||
}
|
||||
|
||||
for k := range r.Mailbox.Items {
|
||||
switch k {
|
||||
case imap.StatusMessages:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Messages, "EXISTS"})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusRecent:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Recent, "RECENT"})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusUidNext:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidNext,
|
||||
Arguments: []interface{}{status.UidNext},
|
||||
Arguments: []interface{}{mbox.UidNext},
|
||||
Info: "Predicted next UID",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.MailboxUidValidity:
|
||||
case imap.StatusUidValidity:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidValidity,
|
||||
Arguments: []interface{}{status.UidValidity},
|
||||
Arguments: []interface{}{mbox.UidValidity},
|
||||
Info: "UIDs valid",
|
||||
}
|
||||
if err = statusRes.WriteTo(w); err != nil {
|
||||
return
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
48
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
48
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
@@ -7,57 +7,47 @@ import (
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
const statusName = "STATUS"
|
||||
|
||||
// A STATUS response.
|
||||
// See RFC 3501 section 7.2.4
|
||||
type Status struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (r *Status) HandleFrom(hdlr imap.RespHandler) error {
|
||||
func (r *Status) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
mbox.Items = nil
|
||||
|
||||
for h := range hdlr {
|
||||
fields, ok := h.AcceptNamedResp(imap.Status)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(fields) < 2 {
|
||||
return errors.New("STATUS response expects two fields")
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != statusName {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 2 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
name, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("STATUS response expects a string as first argument")
|
||||
if name, err := imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
} else if name, err := utf7.Encoding.NewDecoder().String(name); err != nil {
|
||||
return err
|
||||
} else {
|
||||
mbox.Name = imap.CanonicalMailboxName(name)
|
||||
}
|
||||
mbox.Name, _ = utf7.Decoder.String(name)
|
||||
mbox.Name = imap.CanonicalMailboxName(mbox.Name)
|
||||
|
||||
var items []interface{}
|
||||
if items, ok = fields[1].([]interface{}); !ok {
|
||||
return errors.New("STATUS response expects a list as second argument")
|
||||
}
|
||||
|
||||
if err := mbox.Parse(items); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
mbox.Items = nil
|
||||
return mbox.Parse(items)
|
||||
}
|
||||
|
||||
func (r *Status) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
|
||||
fields := []interface{}{imap.Status, mbox.Name, mbox.Format()}
|
||||
|
||||
res := imap.NewUntaggedResp(fields)
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
name, _ := utf7.Encoding.NewEncoder().String(mbox.Name)
|
||||
fields := []interface{}{statusName, name, mbox.Format()}
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
|
9
vendor/github.com/emersion/go-imap/search.go
generated
vendored
9
vendor/github.com/emersion/go-imap/search.go
generated
vendored
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -43,8 +42,8 @@ func convertField(f interface{}, charsetReader func(io.Reader) io.Reader) string
|
||||
}
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
b := make([]byte, l.Len())
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
@@ -238,7 +237,7 @@ func (c *SearchCriteria) parseField(fields []interface{}, charsetReader func(io.
|
||||
case "UID":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if c.Uid, err = NewSeqSet(maybeString(f)); err != nil {
|
||||
} else if c.Uid, err = ParseSeqSet(maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "UNANSWERED", "UNDELETED", "UNDRAFT", "UNFLAGGED", "UNSEEN":
|
||||
@@ -251,7 +250,7 @@ func (c *SearchCriteria) parseField(fields []interface{}, charsetReader func(io.
|
||||
c.WithoutFlags = append(c.WithoutFlags, CanonicalFlag(maybeString(f)))
|
||||
}
|
||||
default: // Try to parse a sequence set
|
||||
if c.SeqNum, err = NewSeqSet(key); err != nil {
|
||||
if c.SeqNum, err = ParseSeqSet(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
5
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
5
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
@@ -122,8 +122,8 @@ type SeqSet struct {
|
||||
Set []Seq
|
||||
}
|
||||
|
||||
// NewSeqSet returns a new SeqSet instance after parsing the set string.
|
||||
func NewSeqSet(set string) (s *SeqSet, err error) {
|
||||
// ParseSeqSet returns a new SeqSet instance after parsing the set string.
|
||||
func ParseSeqSet(set string) (s *SeqSet, err error) {
|
||||
s = new(SeqSet)
|
||||
return s, s.Add(set)
|
||||
}
|
||||
@@ -268,7 +268,6 @@ func (s *SeqSet) insertAt(i int, v Seq) {
|
||||
s.Set = set
|
||||
}
|
||||
s.Set[i] = v
|
||||
return
|
||||
}
|
||||
|
||||
// search attempts to find the index of the sequence set value that contains q.
|
||||
|
4
vendor/github.com/emersion/go-imap/server/cmd_any.go
generated
vendored
4
vendor/github.com/emersion/go-imap/server/cmd_any.go
generated
vendored
@@ -24,7 +24,7 @@ func (cmd *Noop) Handle(conn Conn) error {
|
||||
ctx := conn.Context()
|
||||
if ctx.Mailbox != nil {
|
||||
// If a mailbox is selected, NOOP can be used to poll for server updates
|
||||
if mbox, ok := ctx.Mailbox.(backend.UpdaterMailbox); ok {
|
||||
if mbox, ok := ctx.Mailbox.(backend.MailboxPoller); ok {
|
||||
return mbox.Poll()
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ type Logout struct {
|
||||
|
||||
func (cmd *Logout) Handle(conn Conn) error {
|
||||
res := &imap.StatusResp{
|
||||
Type: imap.StatusBye,
|
||||
Type: imap.StatusRespBye,
|
||||
Info: "Closing connection",
|
||||
}
|
||||
|
||||
|
20
vendor/github.com/emersion/go-imap/server/cmd_auth.go
generated
vendored
20
vendor/github.com/emersion/go-imap/server/cmd_auth.go
generated
vendored
@@ -29,10 +29,9 @@ func (cmd *Select) Handle(conn Conn) error {
|
||||
return err
|
||||
}
|
||||
|
||||
items := []string{
|
||||
imap.MailboxFlags, imap.MailboxPermanentFlags,
|
||||
imap.MailboxMessages, imap.MailboxRecent, imap.MailboxUnseen,
|
||||
imap.MailboxUidNext, imap.MailboxUidValidity,
|
||||
items := []imap.StatusItem{
|
||||
imap.StatusMessages, imap.StatusRecent, imap.StatusUnseen,
|
||||
imap.StatusUidNext, imap.StatusUidValidity,
|
||||
}
|
||||
|
||||
status, err := mbox.Status(items)
|
||||
@@ -48,12 +47,12 @@ func (cmd *Select) Handle(conn Conn) error {
|
||||
return err
|
||||
}
|
||||
|
||||
code := imap.CodeReadWrite
|
||||
var code imap.StatusRespCode = imap.CodeReadWrite
|
||||
if ctx.MailboxReadOnly {
|
||||
code = imap.CodeReadOnly
|
||||
}
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
@@ -208,7 +207,7 @@ func (cmd *Status) Handle(conn Conn) error {
|
||||
}
|
||||
|
||||
// Only keep items thqat have been requested
|
||||
items := make(map[string]interface{})
|
||||
items := make(map[imap.StatusItem]interface{})
|
||||
for _, k := range cmd.Items {
|
||||
items[k] = status.Items[k]
|
||||
}
|
||||
@@ -231,7 +230,7 @@ func (cmd *Append) Handle(conn Conn) error {
|
||||
mbox, err := ctx.User.GetMailbox(cmd.Mailbox)
|
||||
if err == backend.ErrNoSuchMailbox {
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusNo,
|
||||
Type: imap.StatusRespNo,
|
||||
Code: imap.CodeTryCreate,
|
||||
Info: err.Error(),
|
||||
})
|
||||
@@ -246,10 +245,13 @@ func (cmd *Append) Handle(conn Conn) error {
|
||||
// If APPEND targets the currently selected mailbox, send an untagged EXISTS
|
||||
// Do this only if the backend doesn't send updates itself
|
||||
if conn.Server().Updates == nil && ctx.Mailbox != nil && ctx.Mailbox.Name() == mbox.Name() {
|
||||
status, err := mbox.Status([]string{imap.MailboxMessages})
|
||||
status, err := mbox.Status([]imap.StatusItem{imap.StatusMessages})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status.Flags = nil
|
||||
status.PermanentFlags = nil
|
||||
status.UnseenSeqNum = 0
|
||||
|
||||
res := &responses.Select{Mailbox: status}
|
||||
if err := conn.WriteResp(res); err != nil {
|
||||
|
4
vendor/github.com/emersion/go-imap/server/cmd_noauth.go
generated
vendored
4
vendor/github.com/emersion/go-imap/server/cmd_noauth.go
generated
vendored
@@ -36,7 +36,7 @@ func (cmd *StartTLS) Handle(conn Conn) error {
|
||||
// Send an OK status response to let the client know that the TLS handshake
|
||||
// can begin
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Info: "Begin TLS negotiation now",
|
||||
})
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func (cmd *StartTLS) Upgrade(conn Conn) error {
|
||||
|
||||
func afterAuthStatus(conn Conn) error {
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeCapability,
|
||||
Arguments: imap.FormatStringList(conn.Capabilities()),
|
||||
})
|
||||
|
28
vendor/github.com/emersion/go-imap/server/cmd_selected.go
generated
vendored
28
vendor/github.com/emersion/go-imap/server/cmd_selected.go
generated
vendored
@@ -2,7 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
@@ -53,12 +52,8 @@ func (cmd *Close) Handle(conn Conn) error {
|
||||
ctx.Mailbox = nil
|
||||
ctx.MailboxReadOnly = false
|
||||
|
||||
if err := mailbox.Expunge(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No need to send expunge updates here, since the mailbox is already unselected
|
||||
return nil
|
||||
return mailbox.Expunge()
|
||||
}
|
||||
|
||||
type Expunge struct {
|
||||
@@ -206,15 +201,10 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
return ErrMailboxReadOnly
|
||||
}
|
||||
|
||||
itemStr := cmd.Item
|
||||
silent := strings.HasSuffix(itemStr, imap.SilentOp)
|
||||
if silent {
|
||||
itemStr = strings.TrimSuffix(itemStr, imap.SilentOp)
|
||||
}
|
||||
item := imap.FlagsOp(itemStr)
|
||||
|
||||
if item != imap.SetFlags && item != imap.AddFlags && item != imap.RemoveFlags {
|
||||
return errors.New("Unsupported STORE operation")
|
||||
// Only flags operations are supported
|
||||
op, silent, err := imap.ParseFlagsOp(cmd.Item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagsList, ok := cmd.Value.([]interface{})
|
||||
@@ -233,7 +223,7 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
// from receiving them
|
||||
// TODO: find a better way to do this, without conn.silent
|
||||
*conn.silent() = silent
|
||||
err = ctx.Mailbox.UpdateMessagesFlags(uid, cmd.SeqSet, item, flags)
|
||||
err = ctx.Mailbox.UpdateMessagesFlags(uid, cmd.SeqSet, op, flags)
|
||||
*conn.silent() = false
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -244,7 +234,7 @@ func (cmd *Store) handle(uid bool, conn Conn) error {
|
||||
if conn.Server().Updates == nil && !silent {
|
||||
inner := &Fetch{}
|
||||
inner.SeqSet = cmd.SeqSet
|
||||
inner.Items = []string{"FLAGS"}
|
||||
inner.Items = []imap.FetchItem{imap.FetchFlags}
|
||||
if uid {
|
||||
inner.Items = append(inner.Items, "UID")
|
||||
}
|
||||
@@ -307,7 +297,7 @@ func (cmd *Uid) Handle(conn Conn) error {
|
||||
}
|
||||
|
||||
return ErrStatusResp(&imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Info: imap.Uid + " " + inner.Name + " completed",
|
||||
Type: imap.StatusRespOk,
|
||||
Info: "UID " + inner.Name + " completed",
|
||||
})
|
||||
}
|
||||
|
59
vendor/github.com/emersion/go-imap/server/conn.go
generated
vendored
59
vendor/github.com/emersion/go-imap/server/conn.go
generated
vendored
@@ -36,7 +36,7 @@ type Conn interface {
|
||||
|
||||
setTLSConn(*tls.Conn)
|
||||
silent() *bool // TODO: remove this
|
||||
serve() error
|
||||
serve(Conn) error
|
||||
commandHandler(cmd *imap.Command) (hdlr Handler, err error)
|
||||
}
|
||||
|
||||
@@ -52,17 +52,21 @@ type Context struct {
|
||||
MailboxReadOnly bool
|
||||
// Responses to send to the client.
|
||||
Responses chan<- imap.WriterTo
|
||||
// Closed when the client is logged out.
|
||||
LoggedOut <-chan struct{}
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
*imap.Conn
|
||||
|
||||
conn Conn // With extensions overrides
|
||||
s *Server
|
||||
ctx *Context
|
||||
l sync.Locker
|
||||
tlsConn *tls.Conn
|
||||
continues chan bool
|
||||
responses chan imap.WriterTo
|
||||
loggedOut chan struct{}
|
||||
silentVal bool
|
||||
}
|
||||
|
||||
@@ -73,6 +77,7 @@ func newConn(s *Server, c net.Conn) *conn {
|
||||
w := imap.NewWriter(nil)
|
||||
|
||||
responses := make(chan imap.WriterTo)
|
||||
loggedOut := make(chan struct{})
|
||||
|
||||
tlsConn, _ := c.(*tls.Conn)
|
||||
|
||||
@@ -84,10 +89,12 @@ func newConn(s *Server, c net.Conn) *conn {
|
||||
ctx: &Context{
|
||||
State: imap.ConnectingState,
|
||||
Responses: responses,
|
||||
LoggedOut: loggedOut,
|
||||
},
|
||||
tlsConn: tlsConn,
|
||||
continues: continues,
|
||||
responses: responses,
|
||||
loggedOut: loggedOut,
|
||||
}
|
||||
|
||||
if s.Debug != nil {
|
||||
@@ -149,14 +156,7 @@ func (c *conn) Close() error {
|
||||
c.ctx.User.Logout()
|
||||
}
|
||||
|
||||
if err := c.Conn.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
close(c.continues)
|
||||
|
||||
c.ctx.State = imap.LogoutState
|
||||
return nil
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *conn) Capabilities() []string {
|
||||
@@ -187,8 +187,8 @@ func (c *conn) send() {
|
||||
// Send continuation requests
|
||||
go func() {
|
||||
for range c.continues {
|
||||
res := &imap.ContinuationResp{Info: "send literal"}
|
||||
if err := res.WriteTo(c.Writer); err != nil {
|
||||
resp := &imap.ContinuationReq{Info: "send literal"}
|
||||
if err := resp.WriteTo(c.Writer); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot send continuation request: ", err)
|
||||
} else if err := c.Writer.Flush(); err != nil {
|
||||
c.Server().ErrorLog.Println("cannot flush connection: ", err)
|
||||
@@ -199,8 +199,8 @@ func (c *conn) send() {
|
||||
// Send responses
|
||||
for {
|
||||
// Get a response that needs to be sent
|
||||
res := <-c.responses
|
||||
|
||||
select {
|
||||
case res := <-c.responses:
|
||||
// Request to send the response
|
||||
c.l.Lock()
|
||||
|
||||
@@ -212,6 +212,9 @@ func (c *conn) send() {
|
||||
}
|
||||
|
||||
c.l.Unlock()
|
||||
case <-c.loggedOut:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +228,7 @@ func (c *conn) greet() error {
|
||||
}
|
||||
|
||||
greeting := &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeCapability,
|
||||
Arguments: args,
|
||||
Info: "IMAP4rev1 Service Ready",
|
||||
@@ -262,7 +265,15 @@ func (c *conn) silent() *bool {
|
||||
return &c.silentVal
|
||||
}
|
||||
|
||||
func (c *conn) serve() error {
|
||||
func (c *conn) serve(conn Conn) error {
|
||||
c.conn = conn
|
||||
|
||||
defer func() {
|
||||
c.ctx.State = imap.LogoutState
|
||||
close(c.continues)
|
||||
close(c.loggedOut)
|
||||
}()
|
||||
|
||||
// Send greeting
|
||||
if err := c.greet(); err != nil {
|
||||
return err
|
||||
@@ -286,7 +297,7 @@ func (c *conn) serve() error {
|
||||
if err != nil {
|
||||
if imap.IsParseError(err) {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
} else {
|
||||
@@ -298,7 +309,7 @@ func (c *conn) serve() error {
|
||||
if err := cmd.Parse(fields); err != nil {
|
||||
res = &imap.StatusResp{
|
||||
Tag: cmd.Tag,
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
} else {
|
||||
@@ -307,7 +318,7 @@ func (c *conn) serve() error {
|
||||
if err != nil {
|
||||
res = &imap.StatusResp{
|
||||
Tag: cmd.Tag,
|
||||
Type: imap.StatusBad,
|
||||
Type: imap.StatusRespBad,
|
||||
Info: err.Error(),
|
||||
}
|
||||
}
|
||||
@@ -323,8 +334,8 @@ func (c *conn) serve() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if up != nil && res.Type == imap.StatusOk {
|
||||
if err := up.Upgrade(c); err != nil {
|
||||
if up != nil && res.Type == imap.StatusRespOk {
|
||||
if err := up.Upgrade(c.conn); err != nil {
|
||||
c.s.ErrorLog.Println("cannot upgrade connection:", err)
|
||||
return err
|
||||
}
|
||||
@@ -356,24 +367,24 @@ func (c *conn) handleCommand(cmd *imap.Command) (res *imap.StatusResp, up Upgrad
|
||||
c.l.Unlock()
|
||||
defer c.l.Lock()
|
||||
|
||||
hdlrErr := hdlr.Handle(c)
|
||||
hdlrErr := hdlr.Handle(c.conn)
|
||||
if statusErr, ok := hdlrErr.(*errStatusResp); ok {
|
||||
res = statusErr.resp
|
||||
} else if hdlrErr != nil {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusNo,
|
||||
Type: imap.StatusRespNo,
|
||||
Info: hdlrErr.Error(),
|
||||
}
|
||||
} else {
|
||||
res = &imap.StatusResp{
|
||||
Type: imap.StatusOk,
|
||||
Type: imap.StatusRespOk,
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
res.Tag = cmd.Tag
|
||||
|
||||
if res.Type == imap.StatusOk && res.Info == "" {
|
||||
if res.Type == imap.StatusRespOk && res.Info == "" {
|
||||
res.Info = cmd.Name + " completed"
|
||||
}
|
||||
}
|
||||
|
112
vendor/github.com/emersion/go-imap/server/server.go
generated
vendored
112
vendor/github.com/emersion/go-imap/server/server.go
generated
vendored
@@ -45,7 +45,7 @@ type Upgrader interface {
|
||||
type HandlerFactory func() Handler
|
||||
|
||||
// A function that creates SASL servers.
|
||||
type SaslServerFactory func(conn Conn) sasl.Server
|
||||
type SASLServerFactory func(conn Conn) sasl.Server
|
||||
|
||||
// An IMAP extension.
|
||||
type Extension interface {
|
||||
@@ -94,7 +94,7 @@ type Server struct {
|
||||
conns map[Conn]struct{}
|
||||
|
||||
commands map[string]HandlerFactory
|
||||
auths map[string]SaslServerFactory
|
||||
auths map[string]SASLServerFactory
|
||||
extensions []Extension
|
||||
|
||||
// TCP address to listen on.
|
||||
@@ -104,7 +104,7 @@ type Server struct {
|
||||
// This server's backend.
|
||||
Backend backend.Backend
|
||||
// Backend updates that will be sent to connected clients.
|
||||
Updates <-chan interface{}
|
||||
Updates <-chan backend.Update
|
||||
// Automatically logout clients after a duration. To do not logout users
|
||||
// automatically, set this to zero. The duration MUST be at least
|
||||
// MinAutoLogout (as stated in RFC 3501 section 5.4).
|
||||
@@ -132,7 +132,7 @@ func New(bkd backend.Backend) *Server {
|
||||
ErrorLog: log.New(os.Stderr, "imap/server: ", log.LstdFlags),
|
||||
}
|
||||
|
||||
s.auths = map[string]SaslServerFactory{
|
||||
s.auths = map[string]SASLServerFactory{
|
||||
sasl.Plain: func(conn Conn) sasl.Server {
|
||||
return sasl.NewPlainServer(func(identity, username, password string) error {
|
||||
if identity != "" && identity != username {
|
||||
@@ -153,42 +153,42 @@ func New(bkd backend.Backend) *Server {
|
||||
}
|
||||
|
||||
s.commands = map[string]HandlerFactory{
|
||||
imap.Noop: func() Handler { return &Noop{} },
|
||||
imap.Capability: func() Handler { return &Capability{} },
|
||||
imap.Logout: func() Handler { return &Logout{} },
|
||||
"NOOP": func() Handler { return &Noop{} },
|
||||
"CAPABILITY": func() Handler { return &Capability{} },
|
||||
"LOGOUT": func() Handler { return &Logout{} },
|
||||
|
||||
imap.StartTLS: func() Handler { return &StartTLS{} },
|
||||
imap.Login: func() Handler { return &Login{} },
|
||||
imap.Authenticate: func() Handler { return &Authenticate{} },
|
||||
"STARTTLS": func() Handler { return &StartTLS{} },
|
||||
"LOGIN": func() Handler { return &Login{} },
|
||||
"AUTHENTICATE": func() Handler { return &Authenticate{} },
|
||||
|
||||
imap.Select: func() Handler { return &Select{} },
|
||||
imap.Examine: func() Handler {
|
||||
"SELECT": func() Handler { return &Select{} },
|
||||
"EXAMINE": func() Handler {
|
||||
hdlr := &Select{}
|
||||
hdlr.ReadOnly = true
|
||||
return hdlr
|
||||
},
|
||||
imap.Create: func() Handler { return &Create{} },
|
||||
imap.Delete: func() Handler { return &Delete{} },
|
||||
imap.Rename: func() Handler { return &Rename{} },
|
||||
imap.Subscribe: func() Handler { return &Subscribe{} },
|
||||
imap.Unsubscribe: func() Handler { return &Unsubscribe{} },
|
||||
imap.List: func() Handler { return &List{} },
|
||||
imap.Lsub: func() Handler {
|
||||
"CREATE": func() Handler { return &Create{} },
|
||||
"DELETE": func() Handler { return &Delete{} },
|
||||
"RENAME": func() Handler { return &Rename{} },
|
||||
"SUBSCRIBE": func() Handler { return &Subscribe{} },
|
||||
"UNSUBSCRIBE": func() Handler { return &Unsubscribe{} },
|
||||
"LIST": func() Handler { return &List{} },
|
||||
"LSUB": func() Handler {
|
||||
hdlr := &List{}
|
||||
hdlr.Subscribed = true
|
||||
return hdlr
|
||||
},
|
||||
imap.Status: func() Handler { return &Status{} },
|
||||
imap.Append: func() Handler { return &Append{} },
|
||||
"STATUS": func() Handler { return &Status{} },
|
||||
"APPEND": func() Handler { return &Append{} },
|
||||
|
||||
imap.Check: func() Handler { return &Check{} },
|
||||
imap.Close: func() Handler { return &Close{} },
|
||||
imap.Expunge: func() Handler { return &Expunge{} },
|
||||
imap.Search: func() Handler { return &Search{} },
|
||||
imap.Fetch: func() Handler { return &Fetch{} },
|
||||
imap.Store: func() Handler { return &Store{} },
|
||||
imap.Copy: func() Handler { return &Copy{} },
|
||||
imap.Uid: func() Handler { return &Uid{} },
|
||||
"CHECK": func() Handler { return &Check{} },
|
||||
"CLOSE": func() Handler { return &Close{} },
|
||||
"EXPUNGE": func() Handler { return &Expunge{} },
|
||||
"SEARCH": func() Handler { return &Search{} },
|
||||
"FETCH": func() Handler { return &Fetch{} },
|
||||
"STORE": func() Handler { return &Store{} },
|
||||
"COPY": func() Handler { return &Copy{} },
|
||||
"UID": func() Handler { return &Uid{} },
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -207,7 +207,11 @@ func (s *Server) Serve(l net.Listener) error {
|
||||
delete(s.listeners, l)
|
||||
}()
|
||||
|
||||
updater, ok := s.Backend.(backend.BackendUpdater)
|
||||
if ok {
|
||||
s.Updates = updater.Updates()
|
||||
go s.listenUpdates()
|
||||
}
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
@@ -274,7 +278,7 @@ func (s *Server) serveConn(conn Conn) error {
|
||||
delete(s.conns, conn)
|
||||
}()
|
||||
|
||||
return conn.serve()
|
||||
return conn.serve(conn)
|
||||
}
|
||||
|
||||
// Get a command handler factory for the provided command name.
|
||||
@@ -289,49 +293,32 @@ func (s *Server) Command(name string) HandlerFactory {
|
||||
return s.commands[name]
|
||||
}
|
||||
|
||||
func (s *Server) listenUpdates() (err error) {
|
||||
updater, ok := s.Backend.(backend.Updater)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
s.Updates = updater.Updates()
|
||||
|
||||
func (s *Server) listenUpdates() {
|
||||
for {
|
||||
item := <-s.Updates
|
||||
update := <-s.Updates
|
||||
|
||||
var (
|
||||
update *backend.Update
|
||||
res imap.WriterTo
|
||||
)
|
||||
|
||||
switch item := item.(type) {
|
||||
var res imap.WriterTo
|
||||
switch update := update.(type) {
|
||||
case *backend.StatusUpdate:
|
||||
update = &item.Update
|
||||
res = item.StatusResp
|
||||
res = update.StatusResp
|
||||
case *backend.MailboxUpdate:
|
||||
update = &item.Update
|
||||
res = &responses.Select{Mailbox: item.MailboxStatus}
|
||||
res = &responses.Select{Mailbox: update.MailboxStatus}
|
||||
case *backend.MessageUpdate:
|
||||
update = &item.Update
|
||||
|
||||
ch := make(chan *imap.Message, 1)
|
||||
ch <- item.Message
|
||||
ch <- update.Message
|
||||
close(ch)
|
||||
|
||||
res = &responses.Fetch{Messages: ch}
|
||||
case *backend.ExpungeUpdate:
|
||||
update = &item.Update
|
||||
|
||||
ch := make(chan uint32, 1)
|
||||
ch <- item.SeqNum
|
||||
ch <- update.SeqNum
|
||||
close(ch)
|
||||
|
||||
res = &responses.Expunge{SeqNums: ch}
|
||||
default:
|
||||
s.ErrorLog.Printf("unhandled update: %T\n", item)
|
||||
s.ErrorLog.Printf("unhandled update: %T\n", update)
|
||||
}
|
||||
|
||||
if update == nil || res == nil {
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -341,10 +328,10 @@ func (s *Server) listenUpdates() (err error) {
|
||||
for conn := range s.conns {
|
||||
ctx := conn.Context()
|
||||
|
||||
if update.Username != "" && (ctx.User == nil || ctx.User.Username() != update.Username) {
|
||||
if update.Username() != "" && (ctx.User == nil || ctx.User.Username() != update.Username()) {
|
||||
continue
|
||||
}
|
||||
if update.Mailbox != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox) {
|
||||
if update.Mailbox() != "" && (ctx.Mailbox == nil || ctx.Mailbox.Name() != update.Mailbox()) {
|
||||
continue
|
||||
}
|
||||
if *conn.silent() {
|
||||
@@ -374,12 +361,11 @@ func (s *Server) listenUpdates() (err error) {
|
||||
for done := 0; done < wait; done++ {
|
||||
<-sends
|
||||
}
|
||||
close(sends)
|
||||
|
||||
backend.DoneUpdate(update)
|
||||
close(update.Done())
|
||||
}()
|
||||
} else {
|
||||
backend.DoneUpdate(update)
|
||||
close(update.Done())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,6 +407,6 @@ func (s *Server) Enable(extensions ...Extension) {
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (s *Server) EnableAuth(name string, f SaslServerFactory) {
|
||||
func (s *Server) EnableAuth(name string, f SASLServerFactory) {
|
||||
s.auths[name] = f
|
||||
}
|
||||
|
26
vendor/github.com/emersion/go-imap/status.go
generated
vendored
26
vendor/github.com/emersion/go-imap/status.go
generated
vendored
@@ -12,13 +12,13 @@ const (
|
||||
// The OK response indicates an information message from the server. When
|
||||
// tagged, it indicates successful completion of the associated command.
|
||||
// The untagged form indicates an information-only message.
|
||||
StatusOk StatusRespType = "OK"
|
||||
StatusRespOk StatusRespType = "OK"
|
||||
|
||||
// The NO response indicates an operational error message from the
|
||||
// server. When tagged, it indicates unsuccessful completion of the
|
||||
// associated command. The untagged form indicates a warning; the
|
||||
// command can still complete successfully.
|
||||
StatusNo = "NO"
|
||||
StatusRespNo = "NO"
|
||||
|
||||
// The BAD response indicates an error message from the server. When
|
||||
// tagged, it reports a protocol-level error in the client's command;
|
||||
@@ -26,22 +26,24 @@ const (
|
||||
// form indicates a protocol-level error for which the associated
|
||||
// command can not be determined; it can also indicate an internal
|
||||
// server failure.
|
||||
StatusBad = "BAD"
|
||||
StatusRespBad = "BAD"
|
||||
|
||||
// The PREAUTH response is always untagged, and is one of three
|
||||
// possible greetings at connection startup. It indicates that the
|
||||
// connection has already been authenticated by external means; thus
|
||||
// no LOGIN command is needed.
|
||||
StatusPreauth = "PREAUTH"
|
||||
StatusRespPreauth = "PREAUTH"
|
||||
|
||||
// The BYE response is always untagged, and indicates that the server
|
||||
// is about to close the connection.
|
||||
StatusBye = "BYE"
|
||||
StatusRespBye = "BYE"
|
||||
)
|
||||
|
||||
type StatusRespCode string
|
||||
|
||||
// Status response codes defined in RFC 3501 section 7.1.
|
||||
const (
|
||||
CodeAlert = "ALERT"
|
||||
CodeAlert StatusRespCode = "ALERT"
|
||||
CodeBadCharset = "BADCHARSET"
|
||||
CodeCapability = "CAPABILITY"
|
||||
CodeParse = "PARSE"
|
||||
@@ -59,21 +61,19 @@ const (
|
||||
type StatusResp struct {
|
||||
// The response tag. If empty, it defaults to *.
|
||||
Tag string
|
||||
|
||||
// The status type.
|
||||
Type StatusRespType
|
||||
|
||||
// The status code.
|
||||
// See https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml
|
||||
Code string
|
||||
|
||||
Code StatusRespCode
|
||||
// Arguments provided with the status code.
|
||||
Arguments []interface{}
|
||||
|
||||
// The status info.
|
||||
Info string
|
||||
}
|
||||
|
||||
func (r *StatusResp) resp() {}
|
||||
|
||||
// If this status is NO or BAD, returns an error with the status info.
|
||||
// Otherwise, returns nil.
|
||||
func (r *StatusResp) Err() error {
|
||||
@@ -82,14 +82,14 @@ func (r *StatusResp) Err() error {
|
||||
return errors.New("imap: connection closed during command execution")
|
||||
}
|
||||
|
||||
if r.Type == StatusNo || r.Type == StatusBad {
|
||||
if r.Type == StatusRespNo || r.Type == StatusRespBad {
|
||||
return errors.New(r.Info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *StatusResp) WriteTo(w *Writer) error {
|
||||
tag := r.Tag
|
||||
tag := Atom(r.Tag)
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
}
|
||||
|
24
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
24
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
@@ -5,15 +5,11 @@ import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var ErrBadUtf7 = errors.New("bad utf-7 encoding")
|
||||
|
||||
var Decoder = &encoding.Decoder{
|
||||
Transformer: &decoder{true},
|
||||
}
|
||||
// ErrInvalidUTF7 means that a transformer encountered invalid UTF-7.
|
||||
var ErrInvalidUTF7 = errors.New("utf7: invalid UTF-7")
|
||||
|
||||
type decoder struct {
|
||||
ascii bool
|
||||
@@ -24,7 +20,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
ch := src[i]
|
||||
|
||||
if ch < min || ch > max { // Illegal code point in ASCII mode
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,14 +43,14 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
start := i + 1
|
||||
for i++; i < len(src) && src[i] != '-'; i++ {
|
||||
if src[i] == '\r' || src[i] == '\n' { // base64 package ignores CR and LF
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(src) { // Implicit shift ("&...")
|
||||
if atEOF {
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
} else {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
@@ -67,7 +63,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
d.ascii = true
|
||||
} else { // Control or non-ASCII code points in base64
|
||||
if !d.ascii { // Null shift ("&...-&...-")
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,7 +72,7 @@ func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err er
|
||||
}
|
||||
|
||||
if len(b) == 0 { // Bad encoding
|
||||
err = ErrBadUtf7
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
@@ -116,16 +112,16 @@ func decode(b64 []byte) []byte {
|
||||
if n := len(b64); b64[n-1] == '=' {
|
||||
return nil
|
||||
} else if n&3 == 0 {
|
||||
b = make([]byte, enc.DecodedLen(n)*3)
|
||||
b = make([]byte, b64Enc.DecodedLen(n)*3)
|
||||
} else {
|
||||
n += 4 - n&3
|
||||
b = make([]byte, n+enc.DecodedLen(n)*3)
|
||||
b = make([]byte, n+b64Enc.DecodedLen(n)*3)
|
||||
copy(b[copy(b, b64):n], []byte("=="))
|
||||
b64, b = b[:n], b[n:]
|
||||
}
|
||||
|
||||
// Decode Base64 into the first 1/3rd of b
|
||||
n, err := enc.Decode(b, b64)
|
||||
n, err := b64Enc.Decode(b, b64)
|
||||
if err != nil || n&1 == 1 {
|
||||
return nil
|
||||
}
|
||||
|
9
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
9
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
@@ -4,14 +4,9 @@ import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var Encoder = &encoding.Encoder{
|
||||
Transformer: &encoder{},
|
||||
}
|
||||
|
||||
type encoder struct{}
|
||||
|
||||
func (e *encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
@@ -81,9 +76,9 @@ func encode(s []byte) []byte {
|
||||
}
|
||||
|
||||
// Encode as base64
|
||||
n := enc.EncodedLen(len(b)) + 2
|
||||
n := b64Enc.EncodedLen(len(b)) + 2
|
||||
b64 := make([]byte, n)
|
||||
enc.Encode(b64[1:], b)
|
||||
b64Enc.Encode(b64[1:], b)
|
||||
|
||||
// Strip padding
|
||||
n -= 2 - (len(b)+2)%3
|
||||
|
21
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
21
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
@@ -3,6 +3,8 @@ package utf7
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,4 +14,21 @@ const (
|
||||
repl = '\uFFFD' // Unicode replacement code point
|
||||
)
|
||||
|
||||
var enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
|
||||
var b64Enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,")
|
||||
|
||||
type enc struct{}
|
||||
|
||||
func (e enc) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{
|
||||
Transformer: &decoder{true},
|
||||
}
|
||||
}
|
||||
|
||||
func (e enc) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{
|
||||
Transformer: &encoder{},
|
||||
}
|
||||
}
|
||||
|
||||
// Encoding is the modified UTF-7 encoding.
|
||||
var Encoding encoding.Encoding = enc{}
|
||||
|
17
vendor/github.com/emersion/go-imap/write.go
generated
vendored
17
vendor/github.com/emersion/go-imap/write.go
generated
vendored
@@ -14,8 +14,12 @@ type flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
type (
|
||||
// A string that will be quoted.
|
||||
type Quoted string
|
||||
Quoted string
|
||||
// A raw atom.
|
||||
Atom string
|
||||
)
|
||||
|
||||
type WriterTo interface {
|
||||
WriteTo(w *Writer) error
|
||||
@@ -83,8 +87,7 @@ func (w *Writer) writeAstring(s string) error {
|
||||
return w.writeLiteral(bytes.NewBufferString(s))
|
||||
}
|
||||
|
||||
specials := string([]rune{dquote, listStart, listEnd, literalStart, sp})
|
||||
if strings.ToUpper(s) == nilAtom || s == "" || strings.ContainsAny(s, specials) {
|
||||
if strings.ToUpper(s) == nilAtom || s == "" || strings.ContainsAny(s, atomSpecials) {
|
||||
return w.writeQuoted(s)
|
||||
}
|
||||
|
||||
@@ -162,6 +165,8 @@ func (w *Writer) writeField(field interface{}) error {
|
||||
return w.writeAstring(field)
|
||||
case Quoted:
|
||||
return w.writeQuoted(string(field))
|
||||
case Atom:
|
||||
return w.writeAtom(string(field))
|
||||
case int:
|
||||
return w.writeNumber(uint32(field))
|
||||
case uint32:
|
||||
@@ -184,18 +189,18 @@ func (w *Writer) writeField(field interface{}) error {
|
||||
return w.writeString(field.String())
|
||||
case *BodySectionName:
|
||||
// Can contain spaces - that's why we don't just pass it as a string
|
||||
return w.writeString(field.String())
|
||||
return w.writeString(string(field.FetchItem()))
|
||||
}
|
||||
|
||||
return fmt.Errorf("imap: cannot format field: %v", field)
|
||||
}
|
||||
|
||||
func (w *Writer) writeRespCode(code string, args []interface{}) error {
|
||||
func (w *Writer) writeRespCode(code StatusRespCode, args []interface{}) error {
|
||||
if err := w.writeString(string(respCodeStart)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := []interface{}{code}
|
||||
fields := []interface{}{string(code)}
|
||||
fields = append(fields, args...)
|
||||
|
||||
if err := w.writeFields(fields); err != nil {
|
||||
|
24
vendor/github.com/emersion/go-message/.gitignore
generated
vendored
Normal file
24
vendor/github.com/emersion/go-message/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
5
vendor/github.com/emersion/go-message/.travis.yml
generated
vendored
Normal file
5
vendor/github.com/emersion/go-message/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.8
|
||||
script: bash <(curl -sL https://gist.github.com/emersion/49d4dda535497002639626bd9e16480c/raw/codecov-go.sh)
|
||||
after_script: bash <(curl -s https://codecov.io/bash)
|
21
vendor/github.com/emersion/go-message/LICENSE
generated
vendored
Normal file
21
vendor/github.com/emersion/go-message/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 emersion
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
22
vendor/github.com/emersion/go-message/README.md
generated
vendored
Normal file
22
vendor/github.com/emersion/go-message/README.md
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# go-message
|
||||
|
||||
[](https://godoc.org/github.com/emersion/go-message)
|
||||
[](https://travis-ci.org/emersion/go-message)
|
||||
[](https://codecov.io/gh/emersion/go-message)
|
||||
[](https://goreportcard.com/report/github.com/emersion/go-message)
|
||||
[](https://github.com/emersion/stability-badges#unstable)
|
||||
|
||||
A Go library for the Internet Message Format. It implements:
|
||||
* [RFC 5322](https://tools.ietf.org/html/rfc5322): Internet Message Format
|
||||
* [RFC 2045](https://tools.ietf.org/html/rfc2045), [RFC 2046](https://tools.ietf.org/html/rfc2046) and [RFC 2047](https://tools.ietf.org/html/rfc2047): Multipurpose Internet Mail Extensions
|
||||
* [RFC 2183](https://tools.ietf.org/html/rfc2183): Content-Disposition Header Field
|
||||
|
||||
## Features
|
||||
|
||||
* Streaming API
|
||||
* Automatic encoding and charset handling
|
||||
* A [`mail`](https://godoc.org/github.com/emersion/go-message/mail) subpackage to read and write mail messages
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
57
vendor/github.com/emersion/go-message/charset/charset.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-message/charset/charset.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Package charset provides functions to decode and encode charsets.
|
||||
package charset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/traditionalchinese"
|
||||
)
|
||||
|
||||
var charsets = map[string]encoding.Encoding{
|
||||
"big5": traditionalchinese.Big5,
|
||||
"euc-jp": japanese.EUCJP,
|
||||
"gbk": simplifiedchinese.GBK,
|
||||
"gb2312": simplifiedchinese.GBK, // as GBK is a superset of HZGB2312, so just use GBK
|
||||
"gb18030": simplifiedchinese.GB18030, // GB18030 Use for parse QQ business mail message
|
||||
"iso-2022-jp": japanese.ISO2022JP,
|
||||
"iso-8859-1": charmap.ISO8859_1,
|
||||
"iso-8859-2": charmap.ISO8859_2,
|
||||
"iso-8859-3": charmap.ISO8859_3,
|
||||
"iso-8859-4": charmap.ISO8859_4,
|
||||
"iso-8859-9": charmap.ISO8859_9,
|
||||
"iso-8859-10": charmap.ISO8859_10,
|
||||
"iso-8859-13": charmap.ISO8859_13,
|
||||
"iso-8859-14": charmap.ISO8859_14,
|
||||
"iso-8859-15": charmap.ISO8859_15,
|
||||
"iso-8859-16": charmap.ISO8859_16,
|
||||
"koi8-r": charmap.KOI8R,
|
||||
"shift_jis": japanese.ShiftJIS,
|
||||
"windows-1250": charmap.Windows1250,
|
||||
"windows-1251": charmap.Windows1251,
|
||||
"windows-1252": charmap.Windows1252,
|
||||
}
|
||||
|
||||
// Reader returns an io.Reader that converts the provided charset to UTF-8.
|
||||
func Reader(charset string, input io.Reader) (io.Reader, error) {
|
||||
charset = strings.ToLower(charset)
|
||||
// "ascii" is not in the spec but is common
|
||||
if charset == "utf-8" || charset == "us-ascii" || charset == "ascii" {
|
||||
return input, nil
|
||||
}
|
||||
if enc, ok := charsets[charset]; ok {
|
||||
return enc.NewDecoder().Reader(input), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unhandled charset %q", charset)
|
||||
}
|
||||
|
||||
// RegisterEncoding registers an encoding. This is intended to be called from
|
||||
// the init function in packages that want to support additional charsets.
|
||||
func RegisterEncoding(name string, enc encoding.Encoding) {
|
||||
charsets[name] = enc
|
||||
}
|
22
vendor/github.com/emersion/go-message/charset/header.go
generated
vendored
Normal file
22
vendor/github.com/emersion/go-message/charset/header.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package charset
|
||||
|
||||
import (
|
||||
"mime"
|
||||
)
|
||||
|
||||
var wordDecoder = &mime.WordDecoder{CharsetReader: Reader}
|
||||
|
||||
// DecodeHeader decodes an internationalized header field. If it fails, it
|
||||
// returns the input string and the error.
|
||||
func DecodeHeader(s string) (string, error) {
|
||||
dec, err := wordDecoder.DecodeHeader(s)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// EncodeHeader encodes an internationalized header field.
|
||||
func EncodeHeader(s string) string {
|
||||
return mime.QEncoding.Encode("utf-8", s)
|
||||
}
|
47
vendor/github.com/emersion/go-message/encoding.go
generated
vendored
Normal file
47
vendor/github.com/emersion/go-message/encoding.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/quotedprintable"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-textwrapper"
|
||||
)
|
||||
|
||||
func encodingReader(enc string, r io.Reader) (io.Reader, error) {
|
||||
var dec io.Reader
|
||||
switch strings.ToLower(enc) {
|
||||
case "quoted-printable":
|
||||
dec = quotedprintable.NewReader(r)
|
||||
case "base64":
|
||||
dec = base64.NewDecoder(base64.StdEncoding, r)
|
||||
case "7bit", "8bit", "binary", "":
|
||||
dec = r
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled encoding %q", enc)
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
||||
func encodingWriter(enc string, w io.Writer) io.WriteCloser {
|
||||
var wc io.WriteCloser
|
||||
switch strings.ToLower(enc) {
|
||||
case "quoted-printable":
|
||||
wc = quotedprintable.NewWriter(w)
|
||||
case "base64":
|
||||
wc = base64.NewEncoder(base64.StdEncoding, textwrapper.NewRFC822(w))
|
||||
case "7bit", "8bit":
|
||||
wc = nopCloser{textwrapper.New(w, "\r\n", 1000)}
|
||||
default: // "binary"
|
||||
wc = nopCloser{w}
|
||||
}
|
||||
return wc
|
||||
}
|
132
vendor/github.com/emersion/go-message/entity.go
generated
vendored
Normal file
132
vendor/github.com/emersion/go-message/entity.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-message/charset"
|
||||
)
|
||||
|
||||
type unknownEncodingError struct {
|
||||
error
|
||||
}
|
||||
|
||||
// IsUnknownEncoding returns a boolean indicating whether the error is known to
|
||||
// report that the transfer encoding or the charset advertised by the entity is
|
||||
// unknown.
|
||||
func IsUnknownEncoding(err error) bool {
|
||||
_, ok := err.(unknownEncodingError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// An Entity is either a whole message or a one of the parts in the body of a
|
||||
// multipart entity.
|
||||
type Entity struct {
|
||||
Header Header // The entity's header.
|
||||
Body io.Reader // The decoded entity's body.
|
||||
|
||||
mediaType string
|
||||
mediaParams map[string]string
|
||||
}
|
||||
|
||||
// New makes a new message with the provided header and body. The entity's
|
||||
// transfer encoding and charset are automatically decoded to UTF-8.
|
||||
//
|
||||
// If the message uses an unknown transfer encoding or charset, New returns an
|
||||
// error that verifies IsUnknownEncoding, but also returns an Entity that can
|
||||
// be read.
|
||||
func New(header Header, body io.Reader) (*Entity, error) {
|
||||
var err error
|
||||
|
||||
enc := header.Get("Content-Transfer-Encoding")
|
||||
if decoded, encErr := encodingReader(enc, body); encErr != nil {
|
||||
err = unknownEncodingError{encErr}
|
||||
} else {
|
||||
body = decoded
|
||||
}
|
||||
|
||||
mediaType, mediaParams, _ := header.ContentType()
|
||||
if ch, ok := mediaParams["charset"]; ok {
|
||||
if converted, charsetErr := charset.Reader(ch, body); charsetErr != nil {
|
||||
err = unknownEncodingError{charsetErr}
|
||||
} else {
|
||||
body = converted
|
||||
}
|
||||
}
|
||||
|
||||
return &Entity{
|
||||
Header: header,
|
||||
Body: body,
|
||||
mediaType: mediaType,
|
||||
mediaParams: mediaParams,
|
||||
}, err
|
||||
}
|
||||
|
||||
// NewMultipart makes a new multipart message with the provided header and
|
||||
// parts. The Content-Type header must begin with "multipart/".
|
||||
//
|
||||
// If the message uses an unknown transfer encoding, NewMultipart returns an
|
||||
// error that verifies IsUnknownEncoding, but also returns an Entity that can
|
||||
// be read.
|
||||
func NewMultipart(header Header, parts []*Entity) (*Entity, error) {
|
||||
r := &multipartBody{
|
||||
header: header,
|
||||
parts: parts,
|
||||
}
|
||||
|
||||
return New(header, r)
|
||||
}
|
||||
|
||||
// Read reads a message from r. The message's encoding and charset are
|
||||
// automatically decoded to UTF-8. Note that this function only reads the
|
||||
// message header.
|
||||
//
|
||||
// If the message uses an unknown transfer encoding or charset, Read returns an
|
||||
// error that verifies IsUnknownEncoding, but also returns an Entity that can
|
||||
// be read.
|
||||
func Read(r io.Reader) (*Entity, error) {
|
||||
br := bufio.NewReader(r)
|
||||
h, err := textproto.NewReader(br).ReadMIMEHeader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return New(Header(h), br)
|
||||
}
|
||||
|
||||
// MultipartReader returns a MultipartReader that reads parts from this entity's
|
||||
// body. If this entity is not multipart, it returns nil.
|
||||
func (e *Entity) MultipartReader() MultipartReader {
|
||||
if !strings.HasPrefix(e.mediaType, "multipart/") {
|
||||
return nil
|
||||
}
|
||||
if mb, ok := e.Body.(*multipartBody); ok {
|
||||
return mb
|
||||
}
|
||||
return &multipartReader{multipart.NewReader(e.Body, e.mediaParams["boundary"])}
|
||||
}
|
||||
|
||||
// writeBodyTo writes this entity's body to w (without the header).
|
||||
func (e *Entity) writeBodyTo(w *Writer) error {
|
||||
var err error
|
||||
if mb, ok := e.Body.(*multipartBody); ok {
|
||||
err = mb.writeBodyTo(w)
|
||||
} else {
|
||||
_, err = io.Copy(w, e.Body)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteTo writes this entity's header and body to w.
|
||||
func (e *Entity) WriteTo(w io.Writer) error {
|
||||
ew, err := CreateWriter(w, e.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ew.Close()
|
||||
|
||||
return e.writeBodyTo(ew)
|
||||
}
|
154
vendor/github.com/emersion/go-message/header.go
generated
vendored
Normal file
154
vendor/github.com/emersion/go-message/header.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-message/charset"
|
||||
)
|
||||
|
||||
const maxHeaderLen = 76
|
||||
|
||||
func parseHeaderWithParams(s string) (f string, params map[string]string, err error) {
|
||||
f, params, err = mime.ParseMediaType(s)
|
||||
if err != nil {
|
||||
return s, nil, err
|
||||
}
|
||||
for k, v := range params {
|
||||
params[k], _ = charset.DecodeHeader(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func formatHeaderWithParams(f string, params map[string]string) string {
|
||||
encParams := make(map[string]string)
|
||||
for k, v := range params {
|
||||
encParams[k] = charset.EncodeHeader(v)
|
||||
}
|
||||
return mime.FormatMediaType(f, encParams)
|
||||
}
|
||||
|
||||
// formatHeaderField formats a header field, ensuring each line is no longer
|
||||
// than 76 characters. It tries to fold lines at whitespace characters if
|
||||
// possible. If the header contains a word longer than this limit, it will be
|
||||
// split.
|
||||
func formatHeaderField(k, v string) string {
|
||||
s := k + ": "
|
||||
|
||||
if v == "" {
|
||||
return s + "\r\n"
|
||||
}
|
||||
|
||||
first := true
|
||||
for len(v) > 0 {
|
||||
maxlen := maxHeaderLen
|
||||
if first {
|
||||
maxlen -= len(s)
|
||||
}
|
||||
|
||||
// We'll need to fold before i
|
||||
foldBefore := maxlen + 1
|
||||
foldAt := len(v)
|
||||
|
||||
var folding string
|
||||
if foldBefore > len(v) {
|
||||
// We reached the end of the string
|
||||
if v[len(v)-1] != '\n' {
|
||||
// If there isn't already a trailing CRLF, insert one
|
||||
folding = "\r\n"
|
||||
}
|
||||
} else {
|
||||
// Find the closest whitespace before i
|
||||
foldAt = strings.LastIndexAny(v[:foldBefore], " \t\n")
|
||||
if foldAt == 0 {
|
||||
// The whitespace we found was the previous folding WSP
|
||||
foldAt = foldBefore - 1
|
||||
} else if foldAt < 0 {
|
||||
// We didn't find any whitespace, we have to insert one
|
||||
foldAt = foldBefore - 2
|
||||
}
|
||||
|
||||
switch v[foldAt] {
|
||||
case ' ', '\t':
|
||||
if v[foldAt-1] != '\n' {
|
||||
folding = "\r\n" // The next char will be a WSP, don't need to insert one
|
||||
}
|
||||
case '\n':
|
||||
folding = "" // There is already a CRLF, nothing to do
|
||||
default:
|
||||
folding = "\r\n " // Another char, we need to insert CRLF + WSP
|
||||
}
|
||||
}
|
||||
|
||||
s += v[:foldAt] + folding
|
||||
v = v[foldAt:]
|
||||
first = false
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// A Header represents the key-value pairs in a message header.
|
||||
type Header map[string][]string
|
||||
|
||||
// Add adds the key, value pair to the header. It appends to any existing values
|
||||
// associated with key.
|
||||
func (h Header) Add(key, value string) {
|
||||
textproto.MIMEHeader(h).Add(key, value)
|
||||
}
|
||||
|
||||
// Set sets the header entries associated with key to the single element value.
|
||||
// It replaces any existing values associated with key.
|
||||
func (h Header) Set(key, value string) {
|
||||
textproto.MIMEHeader(h).Set(key, value)
|
||||
}
|
||||
|
||||
// Get gets the first value associated with the given key. If there are no
|
||||
// values associated with the key, Get returns "".
|
||||
func (h Header) Get(key string) string {
|
||||
return textproto.MIMEHeader(h).Get(key)
|
||||
}
|
||||
|
||||
// Del deletes the values associated with key.
|
||||
func (h Header) Del(key string) {
|
||||
textproto.MIMEHeader(h).Del(key)
|
||||
}
|
||||
|
||||
// ContentType parses the Content-Type header field.
|
||||
//
|
||||
// If no Content-Type is specified, it returns "text/plain".
|
||||
func (h Header) ContentType() (t string, params map[string]string, err error) {
|
||||
v := h.Get("Content-Type")
|
||||
if v == "" {
|
||||
return "text/plain", nil, nil
|
||||
}
|
||||
return parseHeaderWithParams(v)
|
||||
}
|
||||
|
||||
// SetContentType formats the Content-Type header field.
|
||||
func (h Header) SetContentType(t string, params map[string]string) {
|
||||
h.Set("Content-Type", formatHeaderWithParams(t, params))
|
||||
}
|
||||
|
||||
// ContentDescription parses the Content-Description header field.
|
||||
func (h Header) ContentDescription() (string, error) {
|
||||
return charset.DecodeHeader(h.Get("Content-Description"))
|
||||
}
|
||||
|
||||
// SetContentDescription parses the Content-Description header field.
|
||||
func (h Header) SetContentDescription(desc string) {
|
||||
h.Set("Content-Description", charset.EncodeHeader(desc))
|
||||
}
|
||||
|
||||
// ContentDisposition parses the Content-Disposition header field, as defined in
|
||||
// RFC 2183.
|
||||
func (h Header) ContentDisposition() (disp string, params map[string]string, err error) {
|
||||
return parseHeaderWithParams(h.Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
// SetContentDisposition formats the Content-Disposition header field, as
|
||||
// defined in RFC 2183.
|
||||
func (h Header) SetContentDisposition(disp string, params map[string]string) {
|
||||
h.Set("Content-Disposition", formatHeaderWithParams(disp, params))
|
||||
}
|
37
vendor/github.com/emersion/go-message/mail/address.go
generated
vendored
Normal file
37
vendor/github.com/emersion/go-message/mail/address.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Address represents a single mail address.
|
||||
type Address mail.Address
|
||||
|
||||
// String formats the address as a valid RFC 5322 address. If the address's name
|
||||
// contains non-ASCII characters the name will be rendered according to
|
||||
// RFC 2047.
|
||||
func (a *Address) String() string {
|
||||
return ((*mail.Address)(a)).String()
|
||||
}
|
||||
|
||||
func parseAddressList(s string) ([]*Address, error) {
|
||||
list, err := mail.ParseAddressList(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrs := make([]*Address, len(list))
|
||||
for i, a := range list {
|
||||
addrs[i] = (*Address)(a)
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
func formatAddressList(l []*Address) string {
|
||||
formatted := make([]string, len(l))
|
||||
for i, a := range l {
|
||||
formatted[i] = a.String()
|
||||
}
|
||||
return strings.Join(formatted, ", ")
|
||||
}
|
38
vendor/github.com/emersion/go-message/mail/attachment.go
generated
vendored
Normal file
38
vendor/github.com/emersion/go-message/mail/attachment.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// An AttachmentHeader represents an attachment's header.
|
||||
type AttachmentHeader struct {
|
||||
message.Header
|
||||
}
|
||||
|
||||
// NewAttachmentHeader creates a new AttachmentHeader.
|
||||
func NewAttachmentHeader() AttachmentHeader {
|
||||
h := AttachmentHeader{make(message.Header)}
|
||||
h.Set("Content-Disposition", "attachment")
|
||||
h.Set("Content-Transfer-Encoding", "base64")
|
||||
return h
|
||||
}
|
||||
|
||||
// Filename parses the attachment's filename.
|
||||
func (h AttachmentHeader) Filename() (string, error) {
|
||||
_, params, err := h.ContentDisposition()
|
||||
|
||||
filename, ok := params["filename"]
|
||||
if !ok {
|
||||
// Using "name" in Content-Type is discouraged
|
||||
_, params, err = h.ContentType()
|
||||
filename = params["name"]
|
||||
}
|
||||
|
||||
return filename, err
|
||||
}
|
||||
|
||||
// SetFilename formats the attachment's filename.
|
||||
func (h AttachmentHeader) SetFilename(filename string) {
|
||||
dispParams := map[string]string{"filename": filename}
|
||||
h.SetContentDisposition("attachment", dispParams)
|
||||
}
|
57
vendor/github.com/emersion/go-message/mail/header.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-message/mail/header.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-message"
|
||||
"github.com/emersion/go-message/charset"
|
||||
)
|
||||
|
||||
const dateLayout = "Mon, 02 Jan 2006 15:04:05 -0700"
|
||||
|
||||
// A Header is a mail header.
|
||||
type Header struct {
|
||||
message.Header
|
||||
}
|
||||
|
||||
// NewHeader creates a new mail header.
|
||||
func NewHeader() Header {
|
||||
return Header{make(message.Header)}
|
||||
}
|
||||
|
||||
// AddressList parses the named header field as a list of addresses. If the
|
||||
// header is missing, it returns nil.
|
||||
func (h Header) AddressList(key string) ([]*Address, error) {
|
||||
v := h.Get(key)
|
||||
if v == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return parseAddressList(v)
|
||||
}
|
||||
|
||||
// SetAddressList formats the named header to the provided list of addresses.
|
||||
func (h Header) SetAddressList(key string, addrs []*Address) {
|
||||
h.Set(key, formatAddressList(addrs))
|
||||
}
|
||||
|
||||
// Date parses the Date header field.
|
||||
func (h Header) Date() (time.Time, error) {
|
||||
return mail.Header(h.Header).Date()
|
||||
}
|
||||
|
||||
// SetDate formats the Date header field.
|
||||
func (h Header) SetDate(t time.Time) {
|
||||
h.Set("Date", t.Format(dateLayout))
|
||||
}
|
||||
|
||||
// Subject parses the Subject header field. If there is an error, the raw field
|
||||
// value is returned alongside the error.
|
||||
func (h Header) Subject() (string, error) {
|
||||
return charset.DecodeHeader(h.Get("Subject"))
|
||||
}
|
||||
|
||||
// SetSubject formats the Subject header field.
|
||||
func (h Header) SetSubject(s string) {
|
||||
h.Set("Subject", charset.EncodeHeader(s))
|
||||
}
|
9
vendor/github.com/emersion/go-message/mail/mail.go
generated
vendored
Normal file
9
vendor/github.com/emersion/go-message/mail/mail.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Package mail implements reading and writing mail messages.
|
||||
//
|
||||
// This package assumes that a mail message contains one or more text parts and
|
||||
// zero or more attachment parts. Each text part represents a different version
|
||||
// of the message content (e.g. a different type, a different language and so
|
||||
// on).
|
||||
//
|
||||
// RFC 5322 defines the Internet Message Format.
|
||||
package mail
|
130
vendor/github.com/emersion/go-message/mail/reader.go
generated
vendored
Normal file
130
vendor/github.com/emersion/go-message/mail/reader.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// A PartHeader is a mail part header. It contains convenience functions to get
|
||||
// and set header fields.
|
||||
type PartHeader interface {
|
||||
// Add adds the key, value pair to the header.
|
||||
Add(key, value string)
|
||||
// Del deletes the values associated with key.
|
||||
Del(key string)
|
||||
// Get gets the first value associated with the given key. If there are no
|
||||
// values associated with the key, Get returns "".
|
||||
Get(key string) string
|
||||
// Set sets the header entries associated with key to the single element
|
||||
// value. It replaces any existing values associated with key.
|
||||
Set(key, value string)
|
||||
}
|
||||
|
||||
// A Part is either a mail text or an attachment. Header is either a TextHeader
|
||||
// or an AttachmentHeader.
|
||||
type Part struct {
|
||||
Header PartHeader
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
// A Reader reads a mail message.
|
||||
type Reader struct {
|
||||
Header Header
|
||||
|
||||
e *message.Entity
|
||||
readers *list.List
|
||||
}
|
||||
|
||||
// NewReader creates a new mail reader.
|
||||
func NewReader(e *message.Entity) *Reader {
|
||||
mr := e.MultipartReader()
|
||||
if mr == nil {
|
||||
// Artificially create a multipart entity
|
||||
// With this header, no error will be returned by message.NewMultipart
|
||||
h := make(message.Header)
|
||||
h.Set("Content-Type", "multipart/mixed")
|
||||
me, _ := message.NewMultipart(h, []*message.Entity{e})
|
||||
mr = me.MultipartReader()
|
||||
}
|
||||
|
||||
l := list.New()
|
||||
l.PushBack(mr)
|
||||
|
||||
return &Reader{Header{e.Header}, e, l}
|
||||
}
|
||||
|
||||
// CreateReader reads a mail header from r and returns a new mail reader.
|
||||
//
|
||||
// If the message uses an unknown transfer encoding or charset, CreateReader
|
||||
// returns an error that verifies message.IsUnknownEncoding, but also returns a
|
||||
// Reader that can be used.
|
||||
func CreateReader(r io.Reader) (*Reader, error) {
|
||||
e, err := message.Read(r)
|
||||
if err != nil && !message.IsUnknownEncoding(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewReader(e), err
|
||||
}
|
||||
|
||||
// NextPart returns the next mail part. If there is no more part, io.EOF is
|
||||
// returned as error.
|
||||
//
|
||||
// The returned Part.Body must be read completely before the next call to
|
||||
// NextPart, otherwise it will be discarded.
|
||||
//
|
||||
// If the part uses an unknown transfer encoding or charset, NextPart returns an
|
||||
// error that verifies message.IsUnknownEncoding, but also returns a Part that
|
||||
// can be used.
|
||||
func (r *Reader) NextPart() (*Part, error) {
|
||||
for r.readers.Len() > 0 {
|
||||
e := r.readers.Back()
|
||||
mr := e.Value.(message.MultipartReader)
|
||||
|
||||
p, err := mr.NextPart()
|
||||
if err == io.EOF {
|
||||
// This whole multipart entity has been read, continue with the next one
|
||||
r.readers.Remove(e)
|
||||
continue
|
||||
} else if err != nil && !message.IsUnknownEncoding(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pmr := p.MultipartReader(); pmr != nil {
|
||||
// This is a multipart part, read it
|
||||
r.readers.PushBack(pmr)
|
||||
} else {
|
||||
// This is a non-multipart part, return a mail part
|
||||
mp := &Part{Body: p.Body}
|
||||
t, _, _ := p.Header.ContentType()
|
||||
disp, _, _ := p.Header.ContentDisposition()
|
||||
if strings.HasPrefix(t, "text/") && disp != "attachment" {
|
||||
mp.Header = TextHeader{p.Header}
|
||||
} else {
|
||||
mp.Header = AttachmentHeader{p.Header}
|
||||
}
|
||||
return mp, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Close finishes the reader.
|
||||
func (r *Reader) Close() error {
|
||||
for r.readers.Len() > 0 {
|
||||
e := r.readers.Back()
|
||||
mr := e.Value.(message.MultipartReader)
|
||||
|
||||
if err := mr.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.readers.Remove(e)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
18
vendor/github.com/emersion/go-message/mail/text.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-message/mail/text.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// A TextHeader represents a message text header.
|
||||
type TextHeader struct {
|
||||
message.Header
|
||||
}
|
||||
|
||||
// NewTextHeader creates a new message text header.
|
||||
func NewTextHeader() TextHeader {
|
||||
h := TextHeader{make(message.Header)}
|
||||
h.Set("Content-Disposition", "inline")
|
||||
h.Set("Content-Transfer-Encoding", "quoted-printable")
|
||||
return h
|
||||
}
|
73
vendor/github.com/emersion/go-message/mail/writer.go
generated
vendored
Normal file
73
vendor/github.com/emersion/go-message/mail/writer.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/emersion/go-message"
|
||||
)
|
||||
|
||||
// A Writer writes a mail message. A mail message contains one or more text
|
||||
// parts and zero or more attachments.
|
||||
type Writer struct {
|
||||
mw *message.Writer
|
||||
}
|
||||
|
||||
// CreateWriter writes a mail header to w and creates a new Writer.
|
||||
func CreateWriter(w io.Writer, header Header) (*Writer, error) {
|
||||
header.Set("Content-Type", "multipart/mixed")
|
||||
|
||||
mw, err := message.CreateWriter(w, header.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Writer{mw}, nil
|
||||
}
|
||||
|
||||
// CreateText creates a TextWriter. One or more parts representing the same text
|
||||
// in different formats can be written to a TextWriter.
|
||||
func (w *Writer) CreateText() (*TextWriter, error) {
|
||||
h := make(message.Header)
|
||||
h.Set("Content-Type", "multipart/alternative")
|
||||
|
||||
mw, err := w.mw.CreatePart(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TextWriter{mw}, nil
|
||||
}
|
||||
|
||||
// CreateSingleText creates a new single text part with the provided header. The
|
||||
// body of the part should be written to the returned io.WriteCloser. Only one
|
||||
// single text part should be written, use CreateText if you want multiple text
|
||||
// parts.
|
||||
func (w *Writer) CreateSingleText(header TextHeader) (io.WriteCloser, error) {
|
||||
return w.mw.CreatePart(header.Header)
|
||||
}
|
||||
|
||||
// CreateAttachment creates a new attachment with the provided header. The body
|
||||
// of the part should be written to the returned io.WriteCloser.
|
||||
func (w *Writer) CreateAttachment(header AttachmentHeader) (io.WriteCloser, error) {
|
||||
return w.mw.CreatePart(header.Header)
|
||||
}
|
||||
|
||||
// Close finishes the Writer.
|
||||
func (w *Writer) Close() error {
|
||||
return w.mw.Close()
|
||||
}
|
||||
|
||||
// TextWriter writes a mail message's text.
|
||||
type TextWriter struct {
|
||||
mw *message.Writer
|
||||
}
|
||||
|
||||
// CreatePart creates a new text part with the provided header. The body of the
|
||||
// part should be written to the returned io.WriteCloser.
|
||||
func (w *TextWriter) CreatePart(header TextHeader) (io.WriteCloser, error) {
|
||||
return w.mw.CreatePart(header.Header)
|
||||
}
|
||||
|
||||
// Close finishes the TextWriter.
|
||||
func (w *TextWriter) Close() error {
|
||||
return w.mw.Close()
|
||||
}
|
5
vendor/github.com/emersion/go-message/message.go
generated
vendored
Normal file
5
vendor/github.com/emersion/go-message/message.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Package message implements reading and writing multipurpose messages.
|
||||
//
|
||||
// RFC 2045, RFC 2046 and RFC 2047 defines MIME, and RFC 2183 defines the
|
||||
// Content-Disposition header field.
|
||||
package message
|
110
vendor/github.com/emersion/go-message/multipart.go
generated
vendored
Normal file
110
vendor/github.com/emersion/go-message/multipart.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"io"
|
||||
"mime/multipart"
|
||||
)
|
||||
|
||||
// MultipartReader is an iterator over parts in a MIME multipart body.
|
||||
type MultipartReader interface {
|
||||
io.Closer
|
||||
|
||||
// NextPart returns the next part in the multipart or an error. When there are
|
||||
// no more parts, the error io.EOF is returned.
|
||||
//
|
||||
// Entity.Body must be read completely before the next call to NextPart,
|
||||
// otherwise it will be discarded.
|
||||
NextPart() (*Entity, error)
|
||||
}
|
||||
|
||||
type multipartReader struct {
|
||||
r *multipart.Reader
|
||||
}
|
||||
|
||||
// NextPart implements MultipartReader.
|
||||
func (r *multipartReader) NextPart() (*Entity, error) {
|
||||
p, err := r.r.NextPart()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return New(Header(p.Header), p)
|
||||
}
|
||||
|
||||
// Close implements io.Closer.
|
||||
func (r *multipartReader) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type multipartBody struct {
|
||||
header Header
|
||||
parts []*Entity
|
||||
|
||||
r *io.PipeReader
|
||||
w *Writer
|
||||
|
||||
i int
|
||||
}
|
||||
|
||||
// Read implements io.Reader.
|
||||
func (m *multipartBody) Read(p []byte) (n int, err error) {
|
||||
if m.r == nil {
|
||||
r, w := io.Pipe()
|
||||
m.r = r
|
||||
m.w = newWriter(w, m.header)
|
||||
|
||||
// Prevent calls to NextPart to succeed
|
||||
m.i = len(m.parts)
|
||||
|
||||
go func() {
|
||||
if err := m.writeBodyTo(m.w); err != nil {
|
||||
w.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.w.Close(); err != nil {
|
||||
w.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
return m.r.Read(p)
|
||||
}
|
||||
|
||||
// Close implements io.Closer.
|
||||
func (m *multipartBody) Close() error {
|
||||
if m.r != nil {
|
||||
m.r.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextPart implements MultipartReader.
|
||||
func (m *multipartBody) NextPart() (*Entity, error) {
|
||||
if m.i >= len(m.parts) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
part := m.parts[m.i]
|
||||
m.i++
|
||||
return part, nil
|
||||
}
|
||||
|
||||
func (m *multipartBody) writeBodyTo(w *Writer) error {
|
||||
for _, p := range m.parts {
|
||||
pw, err := w.CreatePart(p.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.writeBodyTo(pw); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
116
vendor/github.com/emersion/go-message/writer.go
generated
vendored
Normal file
116
vendor/github.com/emersion/go-message/writer.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// From https://golang.org/src/mime/multipart/writer.go?s=2140:2215#L76
|
||||
func writeHeader(w io.Writer, header Header) error {
|
||||
keys := make([]string, 0, len(header))
|
||||
for k := range header {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
for _, v := range header[k] {
|
||||
if _, err := io.WriteString(w, formatHeaderField(k, v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := io.WriteString(w, "\r\n")
|
||||
return err
|
||||
}
|
||||
|
||||
// A Writer formats entities.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
c io.Closer
|
||||
mw *multipart.Writer
|
||||
}
|
||||
|
||||
// newWriter creates a new Writer writing to w with the provided header. Nothing
|
||||
// is written to w when it is called. header is modified in-place.
|
||||
func newWriter(w io.Writer, header Header) *Writer {
|
||||
ww := &Writer{w: w}
|
||||
|
||||
mediaType, mediaParams, _ := header.ContentType()
|
||||
if strings.HasPrefix(mediaType, "multipart/") {
|
||||
ww.mw = multipart.NewWriter(ww.w)
|
||||
|
||||
// Do not set ww's io.Closer for now: if this is a multipart entity but
|
||||
// CreatePart is not used (only Write is used), then the final boundary
|
||||
// is expected to be written by the user too. In this case, ww.Close
|
||||
// shouldn't write the final boundary.
|
||||
|
||||
if mediaParams["boundary"] != "" {
|
||||
ww.mw.SetBoundary(mediaParams["boundary"])
|
||||
} else {
|
||||
mediaParams["boundary"] = ww.mw.Boundary()
|
||||
header.SetContentType(mediaType, mediaParams)
|
||||
}
|
||||
|
||||
header.Del("Content-Transfer-Encoding")
|
||||
} else {
|
||||
wc := encodingWriter(header.Get("Content-Transfer-Encoding"), ww.w)
|
||||
ww.w = wc
|
||||
ww.c = wc
|
||||
}
|
||||
|
||||
return ww
|
||||
}
|
||||
|
||||
// CreateWriter creates a new Writer writing to w. If header contains an
|
||||
// encoding, data written to the Writer will automatically be encoded with it.
|
||||
func CreateWriter(w io.Writer, header Header) (*Writer, error) {
|
||||
ww := newWriter(w, header)
|
||||
if err := writeHeader(w, header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ww, nil
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *Writer) Write(b []byte) (int, error) {
|
||||
return w.w.Write(b)
|
||||
}
|
||||
|
||||
// Close implements io.Closer.
|
||||
func (w *Writer) Close() error {
|
||||
if w.c != nil {
|
||||
return w.c.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePart returns a Writer to a new part in this multipart entity. If this
|
||||
// entity is not multipart, it fails. The body of the part should be written to
|
||||
// the returned io.WriteCloser.
|
||||
func (w *Writer) CreatePart(header Header) (*Writer, error) {
|
||||
if w.mw == nil {
|
||||
return nil, errors.New("cannot create a part in a non-multipart message")
|
||||
}
|
||||
|
||||
if w.c == nil {
|
||||
// We know that the user calls CreatePart so Close should write the final
|
||||
// boundary
|
||||
w.c = w.mw
|
||||
}
|
||||
|
||||
// cw -> ww -> pw -> w.mw -> w.w
|
||||
|
||||
ww := &struct{ io.Writer }{nil}
|
||||
cw := newWriter(ww, header)
|
||||
pw, err := w.mw.CreatePart(textproto.MIMEHeader(header))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ww.Writer = pw
|
||||
return cw, nil
|
||||
}
|
24
vendor/github.com/emersion/go-textwrapper/.gitignore
generated
vendored
Normal file
24
vendor/github.com/emersion/go-textwrapper/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
1
vendor/github.com/emersion/go-textwrapper/.travis.yml
generated
vendored
Normal file
1
vendor/github.com/emersion/go-textwrapper/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
language: go
|
21
vendor/github.com/emersion/go-textwrapper/LICENSE
generated
vendored
Normal file
21
vendor/github.com/emersion/go-textwrapper/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 emersion
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
27
vendor/github.com/emersion/go-textwrapper/README.md
generated
vendored
Normal file
27
vendor/github.com/emersion/go-textwrapper/README.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# go-textwrapper
|
||||
|
||||
[](https://godoc.org/github.com/emersion/go-textwrapper)
|
||||
[](https://travis-ci.org/emersion/go-textwrapper)
|
||||
|
||||
A writer that wraps long text lines to a specified length
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/emersion/go-textwrapper"
|
||||
)
|
||||
|
||||
func main() {
|
||||
w := textwrapper.New(os.Stdout, "/", 5)
|
||||
|
||||
w.Write([]byte("helloworldhelloworldhelloworld"))
|
||||
// Output: hello/world/hello/world/hello/world
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
61
vendor/github.com/emersion/go-textwrapper/wrapper.go
generated
vendored
Normal file
61
vendor/github.com/emersion/go-textwrapper/wrapper.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// A writer that wraps long text lines to a specified length.
|
||||
package textwrapper
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type writer struct {
|
||||
Sep string
|
||||
Len int
|
||||
|
||||
w io.Writer
|
||||
i int
|
||||
}
|
||||
|
||||
func (w *writer) Write(b []byte) (N int, err error) {
|
||||
to := w.Len - w.i
|
||||
|
||||
for len(b) > to {
|
||||
var n int
|
||||
n, err = w.w.Write(b[:to])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
N += n
|
||||
b = b[to:]
|
||||
|
||||
_, err = w.w.Write([]byte(w.Sep))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.i = 0
|
||||
to = w.Len
|
||||
}
|
||||
|
||||
w.i += len(b)
|
||||
|
||||
n, err := w.w.Write(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
N += n
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a writer that splits its input into multiple parts that have the same
|
||||
// length and adds a separator between these parts.
|
||||
func New(w io.Writer, sep string, l int) io.Writer {
|
||||
return &writer{
|
||||
Sep: sep,
|
||||
Len: l,
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a RFC822 text wrapper. It adds a CRLF (ie. \r\n) each 76 characters.
|
||||
func NewRFC822(w io.Writer) io.Writer {
|
||||
return New(w, "\r\n", 76)
|
||||
}
|
6
vendor/golang.org/x/sys/unix/fcntl.go
generated
vendored
6
vendor/golang.org/x/sys/unix/fcntl.go
generated
vendored
@@ -14,7 +14,11 @@ var fcntl64Syscall uintptr = SYS_FCNTL
|
||||
|
||||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
valptr, _, err := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg))
|
||||
valptr, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(arg))
|
||||
var err error
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
}
|
||||
return int(valptr), err
|
||||
}
|
||||
|
||||
|
6
vendor/golang.org/x/sys/unix/syscall_solaris.go
generated
vendored
6
vendor/golang.org/x/sys/unix/syscall_solaris.go
generated
vendored
@@ -314,7 +314,11 @@ func UtimesNanoAt(dirfd int, path string, ts []Timespec, flags int) error {
|
||||
|
||||
// FcntlInt performs a fcntl syscall on fd with the provided command and argument.
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
valptr, _, err := sysvicall6(uintptr(unsafe.Pointer(&procfcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(arg), 0, 0, 0)
|
||||
valptr, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procfcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(arg), 0, 0, 0)
|
||||
var err error
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
}
|
||||
return int(valptr), err
|
||||
}
|
||||
|
||||
|
249
vendor/golang.org/x/text/encoding/charmap/charmap.go
generated
vendored
Normal file
249
vendor/golang.org/x/text/encoding/charmap/charmap.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run maketables.go
|
||||
|
||||
// Package charmap provides simple character encodings such as IBM Code Page 437
|
||||
// and Windows 1252.
|
||||
package charmap // import "golang.org/x/text/encoding/charmap"
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// These encodings vary only in the way clients should interpret them. Their
|
||||
// coded character set is identical and a single implementation can be shared.
|
||||
var (
|
||||
// ISO8859_6E is the ISO 8859-6E encoding.
|
||||
ISO8859_6E encoding.Encoding = &iso8859_6E
|
||||
|
||||
// ISO8859_6I is the ISO 8859-6I encoding.
|
||||
ISO8859_6I encoding.Encoding = &iso8859_6I
|
||||
|
||||
// ISO8859_8E is the ISO 8859-8E encoding.
|
||||
ISO8859_8E encoding.Encoding = &iso8859_8E
|
||||
|
||||
// ISO8859_8I is the ISO 8859-8I encoding.
|
||||
ISO8859_8I encoding.Encoding = &iso8859_8I
|
||||
|
||||
iso8859_6E = internal.Encoding{
|
||||
Encoding: ISO8859_6,
|
||||
Name: "ISO-8859-6E",
|
||||
MIB: identifier.ISO88596E,
|
||||
}
|
||||
|
||||
iso8859_6I = internal.Encoding{
|
||||
Encoding: ISO8859_6,
|
||||
Name: "ISO-8859-6I",
|
||||
MIB: identifier.ISO88596I,
|
||||
}
|
||||
|
||||
iso8859_8E = internal.Encoding{
|
||||
Encoding: ISO8859_8,
|
||||
Name: "ISO-8859-8E",
|
||||
MIB: identifier.ISO88598E,
|
||||
}
|
||||
|
||||
iso8859_8I = internal.Encoding{
|
||||
Encoding: ISO8859_8,
|
||||
Name: "ISO-8859-8I",
|
||||
MIB: identifier.ISO88598I,
|
||||
}
|
||||
)
|
||||
|
||||
// All is a list of all defined encodings in this package.
|
||||
var All []encoding.Encoding = listAll
|
||||
|
||||
// TODO: implement these encodings, in order of importance.
|
||||
// ASCII, ISO8859_1: Rather common. Close to Windows 1252.
|
||||
// ISO8859_9: Close to Windows 1254.
|
||||
|
||||
// utf8Enc holds a rune's UTF-8 encoding in data[:len].
|
||||
type utf8Enc struct {
|
||||
len uint8
|
||||
data [3]byte
|
||||
}
|
||||
|
||||
// Charmap is an 8-bit character set encoding.
|
||||
type Charmap struct {
|
||||
// name is the encoding's name.
|
||||
name string
|
||||
// mib is the encoding type of this encoder.
|
||||
mib identifier.MIB
|
||||
// asciiSuperset states whether the encoding is a superset of ASCII.
|
||||
asciiSuperset bool
|
||||
// low is the lower bound of the encoded byte for a non-ASCII rune. If
|
||||
// Charmap.asciiSuperset is true then this will be 0x80, otherwise 0x00.
|
||||
low uint8
|
||||
// replacement is the encoded replacement character.
|
||||
replacement byte
|
||||
// decode is the map from encoded byte to UTF-8.
|
||||
decode [256]utf8Enc
|
||||
// encoding is the map from runes to encoded bytes. Each entry is a
|
||||
// uint32: the high 8 bits are the encoded byte and the low 24 bits are
|
||||
// the rune. The table entries are sorted by ascending rune.
|
||||
encode [256]uint32
|
||||
}
|
||||
|
||||
// NewDecoder implements the encoding.Encoding interface.
|
||||
func (m *Charmap) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: charmapDecoder{charmap: m}}
|
||||
}
|
||||
|
||||
// NewEncoder implements the encoding.Encoding interface.
|
||||
func (m *Charmap) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: charmapEncoder{charmap: m}}
|
||||
}
|
||||
|
||||
// String returns the Charmap's name.
|
||||
func (m *Charmap) String() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
// ID implements an internal interface.
|
||||
func (m *Charmap) ID() (mib identifier.MIB, other string) {
|
||||
return m.mib, ""
|
||||
}
|
||||
|
||||
// charmapDecoder implements transform.Transformer by decoding to UTF-8.
|
||||
type charmapDecoder struct {
|
||||
transform.NopResetter
|
||||
charmap *Charmap
|
||||
}
|
||||
|
||||
func (m charmapDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for i, c := range src {
|
||||
if m.charmap.asciiSuperset && c < utf8.RuneSelf {
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = c
|
||||
nDst++
|
||||
nSrc = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
decode := &m.charmap.decode[c]
|
||||
n := int(decode.len)
|
||||
if nDst+n > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
// It's 15% faster to avoid calling copy for these tiny slices.
|
||||
for j := 0; j < n; j++ {
|
||||
dst[nDst] = decode.data[j]
|
||||
nDst++
|
||||
}
|
||||
nSrc = i + 1
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// DecodeByte returns the Charmap's rune decoding of the byte b.
|
||||
func (m *Charmap) DecodeByte(b byte) rune {
|
||||
switch x := &m.decode[b]; x.len {
|
||||
case 1:
|
||||
return rune(x.data[0])
|
||||
case 2:
|
||||
return rune(x.data[0]&0x1f)<<6 | rune(x.data[1]&0x3f)
|
||||
default:
|
||||
return rune(x.data[0]&0x0f)<<12 | rune(x.data[1]&0x3f)<<6 | rune(x.data[2]&0x3f)
|
||||
}
|
||||
}
|
||||
|
||||
// charmapEncoder implements transform.Transformer by encoding from UTF-8.
|
||||
type charmapEncoder struct {
|
||||
transform.NopResetter
|
||||
charmap *Charmap
|
||||
}
|
||||
|
||||
func (m charmapEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for nSrc < len(src) {
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
if m.charmap.asciiSuperset {
|
||||
nSrc++
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
}
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = internal.RepertoireError(m.charmap.replacement)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Binary search in [low, high) for that rune in the m.charmap.encode table.
|
||||
for low, high := int(m.charmap.low), 0x100; ; {
|
||||
if low >= high {
|
||||
err = internal.RepertoireError(m.charmap.replacement)
|
||||
break loop
|
||||
}
|
||||
mid := (low + high) / 2
|
||||
got := m.charmap.encode[mid]
|
||||
gotRune := rune(got & (1<<24 - 1))
|
||||
if gotRune < r {
|
||||
low = mid + 1
|
||||
} else if gotRune > r {
|
||||
high = mid
|
||||
} else {
|
||||
dst[nDst] = byte(got >> 24)
|
||||
nDst++
|
||||
break
|
||||
}
|
||||
}
|
||||
nSrc += size
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// EncodeRune returns the Charmap's byte encoding of the rune r. ok is whether
|
||||
// r is in the Charmap's repertoire. If not, b is set to the Charmap's
|
||||
// replacement byte. This is often the ASCII substitute character '\x1a'.
|
||||
func (m *Charmap) EncodeRune(r rune) (b byte, ok bool) {
|
||||
if r < utf8.RuneSelf && m.asciiSuperset {
|
||||
return byte(r), true
|
||||
}
|
||||
for low, high := int(m.low), 0x100; ; {
|
||||
if low >= high {
|
||||
return m.replacement, false
|
||||
}
|
||||
mid := (low + high) / 2
|
||||
got := m.encode[mid]
|
||||
gotRune := rune(got & (1<<24 - 1))
|
||||
if gotRune < r {
|
||||
low = mid + 1
|
||||
} else if gotRune > r {
|
||||
high = mid
|
||||
} else {
|
||||
return byte(got >> 24), true
|
||||
}
|
||||
}
|
||||
}
|
556
vendor/golang.org/x/text/encoding/charmap/maketables.go
generated
vendored
Normal file
556
vendor/golang.org/x/text/encoding/charmap/maketables.go
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/internal/gen"
|
||||
)
|
||||
|
||||
const ascii = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
||||
` !"#$%&'()*+,-./0123456789:;<=>?` +
|
||||
`@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_` +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
|
||||
|
||||
var encodings = []struct {
|
||||
name string
|
||||
mib string
|
||||
comment string
|
||||
varName string
|
||||
replacement byte
|
||||
mapping string
|
||||
}{
|
||||
{
|
||||
"IBM Code Page 037",
|
||||
"IBM037",
|
||||
"",
|
||||
"CodePage037",
|
||||
0x3f,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM037-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 437",
|
||||
"PC8CodePage437",
|
||||
"",
|
||||
"CodePage437",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM437-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 850",
|
||||
"PC850Multilingual",
|
||||
"",
|
||||
"CodePage850",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM850-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 852",
|
||||
"PCp852",
|
||||
"",
|
||||
"CodePage852",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM852-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 855",
|
||||
"IBM855",
|
||||
"",
|
||||
"CodePage855",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM855-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"Windows Code Page 858", // PC latin1 with Euro
|
||||
"IBM00858",
|
||||
"",
|
||||
"CodePage858",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/windows-858-2000.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 860",
|
||||
"IBM860",
|
||||
"",
|
||||
"CodePage860",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM860-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 862",
|
||||
"PC862LatinHebrew",
|
||||
"",
|
||||
"CodePage862",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM862-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 863",
|
||||
"IBM863",
|
||||
"",
|
||||
"CodePage863",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM863-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 865",
|
||||
"IBM865",
|
||||
"",
|
||||
"CodePage865",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM865-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 866",
|
||||
"IBM866",
|
||||
"",
|
||||
"CodePage866",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-ibm866.txt",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 1047",
|
||||
"IBM1047",
|
||||
"",
|
||||
"CodePage1047",
|
||||
0x3f,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/glibc-IBM1047-2.1.2.ucm",
|
||||
},
|
||||
{
|
||||
"IBM Code Page 1140",
|
||||
"IBM01140",
|
||||
"",
|
||||
"CodePage1140",
|
||||
0x3f,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/ibm-1140_P100-1997.ucm",
|
||||
},
|
||||
{
|
||||
"ISO 8859-1",
|
||||
"ISOLatin1",
|
||||
"",
|
||||
"ISO8859_1",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/iso-8859_1-1998.ucm",
|
||||
},
|
||||
{
|
||||
"ISO 8859-2",
|
||||
"ISOLatin2",
|
||||
"",
|
||||
"ISO8859_2",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-2.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-3",
|
||||
"ISOLatin3",
|
||||
"",
|
||||
"ISO8859_3",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-3.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-4",
|
||||
"ISOLatin4",
|
||||
"",
|
||||
"ISO8859_4",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-4.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-5",
|
||||
"ISOLatinCyrillic",
|
||||
"",
|
||||
"ISO8859_5",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-5.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-6",
|
||||
"ISOLatinArabic",
|
||||
"",
|
||||
"ISO8859_6,ISO8859_6E,ISO8859_6I",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-6.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-7",
|
||||
"ISOLatinGreek",
|
||||
"",
|
||||
"ISO8859_7",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-7.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-8",
|
||||
"ISOLatinHebrew",
|
||||
"",
|
||||
"ISO8859_8,ISO8859_8E,ISO8859_8I",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-8.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-9",
|
||||
"ISOLatin5",
|
||||
"",
|
||||
"ISO8859_9",
|
||||
encoding.ASCIISub,
|
||||
"http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/iso-8859_9-1999.ucm",
|
||||
},
|
||||
{
|
||||
"ISO 8859-10",
|
||||
"ISOLatin6",
|
||||
"",
|
||||
"ISO8859_10",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-10.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-13",
|
||||
"ISO885913",
|
||||
"",
|
||||
"ISO8859_13",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-13.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-14",
|
||||
"ISO885914",
|
||||
"",
|
||||
"ISO8859_14",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-14.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-15",
|
||||
"ISO885915",
|
||||
"",
|
||||
"ISO8859_15",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-15.txt",
|
||||
},
|
||||
{
|
||||
"ISO 8859-16",
|
||||
"ISO885916",
|
||||
"",
|
||||
"ISO8859_16",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-iso-8859-16.txt",
|
||||
},
|
||||
{
|
||||
"KOI8-R",
|
||||
"KOI8R",
|
||||
"",
|
||||
"KOI8R",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-koi8-r.txt",
|
||||
},
|
||||
{
|
||||
"KOI8-U",
|
||||
"KOI8U",
|
||||
"",
|
||||
"KOI8U",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-koi8-u.txt",
|
||||
},
|
||||
{
|
||||
"Macintosh",
|
||||
"Macintosh",
|
||||
"",
|
||||
"Macintosh",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-macintosh.txt",
|
||||
},
|
||||
{
|
||||
"Macintosh Cyrillic",
|
||||
"MacintoshCyrillic",
|
||||
"",
|
||||
"MacintoshCyrillic",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-x-mac-cyrillic.txt",
|
||||
},
|
||||
{
|
||||
"Windows 874",
|
||||
"Windows874",
|
||||
"",
|
||||
"Windows874",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-874.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1250",
|
||||
"Windows1250",
|
||||
"",
|
||||
"Windows1250",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1250.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1251",
|
||||
"Windows1251",
|
||||
"",
|
||||
"Windows1251",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1251.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1252",
|
||||
"Windows1252",
|
||||
"",
|
||||
"Windows1252",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1252.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1253",
|
||||
"Windows1253",
|
||||
"",
|
||||
"Windows1253",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1253.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1254",
|
||||
"Windows1254",
|
||||
"",
|
||||
"Windows1254",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1254.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1255",
|
||||
"Windows1255",
|
||||
"",
|
||||
"Windows1255",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1255.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1256",
|
||||
"Windows1256",
|
||||
"",
|
||||
"Windows1256",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1256.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1257",
|
||||
"Windows1257",
|
||||
"",
|
||||
"Windows1257",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1257.txt",
|
||||
},
|
||||
{
|
||||
"Windows 1258",
|
||||
"Windows1258",
|
||||
"",
|
||||
"Windows1258",
|
||||
encoding.ASCIISub,
|
||||
"http://encoding.spec.whatwg.org/index-windows-1258.txt",
|
||||
},
|
||||
{
|
||||
"X-User-Defined",
|
||||
"XUserDefined",
|
||||
"It is defined at http://encoding.spec.whatwg.org/#x-user-defined",
|
||||
"XUserDefined",
|
||||
encoding.ASCIISub,
|
||||
ascii +
|
||||
"\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787" +
|
||||
"\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f" +
|
||||
"\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797" +
|
||||
"\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f" +
|
||||
"\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7" +
|
||||
"\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af" +
|
||||
"\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7" +
|
||||
"\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf" +
|
||||
"\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7" +
|
||||
"\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf" +
|
||||
"\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7" +
|
||||
"\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df" +
|
||||
"\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7" +
|
||||
"\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef" +
|
||||
"\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7" +
|
||||
"\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff",
|
||||
},
|
||||
}
|
||||
|
||||
func getWHATWG(url string) string {
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatalf("%q: Get: %v", url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
mapping := make([]rune, 128)
|
||||
for i := range mapping {
|
||||
mapping[i] = '\ufffd'
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(res.Body)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
if s == "" || s[0] == '#' {
|
||||
continue
|
||||
}
|
||||
x, y := 0, 0
|
||||
if _, err := fmt.Sscanf(s, "%d\t0x%x", &x, &y); err != nil {
|
||||
log.Fatalf("could not parse %q", s)
|
||||
}
|
||||
if x < 0 || 128 <= x {
|
||||
log.Fatalf("code %d is out of range", x)
|
||||
}
|
||||
if 0x80 <= y && y < 0xa0 {
|
||||
// We diverge from the WHATWG spec by mapping control characters
|
||||
// in the range [0x80, 0xa0) to U+FFFD.
|
||||
continue
|
||||
}
|
||||
mapping[x] = rune(y)
|
||||
}
|
||||
return ascii + string(mapping)
|
||||
}
|
||||
|
||||
func getUCM(url string) string {
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Fatalf("%q: Get: %v", url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
mapping := make([]rune, 256)
|
||||
for i := range mapping {
|
||||
mapping[i] = '\ufffd'
|
||||
}
|
||||
|
||||
charsFound := 0
|
||||
scanner := bufio.NewScanner(res.Body)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
if s == "" || s[0] == '#' {
|
||||
continue
|
||||
}
|
||||
var c byte
|
||||
var r rune
|
||||
if _, err := fmt.Sscanf(s, `<U%x> \x%x |0`, &r, &c); err != nil {
|
||||
continue
|
||||
}
|
||||
mapping[c] = r
|
||||
charsFound++
|
||||
}
|
||||
|
||||
if charsFound < 200 {
|
||||
log.Fatalf("%q: only %d characters found (wrong page format?)", url, charsFound)
|
||||
}
|
||||
|
||||
return string(mapping)
|
||||
}
|
||||
|
||||
func main() {
|
||||
mibs := map[string]bool{}
|
||||
all := []string{}
|
||||
|
||||
w := gen.NewCodeWriter()
|
||||
defer w.WriteGoFile("tables.go", "charmap")
|
||||
|
||||
printf := func(s string, a ...interface{}) { fmt.Fprintf(w, s, a...) }
|
||||
|
||||
printf("import (\n")
|
||||
printf("\t\"golang.org/x/text/encoding\"\n")
|
||||
printf("\t\"golang.org/x/text/encoding/internal/identifier\"\n")
|
||||
printf(")\n\n")
|
||||
for _, e := range encodings {
|
||||
varNames := strings.Split(e.varName, ",")
|
||||
all = append(all, varNames...)
|
||||
varName := varNames[0]
|
||||
switch {
|
||||
case strings.HasPrefix(e.mapping, "http://encoding.spec.whatwg.org/"):
|
||||
e.mapping = getWHATWG(e.mapping)
|
||||
case strings.HasPrefix(e.mapping, "http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/"):
|
||||
e.mapping = getUCM(e.mapping)
|
||||
}
|
||||
|
||||
asciiSuperset, low := strings.HasPrefix(e.mapping, ascii), 0x00
|
||||
if asciiSuperset {
|
||||
low = 0x80
|
||||
}
|
||||
lvn := 1
|
||||
if strings.HasPrefix(varName, "ISO") || strings.HasPrefix(varName, "KOI") {
|
||||
lvn = 3
|
||||
}
|
||||
lowerVarName := strings.ToLower(varName[:lvn]) + varName[lvn:]
|
||||
printf("// %s is the %s encoding.\n", varName, e.name)
|
||||
if e.comment != "" {
|
||||
printf("//\n// %s\n", e.comment)
|
||||
}
|
||||
printf("var %s *Charmap = &%s\n\nvar %s = Charmap{\nname: %q,\n",
|
||||
varName, lowerVarName, lowerVarName, e.name)
|
||||
if mibs[e.mib] {
|
||||
log.Fatalf("MIB type %q declared multiple times.", e.mib)
|
||||
}
|
||||
printf("mib: identifier.%s,\n", e.mib)
|
||||
printf("asciiSuperset: %t,\n", asciiSuperset)
|
||||
printf("low: 0x%02x,\n", low)
|
||||
printf("replacement: 0x%02x,\n", e.replacement)
|
||||
|
||||
printf("decode: [256]utf8Enc{\n")
|
||||
i, backMapping := 0, map[rune]byte{}
|
||||
for _, c := range e.mapping {
|
||||
if _, ok := backMapping[c]; !ok && c != utf8.RuneError {
|
||||
backMapping[c] = byte(i)
|
||||
}
|
||||
var buf [8]byte
|
||||
n := utf8.EncodeRune(buf[:], c)
|
||||
if n > 3 {
|
||||
panic(fmt.Sprintf("rune %q (%U) is too long", c, c))
|
||||
}
|
||||
printf("{%d,[3]byte{0x%02x,0x%02x,0x%02x}},", n, buf[0], buf[1], buf[2])
|
||||
if i%2 == 1 {
|
||||
printf("\n")
|
||||
}
|
||||
i++
|
||||
}
|
||||
printf("},\n")
|
||||
|
||||
printf("encode: [256]uint32{\n")
|
||||
encode := make([]uint32, 0, 256)
|
||||
for c, i := range backMapping {
|
||||
encode = append(encode, uint32(i)<<24|uint32(c))
|
||||
}
|
||||
sort.Sort(byRune(encode))
|
||||
for len(encode) < cap(encode) {
|
||||
encode = append(encode, encode[len(encode)-1])
|
||||
}
|
||||
for i, enc := range encode {
|
||||
printf("0x%08x,", enc)
|
||||
if i%8 == 7 {
|
||||
printf("\n")
|
||||
}
|
||||
}
|
||||
printf("},\n}\n")
|
||||
|
||||
// Add an estimate of the size of a single Charmap{} struct value, which
|
||||
// includes two 256 elem arrays of 4 bytes and some extra fields, which
|
||||
// align to 3 uint64s on 64-bit architectures.
|
||||
w.Size += 2*4*256 + 3*8
|
||||
}
|
||||
// TODO: add proper line breaking.
|
||||
printf("var listAll = []encoding.Encoding{\n%s,\n}\n\n", strings.Join(all, ",\n"))
|
||||
}
|
||||
|
||||
type byRune []uint32
|
||||
|
||||
func (b byRune) Len() int { return len(b) }
|
||||
func (b byRune) Less(i, j int) bool { return b[i]&0xffffff < b[j]&0xffffff }
|
||||
func (b byRune) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
7410
vendor/golang.org/x/text/encoding/charmap/tables.go
generated
vendored
Normal file
7410
vendor/golang.org/x/text/encoding/charmap/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
75
vendor/golang.org/x/text/encoding/internal/internal.go
generated
vendored
Normal file
75
vendor/golang.org/x/text/encoding/internal/internal.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package internal contains code that is shared among encoding implementations.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Encoding is an implementation of the Encoding interface that adds the String
|
||||
// and ID methods to an existing encoding.
|
||||
type Encoding struct {
|
||||
encoding.Encoding
|
||||
Name string
|
||||
MIB identifier.MIB
|
||||
}
|
||||
|
||||
// _ verifies that Encoding implements identifier.Interface.
|
||||
var _ identifier.Interface = (*Encoding)(nil)
|
||||
|
||||
func (e *Encoding) String() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *Encoding) ID() (mib identifier.MIB, other string) {
|
||||
return e.MIB, ""
|
||||
}
|
||||
|
||||
// SimpleEncoding is an Encoding that combines two Transformers.
|
||||
type SimpleEncoding struct {
|
||||
Decoder transform.Transformer
|
||||
Encoder transform.Transformer
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder}
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder}
|
||||
}
|
||||
|
||||
// FuncEncoding is an Encoding that combines two functions returning a new
|
||||
// Transformer.
|
||||
type FuncEncoding struct {
|
||||
Decoder func() transform.Transformer
|
||||
Encoder func() transform.Transformer
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder()}
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder()}
|
||||
}
|
||||
|
||||
// A RepertoireError indicates a rune is not in the repertoire of a destination
|
||||
// encoding. It is associated with an encoding-specific suggested replacement
|
||||
// byte.
|
||||
type RepertoireError byte
|
||||
|
||||
// Error implements the error interrface.
|
||||
func (r RepertoireError) Error() string {
|
||||
return "encoding: rune not supported by encoding."
|
||||
}
|
||||
|
||||
// Replacement returns the replacement string associated with this error.
|
||||
func (r RepertoireError) Replacement() byte { return byte(r) }
|
||||
|
||||
var ErrASCIIReplacement = RepertoireError(encoding.ASCIISub)
|
12
vendor/golang.org/x/text/encoding/japanese/all.go
generated
vendored
Normal file
12
vendor/golang.org/x/text/encoding/japanese/all.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package japanese
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
// All is a list of all defined encodings in this package.
|
||||
var All = []encoding.Encoding{EUCJP, ISO2022JP, ShiftJIS}
|
225
vendor/golang.org/x/text/encoding/japanese/eucjp.go
generated
vendored
Normal file
225
vendor/golang.org/x/text/encoding/japanese/eucjp.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package japanese
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// EUCJP is the EUC-JP encoding.
|
||||
var EUCJP encoding.Encoding = &eucJP
|
||||
|
||||
var eucJP = internal.Encoding{
|
||||
&internal.SimpleEncoding{eucJPDecoder{}, eucJPEncoder{}},
|
||||
"EUC-JP",
|
||||
identifier.EUCPkdFmtJapanese,
|
||||
}
|
||||
|
||||
type eucJPDecoder struct{ transform.NopResetter }
|
||||
|
||||
// See https://encoding.spec.whatwg.org/#euc-jp-decoder.
|
||||
func (eucJPDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
switch c0 := src[nSrc]; {
|
||||
case c0 < utf8.RuneSelf:
|
||||
r, size = rune(c0), 1
|
||||
|
||||
case c0 == 0x8e:
|
||||
if nSrc+1 >= len(src) {
|
||||
if !atEOF {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
r, size = utf8.RuneError, 1
|
||||
break
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
switch {
|
||||
case c1 < 0xa1:
|
||||
r, size = utf8.RuneError, 1
|
||||
case c1 > 0xdf:
|
||||
r, size = utf8.RuneError, 2
|
||||
if c1 == 0xff {
|
||||
size = 1
|
||||
}
|
||||
default:
|
||||
r, size = rune(c1)+(0xff61-0xa1), 2
|
||||
}
|
||||
case c0 == 0x8f:
|
||||
if nSrc+2 >= len(src) {
|
||||
if !atEOF {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
r, size = utf8.RuneError, 1
|
||||
if p := nSrc + 1; p < len(src) && 0xa1 <= src[p] && src[p] < 0xfe {
|
||||
size = 2
|
||||
}
|
||||
break
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
if c1 < 0xa1 || 0xfe < c1 {
|
||||
r, size = utf8.RuneError, 1
|
||||
break
|
||||
}
|
||||
c2 := src[nSrc+2]
|
||||
if c2 < 0xa1 || 0xfe < c2 {
|
||||
r, size = utf8.RuneError, 2
|
||||
break
|
||||
}
|
||||
r, size = utf8.RuneError, 3
|
||||
if i := int(c1-0xa1)*94 + int(c2-0xa1); i < len(jis0212Decode) {
|
||||
r = rune(jis0212Decode[i])
|
||||
if r == 0 {
|
||||
r = utf8.RuneError
|
||||
}
|
||||
}
|
||||
|
||||
case 0xa1 <= c0 && c0 <= 0xfe:
|
||||
if nSrc+1 >= len(src) {
|
||||
if !atEOF {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
r, size = utf8.RuneError, 1
|
||||
break
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
if c1 < 0xa1 || 0xfe < c1 {
|
||||
r, size = utf8.RuneError, 1
|
||||
break
|
||||
}
|
||||
r, size = utf8.RuneError, 2
|
||||
if i := int(c0-0xa1)*94 + int(c1-0xa1); i < len(jis0208Decode) {
|
||||
r = rune(jis0208Decode[i])
|
||||
if r == 0 {
|
||||
r = utf8.RuneError
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
r, size = utf8.RuneError, 1
|
||||
}
|
||||
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break loop
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type eucJPEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (eucJPEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// func init checks that the switch covers all tables.
|
||||
switch {
|
||||
case encode0Low <= r && r < encode0High:
|
||||
if r = rune(encode0[r-encode0Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode1Low <= r && r < encode1High:
|
||||
if r = rune(encode1[r-encode1Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode2Low <= r && r < encode2High:
|
||||
if r = rune(encode2[r-encode2Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode3Low <= r && r < encode3High:
|
||||
if r = rune(encode3[r-encode3Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode4Low <= r && r < encode4High:
|
||||
if r = rune(encode4[r-encode4Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
case encode5Low <= r && r < encode5High:
|
||||
if 0xff61 <= r && r < 0xffa0 {
|
||||
goto write2
|
||||
}
|
||||
if r = rune(encode5[r-encode5Low]); r != 0 {
|
||||
goto write2or3
|
||||
}
|
||||
}
|
||||
err = internal.ErrASCIIReplacement
|
||||
break
|
||||
}
|
||||
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
|
||||
write2or3:
|
||||
if r>>tableShift == jis0208 {
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = 0x8f
|
||||
nDst++
|
||||
}
|
||||
dst[nDst+0] = 0xa1 + uint8(r>>codeShift)&codeMask
|
||||
dst[nDst+1] = 0xa1 + uint8(r)&codeMask
|
||||
nDst += 2
|
||||
continue
|
||||
|
||||
write2:
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = 0x8e
|
||||
dst[nDst+1] = uint8(r - (0xff61 - 0xa1))
|
||||
nDst += 2
|
||||
continue
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Check that the hard-coded encode switch covers all tables.
|
||||
if numEncodeTables != 6 {
|
||||
panic("bad numEncodeTables")
|
||||
}
|
||||
}
|
299
vendor/golang.org/x/text/encoding/japanese/iso2022jp.go
generated
vendored
Normal file
299
vendor/golang.org/x/text/encoding/japanese/iso2022jp.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package japanese
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// ISO2022JP is the ISO-2022-JP encoding.
|
||||
var ISO2022JP encoding.Encoding = &iso2022JP
|
||||
|
||||
var iso2022JP = internal.Encoding{
|
||||
internal.FuncEncoding{iso2022JPNewDecoder, iso2022JPNewEncoder},
|
||||
"ISO-2022-JP",
|
||||
identifier.ISO2022JP,
|
||||
}
|
||||
|
||||
func iso2022JPNewDecoder() transform.Transformer {
|
||||
return new(iso2022JPDecoder)
|
||||
}
|
||||
|
||||
func iso2022JPNewEncoder() transform.Transformer {
|
||||
return new(iso2022JPEncoder)
|
||||
}
|
||||
|
||||
const (
|
||||
asciiState = iota
|
||||
katakanaState
|
||||
jis0208State
|
||||
jis0212State
|
||||
)
|
||||
|
||||
const asciiEsc = 0x1b
|
||||
|
||||
type iso2022JPDecoder int
|
||||
|
||||
func (d *iso2022JPDecoder) Reset() {
|
||||
*d = asciiState
|
||||
}
|
||||
|
||||
func (d *iso2022JPDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
c0 := src[nSrc]
|
||||
if c0 >= utf8.RuneSelf {
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
|
||||
if c0 == asciiEsc {
|
||||
if nSrc+2 >= len(src) {
|
||||
if !atEOF {
|
||||
return nDst, nSrc, transform.ErrShortSrc
|
||||
}
|
||||
// TODO: is it correct to only skip 1??
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
size = 3
|
||||
c1 := src[nSrc+1]
|
||||
c2 := src[nSrc+2]
|
||||
switch {
|
||||
case c1 == '$' && (c2 == '@' || c2 == 'B'): // 0x24 {0x40, 0x42}
|
||||
*d = jis0208State
|
||||
continue
|
||||
case c1 == '$' && c2 == '(': // 0x24 0x28
|
||||
if nSrc+3 >= len(src) {
|
||||
if !atEOF {
|
||||
return nDst, nSrc, transform.ErrShortSrc
|
||||
}
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
size = 4
|
||||
if src[nSrc+3] == 'D' {
|
||||
*d = jis0212State
|
||||
continue
|
||||
}
|
||||
case c1 == '(' && (c2 == 'B' || c2 == 'J'): // 0x28 {0x42, 0x4A}
|
||||
*d = asciiState
|
||||
continue
|
||||
case c1 == '(' && c2 == 'I': // 0x28 0x49
|
||||
*d = katakanaState
|
||||
continue
|
||||
}
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
|
||||
switch *d {
|
||||
case asciiState:
|
||||
r, size = rune(c0), 1
|
||||
|
||||
case katakanaState:
|
||||
if c0 < 0x21 || 0x60 <= c0 {
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
r, size = rune(c0)+(0xff61-0x21), 1
|
||||
|
||||
default:
|
||||
if c0 == 0x0a {
|
||||
*d = asciiState
|
||||
r, size = rune(c0), 1
|
||||
goto write
|
||||
}
|
||||
if nSrc+1 >= len(src) {
|
||||
if !atEOF {
|
||||
return nDst, nSrc, transform.ErrShortSrc
|
||||
}
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
size = 2
|
||||
c1 := src[nSrc+1]
|
||||
i := int(c0-0x21)*94 + int(c1-0x21)
|
||||
if *d == jis0208State && i < len(jis0208Decode) {
|
||||
r = rune(jis0208Decode[i])
|
||||
} else if *d == jis0212State && i < len(jis0212Decode) {
|
||||
r = rune(jis0212Decode[i])
|
||||
} else {
|
||||
r = '\ufffd'
|
||||
goto write
|
||||
}
|
||||
if r == 0 {
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
write:
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
return nDst, nSrc, transform.ErrShortDst
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type iso2022JPEncoder int
|
||||
|
||||
func (e *iso2022JPEncoder) Reset() {
|
||||
*e = asciiState
|
||||
}
|
||||
|
||||
func (e *iso2022JPEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// func init checks that the switch covers all tables.
|
||||
//
|
||||
// http://encoding.spec.whatwg.org/#iso-2022-jp says that "the index jis0212
|
||||
// is not used by the iso-2022-jp encoder due to lack of widespread support".
|
||||
//
|
||||
// TODO: do we have to special-case U+00A5 and U+203E, as per
|
||||
// http://encoding.spec.whatwg.org/#iso-2022-jp
|
||||
// Doing so would mean that "\u00a5" would not be preserved
|
||||
// after an encode-decode round trip.
|
||||
switch {
|
||||
case encode0Low <= r && r < encode0High:
|
||||
if r = rune(encode0[r-encode0Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
case encode1Low <= r && r < encode1High:
|
||||
if r = rune(encode1[r-encode1Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
case encode2Low <= r && r < encode2High:
|
||||
if r = rune(encode2[r-encode2Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
case encode3Low <= r && r < encode3High:
|
||||
if r = rune(encode3[r-encode3Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
case encode4Low <= r && r < encode4High:
|
||||
if r = rune(encode4[r-encode4Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
case encode5Low <= r && r < encode5High:
|
||||
if 0xff61 <= r && r < 0xffa0 {
|
||||
goto writeKatakana
|
||||
}
|
||||
if r = rune(encode5[r-encode5Low]); r>>tableShift == jis0208 {
|
||||
goto writeJIS
|
||||
}
|
||||
}
|
||||
|
||||
// Switch back to ASCII state in case of error so that an ASCII
|
||||
// replacement character can be written in the correct state.
|
||||
if *e != asciiState {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
*e = asciiState
|
||||
dst[nDst+0] = asciiEsc
|
||||
dst[nDst+1] = '('
|
||||
dst[nDst+2] = 'B'
|
||||
nDst += 3
|
||||
}
|
||||
err = internal.ErrASCIIReplacement
|
||||
break
|
||||
}
|
||||
|
||||
if *e != asciiState {
|
||||
if nDst+4 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
*e = asciiState
|
||||
dst[nDst+0] = asciiEsc
|
||||
dst[nDst+1] = '('
|
||||
dst[nDst+2] = 'B'
|
||||
nDst += 3
|
||||
} else if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
|
||||
writeJIS:
|
||||
if *e != jis0208State {
|
||||
if nDst+5 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
*e = jis0208State
|
||||
dst[nDst+0] = asciiEsc
|
||||
dst[nDst+1] = '$'
|
||||
dst[nDst+2] = 'B'
|
||||
nDst += 3
|
||||
} else if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = 0x21 + uint8(r>>codeShift)&codeMask
|
||||
dst[nDst+1] = 0x21 + uint8(r)&codeMask
|
||||
nDst += 2
|
||||
continue
|
||||
|
||||
writeKatakana:
|
||||
if *e != katakanaState {
|
||||
if nDst+4 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
*e = katakanaState
|
||||
dst[nDst+0] = asciiEsc
|
||||
dst[nDst+1] = '('
|
||||
dst[nDst+2] = 'I'
|
||||
nDst += 3
|
||||
} else if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = uint8(r - (0xff61 - 0x21))
|
||||
nDst++
|
||||
continue
|
||||
}
|
||||
if atEOF && err == nil && *e != asciiState {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
} else {
|
||||
*e = asciiState
|
||||
dst[nDst+0] = asciiEsc
|
||||
dst[nDst+1] = '('
|
||||
dst[nDst+2] = 'B'
|
||||
nDst += 3
|
||||
}
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
161
vendor/golang.org/x/text/encoding/japanese/maketables.go
generated
vendored
Normal file
161
vendor/golang.org/x/text/encoding/japanese/maketables.go
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
// This program generates tables.go:
|
||||
// go run maketables.go | gofmt > tables.go
|
||||
|
||||
// TODO: Emoji extensions?
|
||||
// http://www.unicode.org/faq/emoji_dingbats.html
|
||||
// http://www.unicode.org/Public/UNIDATA/EmojiSources.txt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type entry struct {
|
||||
jisCode, table int
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// generated by go run maketables.go; DO NOT EDIT\n\n")
|
||||
fmt.Printf("// Package japanese provides Japanese encodings such as EUC-JP and Shift JIS.\n")
|
||||
fmt.Printf(`package japanese // import "golang.org/x/text/encoding/japanese"` + "\n\n")
|
||||
|
||||
reverse := [65536]entry{}
|
||||
for i := range reverse {
|
||||
reverse[i].table = -1
|
||||
}
|
||||
|
||||
tables := []struct {
|
||||
url string
|
||||
name string
|
||||
}{
|
||||
{"http://encoding.spec.whatwg.org/index-jis0208.txt", "0208"},
|
||||
{"http://encoding.spec.whatwg.org/index-jis0212.txt", "0212"},
|
||||
}
|
||||
for i, table := range tables {
|
||||
res, err := http.Get(table.url)
|
||||
if err != nil {
|
||||
log.Fatalf("%q: Get: %v", table.url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
mapping := [65536]uint16{}
|
||||
|
||||
scanner := bufio.NewScanner(res.Body)
|
||||
for scanner.Scan() {
|
||||
s := strings.TrimSpace(scanner.Text())
|
||||
if s == "" || s[0] == '#' {
|
||||
continue
|
||||
}
|
||||
x, y := 0, uint16(0)
|
||||
if _, err := fmt.Sscanf(s, "%d 0x%x", &x, &y); err != nil {
|
||||
log.Fatalf("%q: could not parse %q", table.url, s)
|
||||
}
|
||||
if x < 0 || 120*94 <= x {
|
||||
log.Fatalf("%q: JIS code %d is out of range", table.url, x)
|
||||
}
|
||||
mapping[x] = y
|
||||
if reverse[y].table == -1 {
|
||||
reverse[y] = entry{jisCode: x, table: i}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatalf("%q: scanner error: %v", table.url, err)
|
||||
}
|
||||
|
||||
fmt.Printf("// jis%sDecode is the decoding table from JIS %s code to Unicode.\n// It is defined at %s\n",
|
||||
table.name, table.name, table.url)
|
||||
fmt.Printf("var jis%sDecode = [...]uint16{\n", table.name)
|
||||
for i, m := range mapping {
|
||||
if m != 0 {
|
||||
fmt.Printf("\t%d: 0x%04X,\n", i, m)
|
||||
}
|
||||
}
|
||||
fmt.Printf("}\n\n")
|
||||
}
|
||||
|
||||
// Any run of at least separation continuous zero entries in the reverse map will
|
||||
// be a separate encode table.
|
||||
const separation = 1024
|
||||
|
||||
intervals := []interval(nil)
|
||||
low, high := -1, -1
|
||||
for i, v := range reverse {
|
||||
if v.table == -1 {
|
||||
continue
|
||||
}
|
||||
if low < 0 {
|
||||
low = i
|
||||
} else if i-high >= separation {
|
||||
if high >= 0 {
|
||||
intervals = append(intervals, interval{low, high})
|
||||
}
|
||||
low = i
|
||||
}
|
||||
high = i + 1
|
||||
}
|
||||
if high >= 0 {
|
||||
intervals = append(intervals, interval{low, high})
|
||||
}
|
||||
sort.Sort(byDecreasingLength(intervals))
|
||||
|
||||
fmt.Printf("const (\n")
|
||||
fmt.Printf("\tjis0208 = 1\n")
|
||||
fmt.Printf("\tjis0212 = 2\n")
|
||||
fmt.Printf("\tcodeMask = 0x7f\n")
|
||||
fmt.Printf("\tcodeShift = 7\n")
|
||||
fmt.Printf("\ttableShift = 14\n")
|
||||
fmt.Printf(")\n\n")
|
||||
|
||||
fmt.Printf("const numEncodeTables = %d\n\n", len(intervals))
|
||||
fmt.Printf("// encodeX are the encoding tables from Unicode to JIS code,\n")
|
||||
fmt.Printf("// sorted by decreasing length.\n")
|
||||
for i, v := range intervals {
|
||||
fmt.Printf("// encode%d: %5d entries for runes in [%5d, %5d).\n", i, v.len(), v.low, v.high)
|
||||
}
|
||||
fmt.Printf("//\n")
|
||||
fmt.Printf("// The high two bits of the value record whether the JIS code comes from the\n")
|
||||
fmt.Printf("// JIS0208 table (high bits == 1) or the JIS0212 table (high bits == 2).\n")
|
||||
fmt.Printf("// The low 14 bits are two 7-bit unsigned integers j1 and j2 that form the\n")
|
||||
fmt.Printf("// JIS code (94*j1 + j2) within that table.\n")
|
||||
fmt.Printf("\n")
|
||||
|
||||
for i, v := range intervals {
|
||||
fmt.Printf("const encode%dLow, encode%dHigh = %d, %d\n\n", i, i, v.low, v.high)
|
||||
fmt.Printf("var encode%d = [...]uint16{\n", i)
|
||||
for j := v.low; j < v.high; j++ {
|
||||
x := reverse[j]
|
||||
if x.table == -1 {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\t%d - %d: jis%s<<14 | 0x%02X<<7 | 0x%02X,\n",
|
||||
j, v.low, tables[x.table].name, x.jisCode/94, x.jisCode%94)
|
||||
}
|
||||
fmt.Printf("}\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
// interval is a half-open interval [low, high).
|
||||
type interval struct {
|
||||
low, high int
|
||||
}
|
||||
|
||||
func (i interval) len() int { return i.high - i.low }
|
||||
|
||||
// byDecreasingLength sorts intervals by decreasing length.
|
||||
type byDecreasingLength []interval
|
||||
|
||||
func (b byDecreasingLength) Len() int { return len(b) }
|
||||
func (b byDecreasingLength) Less(i, j int) bool { return b[i].len() > b[j].len() }
|
||||
func (b byDecreasingLength) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
189
vendor/golang.org/x/text/encoding/japanese/shiftjis.go
generated
vendored
Normal file
189
vendor/golang.org/x/text/encoding/japanese/shiftjis.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package japanese
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// ShiftJIS is the Shift JIS encoding, also known as Code Page 932 and
|
||||
// Windows-31J.
|
||||
var ShiftJIS encoding.Encoding = &shiftJIS
|
||||
|
||||
var shiftJIS = internal.Encoding{
|
||||
&internal.SimpleEncoding{shiftJISDecoder{}, shiftJISEncoder{}},
|
||||
"Shift JIS",
|
||||
identifier.ShiftJIS,
|
||||
}
|
||||
|
||||
type shiftJISDecoder struct{ transform.NopResetter }
|
||||
|
||||
func (shiftJISDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
switch c0 := src[nSrc]; {
|
||||
case c0 < utf8.RuneSelf:
|
||||
r, size = rune(c0), 1
|
||||
|
||||
case 0xa1 <= c0 && c0 < 0xe0:
|
||||
r, size = rune(c0)+(0xff61-0xa1), 1
|
||||
|
||||
case (0x81 <= c0 && c0 < 0xa0) || (0xe0 <= c0 && c0 < 0xfd):
|
||||
if c0 <= 0x9f {
|
||||
c0 -= 0x70
|
||||
} else {
|
||||
c0 -= 0xb0
|
||||
}
|
||||
c0 = 2*c0 - 0x21
|
||||
|
||||
if nSrc+1 >= len(src) {
|
||||
if !atEOF {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
r, size = '\ufffd', 1
|
||||
goto write
|
||||
}
|
||||
c1 := src[nSrc+1]
|
||||
switch {
|
||||
case c1 < 0x40:
|
||||
r, size = '\ufffd', 1 // c1 is ASCII so output on next round
|
||||
goto write
|
||||
case c1 < 0x7f:
|
||||
c0--
|
||||
c1 -= 0x40
|
||||
case c1 == 0x7f:
|
||||
r, size = '\ufffd', 1 // c1 is ASCII so output on next round
|
||||
goto write
|
||||
case c1 < 0x9f:
|
||||
c0--
|
||||
c1 -= 0x41
|
||||
case c1 < 0xfd:
|
||||
c1 -= 0x9f
|
||||
default:
|
||||
r, size = '\ufffd', 2
|
||||
goto write
|
||||
}
|
||||
r, size = '\ufffd', 2
|
||||
if i := int(c0)*94 + int(c1); i < len(jis0208Decode) {
|
||||
r = rune(jis0208Decode[i])
|
||||
if r == 0 {
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
case c0 == 0x80:
|
||||
r, size = 0x80, 1
|
||||
|
||||
default:
|
||||
r, size = '\ufffd', 1
|
||||
}
|
||||
write:
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break loop
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type shiftJISEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (shiftJISEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
loop:
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
// func init checks that the switch covers all tables.
|
||||
switch {
|
||||
case encode0Low <= r && r < encode0High:
|
||||
if r = rune(encode0[r-encode0Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
case encode1Low <= r && r < encode1High:
|
||||
if r = rune(encode1[r-encode1Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
case encode2Low <= r && r < encode2High:
|
||||
if r = rune(encode2[r-encode2Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
case encode3Low <= r && r < encode3High:
|
||||
if r = rune(encode3[r-encode3Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
case encode4Low <= r && r < encode4High:
|
||||
if r = rune(encode4[r-encode4Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
case encode5Low <= r && r < encode5High:
|
||||
if 0xff61 <= r && r < 0xffa0 {
|
||||
r -= 0xff61 - 0xa1
|
||||
goto write1
|
||||
}
|
||||
if r = rune(encode5[r-encode5Low]); r>>tableShift == jis0208 {
|
||||
goto write2
|
||||
}
|
||||
}
|
||||
err = internal.ErrASCIIReplacement
|
||||
break
|
||||
}
|
||||
|
||||
write1:
|
||||
if nDst >= len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = uint8(r)
|
||||
nDst++
|
||||
continue
|
||||
|
||||
write2:
|
||||
j1 := uint8(r>>codeShift) & codeMask
|
||||
j2 := uint8(r) & codeMask
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break loop
|
||||
}
|
||||
if j1 <= 61 {
|
||||
dst[nDst+0] = 129 + j1/2
|
||||
} else {
|
||||
dst[nDst+0] = 193 + j1/2
|
||||
}
|
||||
if j1&1 == 0 {
|
||||
dst[nDst+1] = j2 + j2/63 + 64
|
||||
} else {
|
||||
dst[nDst+1] = j2 + 159
|
||||
}
|
||||
nDst += 2
|
||||
continue
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
26971
vendor/golang.org/x/text/encoding/japanese/tables.go
generated
vendored
Normal file
26971
vendor/golang.org/x/text/encoding/japanese/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user