go mod
This commit is contained in:
1
vendor/github.com/bytbox/go-pop3/.gitignore
generated
vendored
Normal file
1
vendor/github.com/bytbox/go-pop3/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.swp
|
||||
4
vendor/github.com/taknb2nch/go-pop3/LICENSE → vendor/github.com/bytbox/go-pop3/LICENSE
generated
vendored
Executable file → Normal file
4
vendor/github.com/taknb2nch/go-pop3/LICENSE → vendor/github.com/bytbox/go-pop3/LICENSE
generated
vendored
Executable file → Normal file
@@ -1,6 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Takanobu Hagino
|
||||
Copyright (c) 2012 Scott Lawrence <bytbox@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
223
vendor/github.com/bytbox/go-pop3/pop3.go
generated
vendored
Normal file
223
vendor/github.com/bytbox/go-pop3/pop3.go
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// Package pop3 provides an implementation of the Post Office Protocol, Version
|
||||
// 3 as defined in RFC 1939. Commands specified as optional are not
|
||||
// implemented; however, this implementation may be trivially extended to
|
||||
// support them.
|
||||
|
||||
package pop3
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The POP3 client.
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
bin *bufio.Reader
|
||||
}
|
||||
|
||||
// Dial creates an unsecured connection to the POP3 server at the given address
|
||||
// and returns the corresponding Client.
|
||||
func Dial(addr string) (*Client, error) {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewClient(conn)
|
||||
}
|
||||
|
||||
// DialTLS creates a TLS-secured connection to the POP3 server at the given
|
||||
// address and returns the corresponding Client.
|
||||
func DialTLS(addr string) (*Client, error) {
|
||||
conn, err := tls.Dial("tcp", addr, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewClient(conn)
|
||||
}
|
||||
|
||||
// NewClient returns a new Client object using an existing connection.
|
||||
func NewClient(conn net.Conn) (*Client, error) {
|
||||
client := &Client{
|
||||
bin: bufio.NewReader(conn),
|
||||
conn: conn,
|
||||
}
|
||||
// send dud command, to read a line
|
||||
_, err := client.Cmd("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Convenience function to synchronously run an arbitrary command and wait for
|
||||
// output. The terminating CRLF must be included in the format string.
|
||||
//
|
||||
// Output sent after the first line must be retrieved via readLines.
|
||||
func (c *Client) Cmd(format string, args ...interface{}) (string, error) {
|
||||
fmt.Fprintf(c.conn, format, args...)
|
||||
line, _, err := c.bin.ReadLine()
|
||||
if err != nil { return "", err }
|
||||
l := string(line)
|
||||
if l[0:3] != "+OK" {
|
||||
err = errors.New(l[5:])
|
||||
}
|
||||
if len(l) >= 4 {
|
||||
return l[4:], err
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (c *Client) ReadLines() (lines []string, err error) {
|
||||
lines = make([]string, 0)
|
||||
l, _, err := c.bin.ReadLine()
|
||||
line := string(l)
|
||||
for err == nil && line != "." {
|
||||
if len(line) > 0 && line[0] == '.' {
|
||||
line = line[1:]
|
||||
}
|
||||
lines = append(lines, line)
|
||||
l, _, err = c.bin.ReadLine()
|
||||
line = string(l)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// User sends the given username to the server. Generally, there is no reason
|
||||
// not to use the Auth convenience method.
|
||||
func (c *Client) User(username string) (err error) {
|
||||
_, err = c.Cmd("USER %s\r\n", username)
|
||||
return
|
||||
}
|
||||
|
||||
// Pass sends the given password to the server. The password is sent
|
||||
// unencrypted unless the connection is already secured by TLS (via DialTLS or
|
||||
// some other mechanism). Generally, there is no reason not to use the Auth
|
||||
// convenience method.
|
||||
func (c *Client) Pass(password string) (err error) {
|
||||
_, err = c.Cmd("PASS %s\r\n", password)
|
||||
return
|
||||
}
|
||||
|
||||
// Auth sends the given username and password to the server, calling the User
|
||||
// and Pass methods as appropriate.
|
||||
func (c *Client) Auth(username, password string) (err error) {
|
||||
err = c.User(username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.Pass(password)
|
||||
return
|
||||
}
|
||||
|
||||
// Stat retrieves a drop listing for the current maildrop, consisting of the
|
||||
// number of messages and the total size (in octets) of the maildrop.
|
||||
// Information provided besides the number of messages and the size of the
|
||||
// maildrop is ignored. In the event of an error, all returned numeric values
|
||||
// will be 0.
|
||||
func (c *Client) Stat() (count, size int, err error) {
|
||||
l, err := c.Cmd("STAT\r\n")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
parts := strings.Fields(l)
|
||||
count, err = strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return 0, 0, errors.New("Invalid server response")
|
||||
}
|
||||
size, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return 0, 0, errors.New("Invalid server response")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List returns the size of the given message, if it exists. If the message
|
||||
// does not exist, or another error is encountered, the returned size will be
|
||||
// 0.
|
||||
func (c *Client) List(msg int) (size int, err error) {
|
||||
l, err := c.Cmd("LIST %d\r\n", msg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size, err = strconv.Atoi(strings.Fields(l)[1])
|
||||
if err != nil {
|
||||
return 0, errors.New("Invalid server response")
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// ListAll returns a list of all messages and their sizes.
|
||||
func (c *Client) ListAll() (msgs []int, sizes []int, err error) {
|
||||
_, err = c.Cmd("LIST\r\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lines, err := c.ReadLines()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msgs = make([]int, len(lines), len(lines))
|
||||
sizes = make([]int, len(lines), len(lines))
|
||||
for i, l := range lines {
|
||||
var m, s int
|
||||
fs := strings.Fields(l)
|
||||
m, err = strconv.Atoi(fs[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s, err = strconv.Atoi(fs[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msgs[i] = m
|
||||
sizes[i] = s
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Retr downloads and returns the given message. The lines are separated by LF,
|
||||
// whatever the server sent.
|
||||
func (c *Client) Retr(msg int) (text string, err error) {
|
||||
_, err = c.Cmd("RETR %d\r\n", msg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lines, err := c.ReadLines()
|
||||
text = strings.Join(lines, "\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Dele marks the given message as deleted.
|
||||
func (c *Client) Dele(msg int) (err error) {
|
||||
_, err = c.Cmd("DELE %d\r\n", msg)
|
||||
return
|
||||
}
|
||||
|
||||
// Noop does nothing, but will prolong the end of the connection if the server
|
||||
// has a timeout set.
|
||||
func (c *Client) Noop() (err error) {
|
||||
_, err = c.Cmd("NOOP\r\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Rset unmarks any messages marked for deletion previously in this session.
|
||||
func (c *Client) Rset() (err error) {
|
||||
_, err = c.Cmd("RSET\r\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Quit sends the QUIT message to the POP3 server and closes the connection.
|
||||
func (c *Client) Quit() error {
|
||||
_, err := c.Cmd("QUIT\r\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn.Close()
|
||||
return nil
|
||||
}
|
||||
17
vendor/github.com/emersion/go-imap/.build.yml
generated
vendored
Normal file
17
vendor/github.com/emersion/go-imap/.build.yml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
image: alpine/edge
|
||||
packages:
|
||||
- go
|
||||
sources:
|
||||
- https://github.com/emersion/go-imap
|
||||
artifacts:
|
||||
- coverage.html
|
||||
tasks:
|
||||
- build: |
|
||||
cd go-imap
|
||||
go build -race -v ./...
|
||||
- test: |
|
||||
cd go-imap
|
||||
go test -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- coverage: |
|
||||
cd go-imap
|
||||
go tool cover -html=coverage.txt -o ~/coverage.html
|
||||
28
vendor/github.com/emersion/go-imap/.gitignore
generated
vendored
Normal file
28
vendor/github.com/emersion/go-imap/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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
|
||||
|
||||
/client.go
|
||||
/server.go
|
||||
coverage.txt
|
||||
23
vendor/github.com/emersion/go-imap/LICENSE
generated
vendored
Normal file
23
vendor/github.com/emersion/go-imap/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 The Go-IMAP Authors
|
||||
Copyright (c) 2016 emersion
|
||||
Copyright (c) 2016 Proton Technologies AG
|
||||
|
||||
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.
|
||||
177
vendor/github.com/emersion/go-imap/README.md
generated
vendored
Normal file
177
vendor/github.com/emersion/go-imap/README.md
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
# go-imap
|
||||
|
||||
[](https://godocs.io/github.com/emersion/go-imap)
|
||||
[](https://builds.sr.ht/~emersion/go-imap/commits?)
|
||||
|
||||
An [IMAP4rev1](https://tools.ietf.org/html/rfc3501) library written in Go. It
|
||||
can be used to build a client and/or a server.
|
||||
|
||||
## Usage
|
||||
|
||||
### Client [](https://godocs.io/github.com/emersion/go-imap/client)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/emersion/go-imap/client"
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Connecting to server...")
|
||||
|
||||
// Connect to server
|
||||
c, err := client.DialTLS("mail.example.org:993", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Connected")
|
||||
|
||||
// Don't forget to logout
|
||||
defer c.Logout()
|
||||
|
||||
// Login
|
||||
if err := c.Login("username", "password"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Logged in")
|
||||
|
||||
// List mailboxes
|
||||
mailboxes := make(chan *imap.MailboxInfo, 10)
|
||||
done := make(chan error, 1)
|
||||
go func () {
|
||||
done <- c.List("", "*", mailboxes)
|
||||
}()
|
||||
|
||||
log.Println("Mailboxes:")
|
||||
for m := range mailboxes {
|
||||
log.Println("* " + m.Name)
|
||||
}
|
||||
|
||||
if err := <-done; err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Select INBOX
|
||||
mbox, err := c.Select("INBOX", false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("Flags for INBOX:", mbox.Flags)
|
||||
|
||||
// Get the last 4 messages
|
||||
from := uint32(1)
|
||||
to := mbox.Messages
|
||||
if mbox.Messages > 3 {
|
||||
// We're using unsigned integers here, only subtract if the result is > 0
|
||||
from = mbox.Messages - 3
|
||||
}
|
||||
seqset := new(imap.SeqSet)
|
||||
seqset.AddRange(from, to)
|
||||
|
||||
messages := make(chan *imap.Message, 10)
|
||||
done = make(chan error, 1)
|
||||
go func() {
|
||||
done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)
|
||||
}()
|
||||
|
||||
log.Println("Last 4 messages:")
|
||||
for msg := range messages {
|
||||
log.Println("* " + msg.Envelope.Subject)
|
||||
}
|
||||
|
||||
if err := <-done; err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Println("Done!")
|
||||
}
|
||||
```
|
||||
|
||||
### Server [](https://godocs.io/github.com/emersion/go-imap/server)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/emersion/go-imap/server"
|
||||
"github.com/emersion/go-imap/backend/memory"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a memory backend
|
||||
be := memory.New()
|
||||
|
||||
// Create a new server
|
||||
s := server.New(be)
|
||||
s.Addr = ":1143"
|
||||
// Since we will use this server for testing only, we can allow plain text
|
||||
// authentication over unencrypted connections
|
||||
s.AllowInsecureAuth = true
|
||||
|
||||
log.Println("Starting IMAP server at localhost:1143")
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can now use `telnet localhost 1143` to manually connect to the server.
|
||||
|
||||
## Extensions
|
||||
|
||||
Support for several IMAP extensions is included in go-imap itself. This
|
||||
includes:
|
||||
|
||||
* [APPENDLIMIT](https://tools.ietf.org/html/rfc7889)
|
||||
* [CHILDREN](https://tools.ietf.org/html/rfc3348)
|
||||
* [ENABLE](https://tools.ietf.org/html/rfc5161)
|
||||
* [IDLE](https://tools.ietf.org/html/rfc2177)
|
||||
* [IMPORTANT](https://tools.ietf.org/html/rfc8457)
|
||||
* [LITERAL+](https://tools.ietf.org/html/rfc7888)
|
||||
* [MOVE](https://tools.ietf.org/html/rfc6851)
|
||||
* [SASL-IR](https://tools.ietf.org/html/rfc4959)
|
||||
* [SPECIAL-USE](https://tools.ietf.org/html/rfc6154)
|
||||
* [UNSELECT](https://tools.ietf.org/html/rfc3691)
|
||||
|
||||
Support for other extensions is provided via separate packages. See below.
|
||||
|
||||
## Extending go-imap
|
||||
|
||||
### Extensions
|
||||
|
||||
Commands defined in IMAP extensions are available in other packages. See [the
|
||||
wiki](https://github.com/emersion/go-imap/wiki/Using-extensions#using-client-extensions)
|
||||
to learn how to use them.
|
||||
|
||||
* [COMPRESS](https://github.com/emersion/go-imap-compress)
|
||||
* [ID](https://github.com/ProtonMail/go-imap-id)
|
||||
* [METADATA](https://github.com/emersion/go-imap-metadata)
|
||||
* [NAMESPACE](https://github.com/foxcpp/go-imap-namespace)
|
||||
* [QUOTA](https://github.com/emersion/go-imap-quota)
|
||||
* [SORT and THREAD](https://github.com/emersion/go-imap-sortthread)
|
||||
* [UIDPLUS](https://github.com/emersion/go-imap-uidplus)
|
||||
|
||||
### Server backends
|
||||
|
||||
* [Memory](https://github.com/emersion/go-imap/tree/master/backend/memory) (for testing)
|
||||
* [Multi](https://github.com/emersion/go-imap-multi)
|
||||
* [PGP](https://github.com/emersion/go-imap-pgp)
|
||||
* [Proxy](https://github.com/emersion/go-imap-proxy)
|
||||
|
||||
### Related projects
|
||||
|
||||
* [go-message](https://github.com/emersion/go-message) - parsing and formatting MIME and mail messages
|
||||
* [go-msgauth](https://github.com/emersion/go-msgauth) - handle DKIM, DMARC and Authentication-Results
|
||||
* [go-pgpmail](https://github.com/emersion/go-pgpmail) - decrypting and encrypting mails with OpenPGP
|
||||
* [go-sasl](https://github.com/emersion/go-sasl) - sending and receiving SASL authentications
|
||||
* [go-smtp](https://github.com/emersion/go-smtp) - building SMTP clients and servers
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
689
vendor/github.com/emersion/go-imap/client/client.go
generated
vendored
Normal file
689
vendor/github.com/emersion/go-imap/client/client.go
generated
vendored
Normal file
@@ -0,0 +1,689 @@
|
||||
// Package client provides an IMAP client.
|
||||
//
|
||||
// It is not safe to use the same Client from multiple goroutines. In general,
|
||||
// the IMAP protocol doesn't make it possible to send multiple independent
|
||||
// IMAP commands on the same connection.
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
"github.com/emersion/go-imap/responses"
|
||||
)
|
||||
|
||||
// errClosed is used when a connection is closed while waiting for a command
|
||||
// response.
|
||||
var errClosed = fmt.Errorf("imap: connection closed")
|
||||
|
||||
// errUnregisterHandler is returned by a response handler to unregister itself.
|
||||
var errUnregisterHandler = fmt.Errorf("imap: unregister handler")
|
||||
|
||||
// Update is an unilateral server update.
|
||||
type Update interface {
|
||||
update()
|
||||
}
|
||||
|
||||
// StatusUpdate is delivered when a status update is received.
|
||||
type StatusUpdate struct {
|
||||
Status *imap.StatusResp
|
||||
}
|
||||
|
||||
func (u *StatusUpdate) update() {}
|
||||
|
||||
// MailboxUpdate is delivered when a mailbox status changes.
|
||||
type MailboxUpdate struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (u *MailboxUpdate) update() {}
|
||||
|
||||
// ExpungeUpdate is delivered when a message is deleted.
|
||||
type ExpungeUpdate struct {
|
||||
SeqNum uint32
|
||||
}
|
||||
|
||||
func (u *ExpungeUpdate) update() {}
|
||||
|
||||
// MessageUpdate is delivered when a message attribute changes.
|
||||
type MessageUpdate struct {
|
||||
Message *imap.Message
|
||||
}
|
||||
|
||||
func (u *MessageUpdate) update() {}
|
||||
|
||||
// Client is an IMAP client.
|
||||
type Client struct {
|
||||
conn *imap.Conn
|
||||
isTLS bool
|
||||
serverName string
|
||||
|
||||
loggedOut chan struct{}
|
||||
continues chan<- bool
|
||||
upgrading bool
|
||||
|
||||
handlers []responses.Handler
|
||||
handlersLocker sync.Mutex
|
||||
|
||||
// The current connection state.
|
||||
state imap.ConnState
|
||||
// The selected mailbox, if there is one.
|
||||
mailbox *imap.MailboxStatus
|
||||
// The cached server capabilities.
|
||||
caps map[string]bool
|
||||
// state, mailbox and caps may be accessed in different goroutines. Protect
|
||||
// access.
|
||||
locker sync.Mutex
|
||||
|
||||
// A channel to which unilateral updates from the server will be sent. An
|
||||
// update can be one of: *StatusUpdate, *MailboxUpdate, *MessageUpdate,
|
||||
// *ExpungeUpdate. Note that blocking this channel blocks the whole client,
|
||||
// so it's recommended to use a separate goroutine and a buffered channel to
|
||||
// prevent deadlocks.
|
||||
Updates chan<- Update
|
||||
|
||||
// ErrorLog specifies an optional logger for errors accepting connections and
|
||||
// unexpected behavior from handlers. By default, logging goes to os.Stderr
|
||||
// via the log package's standard logger. The logger must be safe to use
|
||||
// simultaneously from multiple goroutines.
|
||||
ErrorLog imap.Logger
|
||||
|
||||
// Timeout specifies a maximum amount of time to wait on a command.
|
||||
//
|
||||
// A Timeout of zero means no timeout. This is the default.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *Client) registerHandler(h responses.Handler) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.handlersLocker.Lock()
|
||||
c.handlers = append(c.handlers, h)
|
||||
c.handlersLocker.Unlock()
|
||||
}
|
||||
|
||||
func (c *Client) handle(resp imap.Resp) error {
|
||||
c.handlersLocker.Lock()
|
||||
for i := len(c.handlers) - 1; i >= 0; i-- {
|
||||
if err := c.handlers[i].Handle(resp); err != responses.ErrUnhandled {
|
||||
if err == errUnregisterHandler {
|
||||
c.handlers = append(c.handlers[:i], c.handlers[i+1:]...)
|
||||
err = nil
|
||||
}
|
||||
c.handlersLocker.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.handlersLocker.Unlock()
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
|
||||
func (c *Client) reader() {
|
||||
defer close(c.loggedOut)
|
||||
// Loop while connected.
|
||||
for {
|
||||
connected, err := c.readOnce()
|
||||
if err != nil {
|
||||
c.ErrorLog.Println("error reading response:", err)
|
||||
}
|
||||
if !connected {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) readOnce() (bool, error) {
|
||||
if c.State() == imap.LogoutState {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
resp, err := imap.ReadResp(c.conn.Reader)
|
||||
if err == io.EOF || c.State() == imap.LogoutState {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
if imap.IsParseError(err) {
|
||||
return true, err
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.handle(resp); err != nil && err != responses.ErrUnhandled {
|
||||
c.ErrorLog.Println("cannot handle response ", resp, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *Client) writeReply(reply []byte) error {
|
||||
if _, err := c.conn.Writer.Write(reply); err != nil {
|
||||
return err
|
||||
}
|
||||
// Flush reply
|
||||
return c.conn.Writer.Flush()
|
||||
}
|
||||
|
||||
type handleResult struct {
|
||||
status *imap.StatusResp
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *Client) execute(cmdr imap.Commander, h responses.Handler) (*imap.StatusResp, error) {
|
||||
cmd := cmdr.Command()
|
||||
cmd.Tag = generateTag()
|
||||
|
||||
var replies <-chan []byte
|
||||
if replier, ok := h.(responses.Replier); ok {
|
||||
replies = replier.Replies()
|
||||
}
|
||||
|
||||
if c.Timeout > 0 {
|
||||
err := c.conn.SetDeadline(time.Now().Add(c.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// It's possible the client had a timeout set from a previous command, but no
|
||||
// longer does. Ensure we respect that. The zero time means no deadline.
|
||||
if err := c.conn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are upgrading.
|
||||
upgrading := c.upgrading
|
||||
|
||||
// Add handler before sending command, to be sure to get the response in time
|
||||
// (in tests, the response is sent right after our command is received, so
|
||||
// sometimes the response was received before the setup of this handler)
|
||||
doneHandle := make(chan handleResult, 1)
|
||||
unregister := make(chan struct{})
|
||||
c.registerHandler(responses.HandlerFunc(func(resp imap.Resp) error {
|
||||
select {
|
||||
case <-unregister:
|
||||
// If an error occured while sending the command, abort
|
||||
return errUnregisterHandler
|
||||
default:
|
||||
}
|
||||
|
||||
if s, ok := resp.(*imap.StatusResp); ok && s.Tag == cmd.Tag {
|
||||
// This is the command's status response, we're done
|
||||
doneHandle <- handleResult{s, nil}
|
||||
// Special handling of connection upgrading.
|
||||
if upgrading {
|
||||
c.upgrading = false
|
||||
// Wait for upgrade to finish.
|
||||
c.conn.Wait()
|
||||
}
|
||||
// Cancel any pending literal write
|
||||
select {
|
||||
case c.continues <- false:
|
||||
default:
|
||||
}
|
||||
return errUnregisterHandler
|
||||
}
|
||||
|
||||
if h != nil {
|
||||
// Pass the response to the response handler
|
||||
if err := h.Handle(resp); err != nil && err != responses.ErrUnhandled {
|
||||
// If the response handler returns an error, abort
|
||||
doneHandle <- handleResult{nil, err}
|
||||
return errUnregisterHandler
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return responses.ErrUnhandled
|
||||
}))
|
||||
|
||||
// Send the command to the server
|
||||
if err := cmd.WriteTo(c.conn.Writer); err != nil {
|
||||
// Error while sending the command
|
||||
close(unregister)
|
||||
|
||||
if err, ok := err.(imap.LiteralLengthErr); ok {
|
||||
// Expected > Actual
|
||||
// The server is waiting for us to write
|
||||
// more bytes, we don't have them. Run.
|
||||
// Expected < Actual
|
||||
// We are about to send a potentially truncated message, we don't
|
||||
// want this (ths terminating CRLF is not sent at this point).
|
||||
c.conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
// Flush writer if we are upgrading
|
||||
if upgrading {
|
||||
if err := c.conn.Writer.Flush(); err != nil {
|
||||
// Error while sending the command
|
||||
close(unregister)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case reply := <-replies:
|
||||
// Response handler needs to send a reply (Used for AUTHENTICATE)
|
||||
if err := c.writeReply(reply); err != nil {
|
||||
close(unregister)
|
||||
return nil, err
|
||||
}
|
||||
case <-c.loggedOut:
|
||||
// If the connection is closed (such as from an I/O error), ensure we
|
||||
// realize this and don't block waiting on a response that will never
|
||||
// come. loggedOut is a channel that closes when the reader goroutine
|
||||
// ends.
|
||||
close(unregister)
|
||||
return nil, errClosed
|
||||
case result := <-doneHandle:
|
||||
return result.status, result.err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// State returns the current connection state.
|
||||
func (c *Client) State() imap.ConnState {
|
||||
c.locker.Lock()
|
||||
state := c.state
|
||||
c.locker.Unlock()
|
||||
return state
|
||||
}
|
||||
|
||||
// Mailbox returns the selected mailbox. It returns nil if there isn't one.
|
||||
func (c *Client) Mailbox() *imap.MailboxStatus {
|
||||
// c.Mailbox fields are not supposed to change, so we can return the pointer.
|
||||
c.locker.Lock()
|
||||
mbox := c.mailbox
|
||||
c.locker.Unlock()
|
||||
return mbox
|
||||
}
|
||||
|
||||
// SetState sets this connection's internal state.
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (c *Client) SetState(state imap.ConnState, mailbox *imap.MailboxStatus) {
|
||||
c.locker.Lock()
|
||||
c.state = state
|
||||
c.mailbox = mailbox
|
||||
c.locker.Unlock()
|
||||
}
|
||||
|
||||
// Execute executes a generic command. cmdr is a value that can be converted to
|
||||
// a raw command and h is a response handler. The function returns when the
|
||||
// command has completed or failed, in this case err is nil. A non-nil err value
|
||||
// indicates a network error.
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (c *Client) Execute(cmdr imap.Commander, h responses.Handler) (*imap.StatusResp, error) {
|
||||
return c.execute(cmdr, h)
|
||||
}
|
||||
|
||||
func (c *Client) handleContinuationReqs() {
|
||||
c.registerHandler(responses.HandlerFunc(func(resp imap.Resp) error {
|
||||
if _, ok := resp.(*imap.ContinuationReq); ok {
|
||||
go func() {
|
||||
c.continues <- true
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
return responses.ErrUnhandled
|
||||
}))
|
||||
}
|
||||
|
||||
func (c *Client) gotStatusCaps(args []interface{}) {
|
||||
c.locker.Lock()
|
||||
|
||||
c.caps = make(map[string]bool)
|
||||
for _, cap := range args {
|
||||
if cap, ok := cap.(string); ok {
|
||||
c.caps[cap] = true
|
||||
}
|
||||
}
|
||||
|
||||
c.locker.Unlock()
|
||||
}
|
||||
|
||||
// The server can send unilateral data. This function handles it.
|
||||
func (c *Client) handleUnilateral() {
|
||||
c.registerHandler(responses.HandlerFunc(func(resp imap.Resp) error {
|
||||
switch resp := resp.(type) {
|
||||
case *imap.StatusResp:
|
||||
if resp.Tag != "*" {
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
|
||||
switch resp.Type {
|
||||
case imap.StatusRespOk, imap.StatusRespNo, imap.StatusRespBad:
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &StatusUpdate{resp}
|
||||
}
|
||||
case imap.StatusRespBye:
|
||||
c.locker.Lock()
|
||||
c.state = imap.LogoutState
|
||||
c.mailbox = nil
|
||||
c.locker.Unlock()
|
||||
|
||||
c.conn.Close()
|
||||
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &StatusUpdate{resp}
|
||||
}
|
||||
default:
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
case *imap.DataResp:
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok {
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "CAPABILITY":
|
||||
c.gotStatusCaps(fields)
|
||||
case "EXISTS":
|
||||
if c.Mailbox() == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if messages, err := imap.ParseNumber(fields[0]); err == nil {
|
||||
c.locker.Lock()
|
||||
c.mailbox.Messages = messages
|
||||
c.locker.Unlock()
|
||||
|
||||
c.mailbox.ItemsLocker.Lock()
|
||||
c.mailbox.Items[imap.StatusMessages] = nil
|
||||
c.mailbox.ItemsLocker.Unlock()
|
||||
}
|
||||
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &MailboxUpdate{c.Mailbox()}
|
||||
}
|
||||
case "RECENT":
|
||||
if c.Mailbox() == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if recent, err := imap.ParseNumber(fields[0]); err == nil {
|
||||
c.locker.Lock()
|
||||
c.mailbox.Recent = recent
|
||||
c.locker.Unlock()
|
||||
|
||||
c.mailbox.ItemsLocker.Lock()
|
||||
c.mailbox.Items[imap.StatusRecent] = nil
|
||||
c.mailbox.ItemsLocker.Unlock()
|
||||
}
|
||||
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &MailboxUpdate{c.Mailbox()}
|
||||
}
|
||||
case "EXPUNGE":
|
||||
seqNum, _ := imap.ParseNumber(fields[0])
|
||||
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &ExpungeUpdate{seqNum}
|
||||
}
|
||||
case "FETCH":
|
||||
seqNum, _ := imap.ParseNumber(fields[0])
|
||||
fields, _ := fields[1].([]interface{})
|
||||
|
||||
msg := &imap.Message{SeqNum: seqNum}
|
||||
if err := msg.Parse(fields); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if c.Updates != nil {
|
||||
c.Updates <- &MessageUpdate{msg}
|
||||
}
|
||||
default:
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
default:
|
||||
return responses.ErrUnhandled
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func (c *Client) handleGreetAndStartReading() error {
|
||||
var greetErr error
|
||||
gotGreet := false
|
||||
|
||||
c.registerHandler(responses.HandlerFunc(func(resp imap.Resp) error {
|
||||
status, ok := resp.(*imap.StatusResp)
|
||||
if !ok {
|
||||
greetErr = fmt.Errorf("invalid greeting received from server: not a status response")
|
||||
return errUnregisterHandler
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
switch status.Type {
|
||||
case imap.StatusRespPreauth:
|
||||
c.state = imap.AuthenticatedState
|
||||
case imap.StatusRespBye:
|
||||
c.state = imap.LogoutState
|
||||
case imap.StatusRespOk:
|
||||
c.state = imap.NotAuthenticatedState
|
||||
default:
|
||||
c.state = imap.LogoutState
|
||||
c.locker.Unlock()
|
||||
greetErr = fmt.Errorf("invalid greeting received from server: %v", status.Type)
|
||||
return errUnregisterHandler
|
||||
}
|
||||
c.locker.Unlock()
|
||||
|
||||
if status.Code == imap.CodeCapability {
|
||||
c.gotStatusCaps(status.Arguments)
|
||||
}
|
||||
|
||||
gotGreet = true
|
||||
return errUnregisterHandler
|
||||
}))
|
||||
|
||||
// call `readOnce` until we get the greeting or an error
|
||||
for !gotGreet {
|
||||
connected, err := c.readOnce()
|
||||
// Check for read errors
|
||||
if err != nil {
|
||||
// return read errors
|
||||
return err
|
||||
}
|
||||
// Check for invalid greet
|
||||
if greetErr != nil {
|
||||
// return read errors
|
||||
return greetErr
|
||||
}
|
||||
// Check if connection was closed.
|
||||
if !connected {
|
||||
// connection closed.
|
||||
return io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
// We got the greeting, now start the reader goroutine.
|
||||
go c.reader()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
|
||||
// tunnel.
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (c *Client) Upgrade(upgrader imap.ConnUpgrader) error {
|
||||
return c.conn.Upgrade(upgrader)
|
||||
}
|
||||
|
||||
// Writer returns the imap.Writer for this client's connection.
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
// libraries implementing extensions of the IMAP protocol.
|
||||
func (c *Client) Writer() *imap.Writer {
|
||||
return c.conn.Writer
|
||||
}
|
||||
|
||||
// IsTLS checks if this client's connection has TLS enabled.
|
||||
func (c *Client) IsTLS() bool {
|
||||
return c.isTLS
|
||||
}
|
||||
|
||||
// LoggedOut returns a channel which is closed when the connection to the server
|
||||
// is closed.
|
||||
func (c *Client) LoggedOut() <-chan struct{} {
|
||||
return c.loggedOut
|
||||
}
|
||||
|
||||
// SetDebug defines an io.Writer to which all network activity will be logged.
|
||||
// If nil is provided, network activity will not be logged.
|
||||
func (c *Client) SetDebug(w io.Writer) {
|
||||
// Need to send a command to unblock the reader goroutine.
|
||||
cmd := new(commands.Noop)
|
||||
err := c.Upgrade(func(conn net.Conn) (net.Conn, error) {
|
||||
// Flag connection as in upgrading
|
||||
c.upgrading = true
|
||||
if status, err := c.execute(cmd, nil); err != nil {
|
||||
return nil, err
|
||||
} else if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wait for reader to block.
|
||||
c.conn.WaitReady()
|
||||
|
||||
c.conn.SetDebug(w)
|
||||
return conn, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("SetDebug:", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// New creates a new client from an existing connection.
|
||||
func New(conn net.Conn) (*Client, error) {
|
||||
continues := make(chan bool)
|
||||
w := imap.NewClientWriter(nil, continues)
|
||||
r := imap.NewReader(nil)
|
||||
|
||||
c := &Client{
|
||||
conn: imap.NewConn(conn, r, w),
|
||||
loggedOut: make(chan struct{}),
|
||||
continues: continues,
|
||||
state: imap.ConnectingState,
|
||||
ErrorLog: log.New(os.Stderr, "imap/client: ", log.LstdFlags),
|
||||
}
|
||||
|
||||
c.handleContinuationReqs()
|
||||
c.handleUnilateral()
|
||||
if err := c.handleGreetAndStartReading(); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
plusOk, _ := c.Support("LITERAL+")
|
||||
minusOk, _ := c.Support("LITERAL-")
|
||||
// We don't use non-sync literal if it is bigger than 4096 bytes, so
|
||||
// LITERAL- is fine too.
|
||||
c.conn.AllowAsyncLiterals = plusOk || minusOk
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Dial connects to an IMAP server using an unencrypted connection.
|
||||
func Dial(addr string) (*Client, error) {
|
||||
return DialWithDialer(new(net.Dialer), addr)
|
||||
}
|
||||
|
||||
type Dialer interface {
|
||||
// Dial connects to the given address.
|
||||
Dial(network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// DialWithDialer connects to an IMAP server using an unencrypted connection
|
||||
// using dialer.Dial.
|
||||
//
|
||||
// Among other uses, this allows to apply a dial timeout.
|
||||
func DialWithDialer(dialer Dialer, addr string) (*Client, error) {
|
||||
conn, err := dialer.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't return to the caller until we try to receive a greeting. As such,
|
||||
// there is no way to set the client's Timeout for that action. As a
|
||||
// workaround, if the dialer has a timeout set, use that for the connection's
|
||||
// deadline.
|
||||
if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 {
|
||||
err := conn.SetDeadline(time.Now().Add(netDialer.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c, err := New(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.serverName, _, _ = net.SplitHostPort(addr)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DialTLS connects to an IMAP server using an encrypted connection.
|
||||
func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
|
||||
return DialWithDialerTLS(new(net.Dialer), addr, tlsConfig)
|
||||
}
|
||||
|
||||
// DialWithDialerTLS connects to an IMAP server using an encrypted connection
|
||||
// using dialer.Dial.
|
||||
//
|
||||
// Among other uses, this allows to apply a dial timeout.
|
||||
func DialWithDialerTLS(dialer Dialer, addr string, tlsConfig *tls.Config) (*Client, error) {
|
||||
conn, err := dialer.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverName, _, _ := net.SplitHostPort(addr)
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
if tlsConfig.ServerName == "" {
|
||||
tlsConfig = tlsConfig.Clone()
|
||||
tlsConfig.ServerName = serverName
|
||||
}
|
||||
tlsConn := tls.Client(conn, tlsConfig)
|
||||
|
||||
// We don't return to the caller until we try to receive a greeting. As such,
|
||||
// there is no way to set the client's Timeout for that action. As a
|
||||
// workaround, if the dialer has a timeout set, use that for the connection's
|
||||
// deadline.
|
||||
if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 {
|
||||
err := tlsConn.SetDeadline(time.Now().Add(netDialer.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c, err := New(tlsConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.isTLS = true
|
||||
c.serverName = serverName
|
||||
return c, nil
|
||||
}
|
||||
88
vendor/github.com/emersion/go-imap/client/cmd_any.go
generated
vendored
Normal file
88
vendor/github.com/emersion/go-imap/client/cmd_any.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
)
|
||||
|
||||
// ErrAlreadyLoggedOut is returned if Logout is called when the client is
|
||||
// already logged out.
|
||||
var ErrAlreadyLoggedOut = errors.New("Already logged out")
|
||||
|
||||
// Capability requests a listing of capabilities that the server supports.
|
||||
// Capabilities are often returned by the server with the greeting or with the
|
||||
// STARTTLS and LOGIN responses, so usually explicitly requesting capabilities
|
||||
// isn't needed.
|
||||
//
|
||||
// Most of the time, Support should be used instead.
|
||||
func (c *Client) Capability() (map[string]bool, error) {
|
||||
cmd := &commands.Capability{}
|
||||
|
||||
if status, err := c.execute(cmd, nil); err != nil {
|
||||
return nil, err
|
||||
} else if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
caps := c.caps
|
||||
c.locker.Unlock()
|
||||
return caps, nil
|
||||
}
|
||||
|
||||
// Support checks if cap is a capability supported by the server. If the server
|
||||
// hasn't sent its capabilities yet, Support requests them.
|
||||
func (c *Client) Support(cap string) (bool, error) {
|
||||
c.locker.Lock()
|
||||
ok := c.caps != nil
|
||||
c.locker.Unlock()
|
||||
|
||||
// If capabilities are not cached, request them
|
||||
if !ok {
|
||||
if _, err := c.Capability(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
supported := c.caps[cap]
|
||||
c.locker.Unlock()
|
||||
|
||||
return supported, nil
|
||||
}
|
||||
|
||||
// Noop always succeeds and does nothing.
|
||||
//
|
||||
// It can be used as a periodic poll for new messages or message status updates
|
||||
// during a period of inactivity. It can also be used to reset any inactivity
|
||||
// autologout timer on the server.
|
||||
func (c *Client) Noop() error {
|
||||
cmd := new(commands.Noop)
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Logout gracefully closes the connection.
|
||||
func (c *Client) Logout() error {
|
||||
if c.State() == imap.LogoutState {
|
||||
return ErrAlreadyLoggedOut
|
||||
}
|
||||
|
||||
cmd := new(commands.Logout)
|
||||
|
||||
if status, err := c.execute(cmd, nil); err == errClosed {
|
||||
// Server closed connection, that's what we want anyway
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else if status != nil {
|
||||
return status.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
380
vendor/github.com/emersion/go-imap/client/cmd_auth.go
generated
vendored
Normal file
380
vendor/github.com/emersion/go-imap/client/cmd_auth.go
generated
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
"github.com/emersion/go-imap/responses"
|
||||
)
|
||||
|
||||
// ErrNotLoggedIn is returned if a function that requires the client to be
|
||||
// logged in is called then the client isn't.
|
||||
var ErrNotLoggedIn = errors.New("Not logged in")
|
||||
|
||||
func (c *Client) ensureAuthenticated() error {
|
||||
state := c.State()
|
||||
if state != imap.AuthenticatedState && state != imap.SelectedState {
|
||||
return ErrNotLoggedIn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Select selects a mailbox so that messages in the mailbox can be accessed. Any
|
||||
// currently selected mailbox is deselected before attempting the new selection.
|
||||
// Even if the readOnly parameter is set to false, the server can decide to open
|
||||
// the mailbox in read-only mode.
|
||||
func (c *Client) Select(name string, readOnly bool) (*imap.MailboxStatus, error) {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &commands.Select{
|
||||
Mailbox: name,
|
||||
ReadOnly: readOnly,
|
||||
}
|
||||
|
||||
mbox := &imap.MailboxStatus{Name: name, Items: make(map[imap.StatusItem]interface{})}
|
||||
res := &responses.Select{
|
||||
Mailbox: mbox,
|
||||
}
|
||||
c.locker.Lock()
|
||||
c.mailbox = mbox
|
||||
c.locker.Unlock()
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
c.locker.Lock()
|
||||
c.mailbox = nil
|
||||
c.locker.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
if err := status.Err(); err != nil {
|
||||
c.locker.Lock()
|
||||
c.mailbox = nil
|
||||
c.locker.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
mbox.ReadOnly = (status.Code == imap.CodeReadOnly)
|
||||
c.state = imap.SelectedState
|
||||
c.locker.Unlock()
|
||||
return mbox, nil
|
||||
}
|
||||
|
||||
// Create creates a mailbox with the given name.
|
||||
func (c *Client) Create(name string) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Create{
|
||||
Mailbox: name,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Delete permanently removes the mailbox with the given name.
|
||||
func (c *Client) Delete(name string) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Delete{
|
||||
Mailbox: name,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Rename changes the name of a mailbox.
|
||||
func (c *Client) Rename(existingName, newName string) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Rename{
|
||||
Existing: existingName,
|
||||
New: newName,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Subscribe adds the specified mailbox name to the server's set of "active" or
|
||||
// "subscribed" mailboxes.
|
||||
func (c *Client) Subscribe(name string) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Subscribe{
|
||||
Mailbox: name,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Unsubscribe removes the specified mailbox name from the server's set of
|
||||
// "active" or "subscribed" mailboxes.
|
||||
func (c *Client) Unsubscribe(name string) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Unsubscribe{
|
||||
Mailbox: name,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// List returns a subset of names from the complete set of all names available
|
||||
// to the client.
|
||||
//
|
||||
// An empty name argument is a special request to return the hierarchy delimiter
|
||||
// and the root name of the name given in the reference. The character "*" is a
|
||||
// wildcard, and matches zero or more characters at this position. The
|
||||
// character "%" is similar to "*", but it does not match a hierarchy delimiter.
|
||||
func (c *Client) List(ref, name string, ch chan *imap.MailboxInfo) error {
|
||||
defer close(ch)
|
||||
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.List{
|
||||
Reference: ref,
|
||||
Mailbox: name,
|
||||
}
|
||||
res := &responses.List{Mailboxes: ch}
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Lsub returns a subset of names from the set of names that the user has
|
||||
// declared as being "active" or "subscribed".
|
||||
func (c *Client) Lsub(ref, name string, ch chan *imap.MailboxInfo) error {
|
||||
defer close(ch)
|
||||
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.List{
|
||||
Reference: ref,
|
||||
Mailbox: name,
|
||||
Subscribed: true,
|
||||
}
|
||||
res := &responses.List{
|
||||
Mailboxes: ch,
|
||||
Subscribed: true,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Status requests the status of the indicated mailbox. It does not change the
|
||||
// currently selected mailbox, nor does it affect the state of any messages in
|
||||
// the queried mailbox.
|
||||
//
|
||||
// See RFC 3501 section 6.3.10 for a list of items that can be requested.
|
||||
func (c *Client) Status(name string, items []imap.StatusItem) (*imap.MailboxStatus, error) {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &commands.Status{
|
||||
Mailbox: name,
|
||||
Items: items,
|
||||
}
|
||||
res := &responses.Status{
|
||||
Mailbox: new(imap.MailboxStatus),
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Mailbox, status.Err()
|
||||
}
|
||||
|
||||
// Append appends the literal argument as a new message to the end of the
|
||||
// specified destination mailbox. This argument SHOULD be in the format of an
|
||||
// RFC 2822 message. flags and date are optional arguments and can be set to
|
||||
// nil and the empty struct.
|
||||
func (c *Client) Append(mbox string, flags []string, date time.Time, msg imap.Literal) error {
|
||||
if err := c.ensureAuthenticated(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Append{
|
||||
Mailbox: mbox,
|
||||
Flags: flags,
|
||||
Date: date,
|
||||
Message: msg,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Enable requests the server to enable the named extensions. The extensions
|
||||
// which were successfully enabled are returned.
|
||||
//
|
||||
// See RFC 5161 section 3.1.
|
||||
func (c *Client) Enable(caps []string) ([]string, error) {
|
||||
if ok, err := c.Support("ENABLE"); !ok || err != nil {
|
||||
return nil, ErrExtensionUnsupported
|
||||
}
|
||||
|
||||
// ENABLE is invalid if a mailbox has been selected.
|
||||
if c.State() != imap.AuthenticatedState {
|
||||
return nil, ErrNotLoggedIn
|
||||
}
|
||||
|
||||
cmd := &commands.Enable{Caps: caps}
|
||||
res := &responses.Enabled{}
|
||||
|
||||
if status, err := c.Execute(cmd, res); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return res.Caps, status.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) idle(stop <-chan struct{}) error {
|
||||
cmd := &commands.Idle{}
|
||||
|
||||
res := &responses.Idle{
|
||||
Stop: stop,
|
||||
RepliesCh: make(chan []byte, 10),
|
||||
}
|
||||
|
||||
if status, err := c.Execute(cmd, res); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return status.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// IdleOptions holds options for Client.Idle.
|
||||
type IdleOptions struct {
|
||||
// LogoutTimeout is used to avoid being logged out by the server when
|
||||
// idling. Each LogoutTimeout, the IDLE command is restarted. If set to
|
||||
// zero, a default is used. If negative, this behavior is disabled.
|
||||
LogoutTimeout time.Duration
|
||||
// Poll interval when the server doesn't support IDLE. If zero, a default
|
||||
// is used. If negative, polling is always disabled.
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// Idle indicates to the server that the client is ready to receive unsolicited
|
||||
// mailbox update messages. When the client wants to send commands again, it
|
||||
// must first close stop.
|
||||
//
|
||||
// If the server doesn't support IDLE, go-imap falls back to polling.
|
||||
func (c *Client) Idle(stop <-chan struct{}, opts *IdleOptions) error {
|
||||
if ok, err := c.Support("IDLE"); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return c.idleFallback(stop, opts)
|
||||
}
|
||||
|
||||
logoutTimeout := 25 * time.Minute
|
||||
if opts != nil {
|
||||
if opts.LogoutTimeout > 0 {
|
||||
logoutTimeout = opts.LogoutTimeout
|
||||
} else if opts.LogoutTimeout < 0 {
|
||||
return c.idle(stop)
|
||||
}
|
||||
}
|
||||
|
||||
t := time.NewTicker(logoutTimeout)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
stopOrRestart := make(chan struct{})
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- c.idle(stopOrRestart)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
close(stopOrRestart)
|
||||
if err := <-done; err != nil {
|
||||
return err
|
||||
}
|
||||
case <-stop:
|
||||
close(stopOrRestart)
|
||||
return <-done
|
||||
case err := <-done:
|
||||
close(stopOrRestart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) idleFallback(stop <-chan struct{}, opts *IdleOptions) error {
|
||||
pollInterval := time.Minute
|
||||
if opts != nil {
|
||||
if opts.PollInterval > 0 {
|
||||
pollInterval = opts.PollInterval
|
||||
} else if opts.PollInterval < 0 {
|
||||
return ErrExtensionUnsupported
|
||||
}
|
||||
}
|
||||
|
||||
t := time.NewTicker(pollInterval)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
if err := c.Noop(); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-stop:
|
||||
return nil
|
||||
case <-c.LoggedOut():
|
||||
return errors.New("disconnected while idling")
|
||||
}
|
||||
}
|
||||
}
|
||||
174
vendor/github.com/emersion/go-imap/client/cmd_noauth.go
generated
vendored
Normal file
174
vendor/github.com/emersion/go-imap/client/cmd_noauth.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
"github.com/emersion/go-imap/responses"
|
||||
"github.com/emersion/go-sasl"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAlreadyLoggedIn is returned if Login or Authenticate is called when the
|
||||
// client is already logged in.
|
||||
ErrAlreadyLoggedIn = errors.New("Already logged in")
|
||||
// ErrTLSAlreadyEnabled is returned if StartTLS is called when TLS is already
|
||||
// enabled.
|
||||
ErrTLSAlreadyEnabled = errors.New("TLS is already enabled")
|
||||
// ErrLoginDisabled is returned if Login or Authenticate is called when the
|
||||
// server has disabled authentication. Most of the time, calling enabling TLS
|
||||
// solves the problem.
|
||||
ErrLoginDisabled = errors.New("Login is disabled in current state")
|
||||
)
|
||||
|
||||
// SupportStartTLS checks if the server supports STARTTLS.
|
||||
func (c *Client) SupportStartTLS() (bool, error) {
|
||||
return c.Support("STARTTLS")
|
||||
}
|
||||
|
||||
// StartTLS starts TLS negotiation.
|
||||
func (c *Client) StartTLS(tlsConfig *tls.Config) error {
|
||||
if c.isTLS {
|
||||
return ErrTLSAlreadyEnabled
|
||||
}
|
||||
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = new(tls.Config)
|
||||
}
|
||||
if tlsConfig.ServerName == "" {
|
||||
tlsConfig = tlsConfig.Clone()
|
||||
tlsConfig.ServerName = c.serverName
|
||||
}
|
||||
|
||||
cmd := new(commands.StartTLS)
|
||||
|
||||
err := c.Upgrade(func(conn net.Conn) (net.Conn, error) {
|
||||
// Flag connection as in upgrading
|
||||
c.upgrading = true
|
||||
if status, err := c.execute(cmd, nil); err != nil {
|
||||
return nil, err
|
||||
} else if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wait for reader to block.
|
||||
c.conn.WaitReady()
|
||||
tlsConn := tls.Client(conn, tlsConfig)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Capabilities change when TLS is enabled
|
||||
c.locker.Lock()
|
||||
c.caps = nil
|
||||
c.locker.Unlock()
|
||||
|
||||
return tlsConn, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.isTLS = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// SupportAuth checks if the server supports a given authentication mechanism.
|
||||
func (c *Client) SupportAuth(mech string) (bool, error) {
|
||||
return c.Support("AUTH=" + mech)
|
||||
}
|
||||
|
||||
// Authenticate indicates a SASL authentication mechanism to the server. If the
|
||||
// server supports the requested authentication mechanism, it performs an
|
||||
// authentication protocol exchange to authenticate and identify the client.
|
||||
func (c *Client) Authenticate(auth sasl.Client) error {
|
||||
if c.State() != imap.NotAuthenticatedState {
|
||||
return ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
mech, ir, err := auth.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := &commands.Authenticate{
|
||||
Mechanism: mech,
|
||||
}
|
||||
|
||||
irOk, err := c.Support("SASL-IR")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if irOk {
|
||||
cmd.InitialResponse = ir
|
||||
}
|
||||
|
||||
res := &responses.Authenticate{
|
||||
Mechanism: auth,
|
||||
InitialResponse: ir,
|
||||
RepliesCh: make(chan []byte, 10),
|
||||
}
|
||||
if irOk {
|
||||
res.InitialResponse = nil
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = status.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
c.state = imap.AuthenticatedState
|
||||
c.caps = nil // Capabilities change when user is logged in
|
||||
c.locker.Unlock()
|
||||
|
||||
if status.Code == "CAPABILITY" {
|
||||
c.gotStatusCaps(status.Arguments)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Login identifies the client to the server and carries the plaintext password
|
||||
// authenticating this user.
|
||||
func (c *Client) Login(username, password string) error {
|
||||
if state := c.State(); state == imap.AuthenticatedState || state == imap.SelectedState {
|
||||
return ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
loginDisabled := c.caps != nil && c.caps["LOGINDISABLED"]
|
||||
c.locker.Unlock()
|
||||
if loginDisabled {
|
||||
return ErrLoginDisabled
|
||||
}
|
||||
|
||||
cmd := &commands.Login{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = status.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
c.state = imap.AuthenticatedState
|
||||
c.caps = nil // Capabilities change when user is logged in
|
||||
c.locker.Unlock()
|
||||
|
||||
if status.Code == "CAPABILITY" {
|
||||
c.gotStatusCaps(status.Arguments)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
367
vendor/github.com/emersion/go-imap/client/cmd_selected.go
generated
vendored
Normal file
367
vendor/github.com/emersion/go-imap/client/cmd_selected.go
generated
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/commands"
|
||||
"github.com/emersion/go-imap/responses"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoMailboxSelected is returned if a command that requires a mailbox to be
|
||||
// selected is called when there isn't.
|
||||
ErrNoMailboxSelected = errors.New("No mailbox selected")
|
||||
|
||||
// ErrExtensionUnsupported is returned if a command uses a extension that
|
||||
// is not supported by the server.
|
||||
ErrExtensionUnsupported = errors.New("The required extension is not supported by the server")
|
||||
)
|
||||
|
||||
// Check requests a checkpoint of the currently selected mailbox. A checkpoint
|
||||
// refers to any implementation-dependent housekeeping associated with the
|
||||
// mailbox that is not normally executed as part of each command.
|
||||
func (c *Client) Check() error {
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
cmd := new(commands.Check)
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Close permanently removes all messages that have the \Deleted flag set from
|
||||
// the currently selected mailbox, and returns to the authenticated state from
|
||||
// the selected state.
|
||||
func (c *Client) Close() error {
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
cmd := new(commands.Close)
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if err := status.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.locker.Lock()
|
||||
c.state = imap.AuthenticatedState
|
||||
c.mailbox = nil
|
||||
c.locker.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Terminate closes the tcp connection
|
||||
func (c *Client) Terminate() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// Expunge permanently removes all messages that have the \Deleted flag set from
|
||||
// the currently selected mailbox. If ch is not nil, sends sequence IDs of each
|
||||
// deleted message to this channel.
|
||||
func (c *Client) Expunge(ch chan uint32) error {
|
||||
if ch != nil {
|
||||
defer close(ch)
|
||||
}
|
||||
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
cmd := new(commands.Expunge)
|
||||
|
||||
var h responses.Handler
|
||||
if ch != nil {
|
||||
h = &responses.Expunge{SeqNums: ch}
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
func (c *Client) executeSearch(uid bool, criteria *imap.SearchCriteria, charset string) (ids []uint32, status *imap.StatusResp, err error) {
|
||||
if c.State() != imap.SelectedState {
|
||||
err = ErrNoMailboxSelected
|
||||
return
|
||||
}
|
||||
|
||||
var cmd imap.Commander = &commands.Search{
|
||||
Charset: charset,
|
||||
Criteria: criteria,
|
||||
}
|
||||
if uid {
|
||||
cmd = &commands.Uid{Cmd: cmd}
|
||||
}
|
||||
|
||||
res := new(responses.Search)
|
||||
|
||||
status, err = c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err, ids = status.Err(), res.Ids
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) search(uid bool, criteria *imap.SearchCriteria) (ids []uint32, err error) {
|
||||
ids, status, err := c.executeSearch(uid, criteria, "UTF-8")
|
||||
if status != nil && status.Code == imap.CodeBadCharset {
|
||||
// Some servers don't support UTF-8
|
||||
ids, _, err = c.executeSearch(uid, criteria, "US-ASCII")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Search searches the mailbox for messages that match the given searching
|
||||
// criteria. Searching criteria consist of one or more search keys. The response
|
||||
// contains a list of message sequence IDs corresponding to those messages that
|
||||
// match the searching criteria. When multiple keys are specified, the result is
|
||||
// the intersection (AND function) of all the messages that match those keys.
|
||||
// Criteria must be UTF-8 encoded. See RFC 3501 section 6.4.4 for a list of
|
||||
// searching criteria. When no criteria has been set, all messages in the mailbox
|
||||
// will be searched using ALL criteria.
|
||||
func (c *Client) Search(criteria *imap.SearchCriteria) (seqNums []uint32, err error) {
|
||||
return c.search(false, criteria)
|
||||
}
|
||||
|
||||
// UidSearch is identical to Search, but UIDs are returned instead of message
|
||||
// sequence numbers.
|
||||
func (c *Client) UidSearch(criteria *imap.SearchCriteria) (uids []uint32, err error) {
|
||||
return c.search(true, criteria)
|
||||
}
|
||||
|
||||
func (c *Client) fetch(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
|
||||
defer close(ch)
|
||||
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
var cmd imap.Commander = &commands.Fetch{
|
||||
SeqSet: seqset,
|
||||
Items: items,
|
||||
}
|
||||
if uid {
|
||||
cmd = &commands.Uid{Cmd: cmd}
|
||||
}
|
||||
|
||||
res := &responses.Fetch{Messages: ch, SeqSet: seqset, Uid: uid}
|
||||
|
||||
status, err := c.execute(cmd, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Fetch retrieves data associated with a message in the mailbox. See RFC 3501
|
||||
// section 6.4.5 for a list of items that can be requested.
|
||||
func (c *Client) Fetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
|
||||
return c.fetch(false, seqset, items, ch)
|
||||
}
|
||||
|
||||
// UidFetch is identical to Fetch, but seqset is interpreted as containing
|
||||
// unique identifiers instead of message sequence numbers.
|
||||
func (c *Client) UidFetch(seqset *imap.SeqSet, items []imap.FetchItem, ch chan *imap.Message) error {
|
||||
return c.fetch(true, seqset, items, ch)
|
||||
}
|
||||
|
||||
func (c *Client) store(uid bool, seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
|
||||
if ch != nil {
|
||||
defer close(ch)
|
||||
}
|
||||
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
// TODO: this could break extensions (this only works when item is FLAGS)
|
||||
if fields, ok := value.([]interface{}); ok {
|
||||
for i, field := range fields {
|
||||
if s, ok := field.(string); ok {
|
||||
fields[i] = imap.RawString(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If ch is nil, the updated values are data which will be lost, so don't
|
||||
// retrieve it.
|
||||
if ch == nil {
|
||||
op, _, err := imap.ParseFlagsOp(item)
|
||||
if err == nil {
|
||||
item = imap.FormatFlagsOp(op, true)
|
||||
}
|
||||
}
|
||||
|
||||
var cmd imap.Commander = &commands.Store{
|
||||
SeqSet: seqset,
|
||||
Item: item,
|
||||
Value: value,
|
||||
}
|
||||
if uid {
|
||||
cmd = &commands.Uid{Cmd: cmd}
|
||||
}
|
||||
|
||||
var h responses.Handler
|
||||
if ch != nil {
|
||||
h = &responses.Fetch{Messages: ch, SeqSet: seqset, Uid: uid}
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Store alters data associated with a message in the mailbox. If ch is not nil,
|
||||
// the updated value of the data will be sent to this channel. See RFC 3501
|
||||
// section 6.4.6 for a list of items that can be updated.
|
||||
func (c *Client) Store(seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
|
||||
return c.store(false, seqset, item, value, ch)
|
||||
}
|
||||
|
||||
// UidStore is identical to Store, but seqset is interpreted as containing
|
||||
// unique identifiers instead of message sequence numbers.
|
||||
func (c *Client) UidStore(seqset *imap.SeqSet, item imap.StoreItem, value interface{}, ch chan *imap.Message) error {
|
||||
return c.store(true, seqset, item, value, ch)
|
||||
}
|
||||
|
||||
func (c *Client) copy(uid bool, seqset *imap.SeqSet, dest string) error {
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
var cmd imap.Commander = &commands.Copy{
|
||||
SeqSet: seqset,
|
||||
Mailbox: dest,
|
||||
}
|
||||
if uid {
|
||||
cmd = &commands.Uid{Cmd: cmd}
|
||||
}
|
||||
|
||||
status, err := c.execute(cmd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return status.Err()
|
||||
}
|
||||
|
||||
// Copy copies the specified message(s) to the end of the specified destination
|
||||
// mailbox.
|
||||
func (c *Client) Copy(seqset *imap.SeqSet, dest string) error {
|
||||
return c.copy(false, seqset, dest)
|
||||
}
|
||||
|
||||
// UidCopy is identical to Copy, but seqset is interpreted as containing unique
|
||||
// identifiers instead of message sequence numbers.
|
||||
func (c *Client) UidCopy(seqset *imap.SeqSet, dest string) error {
|
||||
return c.copy(true, seqset, dest)
|
||||
}
|
||||
|
||||
func (c *Client) move(uid bool, seqset *imap.SeqSet, dest string) error {
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
if ok, err := c.Support("MOVE"); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return c.moveFallback(uid, seqset, dest)
|
||||
}
|
||||
|
||||
var cmd imap.Commander = &commands.Move{
|
||||
SeqSet: seqset,
|
||||
Mailbox: dest,
|
||||
}
|
||||
if uid {
|
||||
cmd = &commands.Uid{Cmd: cmd}
|
||||
}
|
||||
|
||||
if status, err := c.Execute(cmd, nil); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return status.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// moveFallback uses COPY, STORE and EXPUNGE for servers which don't support
|
||||
// MOVE.
|
||||
func (c *Client) moveFallback(uid bool, seqset *imap.SeqSet, dest string) error {
|
||||
item := imap.FormatFlagsOp(imap.AddFlags, true)
|
||||
flags := []interface{}{imap.DeletedFlag}
|
||||
if uid {
|
||||
if err := c.UidCopy(seqset, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.UidStore(seqset, item, flags, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := c.Copy(seqset, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Store(seqset, item, flags, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Expunge(nil)
|
||||
}
|
||||
|
||||
// Move moves the specified message(s) to the end of the specified destination
|
||||
// mailbox.
|
||||
//
|
||||
// If the server doesn't support the MOVE extension defined in RFC 6851,
|
||||
// go-imap will fallback to copy, store and expunge.
|
||||
func (c *Client) Move(seqset *imap.SeqSet, dest string) error {
|
||||
return c.move(false, seqset, dest)
|
||||
}
|
||||
|
||||
// UidMove is identical to Move, but seqset is interpreted as containing unique
|
||||
// identifiers instead of message sequence numbers.
|
||||
func (c *Client) UidMove(seqset *imap.SeqSet, dest string) error {
|
||||
return c.move(true, seqset, dest)
|
||||
}
|
||||
|
||||
// Unselect frees server's resources associated with the selected mailbox and
|
||||
// returns the server to the authenticated state. This command performs the same
|
||||
// actions as Close, except that no messages are permanently removed from the
|
||||
// currently selected mailbox.
|
||||
//
|
||||
// If client does not support the UNSELECT extension, ErrExtensionUnsupported
|
||||
// is returned.
|
||||
func (c *Client) Unselect() error {
|
||||
if ok, err := c.Support("UNSELECT"); !ok || err != nil {
|
||||
return ErrExtensionUnsupported
|
||||
}
|
||||
|
||||
if c.State() != imap.SelectedState {
|
||||
return ErrNoMailboxSelected
|
||||
}
|
||||
|
||||
cmd := &commands.Unselect{}
|
||||
if status, err := c.Execute(cmd, nil); err != nil {
|
||||
return err
|
||||
} else if err := status.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SetState(imap.AuthenticatedState, nil)
|
||||
return nil
|
||||
}
|
||||
24
vendor/github.com/emersion/go-imap/client/tag.go
generated
vendored
Normal file
24
vendor/github.com/emersion/go-imap/client/tag.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func randomString(n int) (string, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func generateTag() string {
|
||||
tag, err := randomString(4)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tag
|
||||
}
|
||||
57
vendor/github.com/emersion/go-imap/command.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-imap/command.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A value that can be converted to a command.
|
||||
type Commander interface {
|
||||
Command() *Command
|
||||
}
|
||||
|
||||
// A command.
|
||||
type Command struct {
|
||||
// The command tag. It acts as a unique identifier for this command. If empty,
|
||||
// the command is untagged.
|
||||
Tag string
|
||||
// The command name.
|
||||
Name string
|
||||
// The command arguments.
|
||||
Arguments []interface{}
|
||||
}
|
||||
|
||||
// Implements the Commander interface.
|
||||
func (cmd *Command) Command() *Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *Command) WriteTo(w *Writer) error {
|
||||
tag := cmd.Tag
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
}
|
||||
|
||||
fields := []interface{}{RawString(tag), RawString(cmd.Name)}
|
||||
fields = append(fields, cmd.Arguments...)
|
||||
return w.writeLine(fields...)
|
||||
}
|
||||
|
||||
// Parse a command from fields.
|
||||
func (cmd *Command) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("imap: cannot parse command: no enough fields")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if cmd.Tag, ok = fields[0].(string); !ok {
|
||||
return errors.New("imap: cannot parse command: invalid tag")
|
||||
}
|
||||
if cmd.Name, ok = fields[1].(string); !ok {
|
||||
return errors.New("imap: cannot parse command: invalid name")
|
||||
}
|
||||
cmd.Name = strings.ToUpper(cmd.Name) // Command names are case-insensitive
|
||||
|
||||
cmd.Arguments = fields[2:]
|
||||
return nil
|
||||
}
|
||||
93
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
Normal file
93
vendor/github.com/emersion/go-imap/commands/append.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Append is an APPEND command, as defined in RFC 3501 section 6.3.11.
|
||||
type Append struct {
|
||||
Mailbox string
|
||||
Flags []string
|
||||
Date time.Time
|
||||
Message imap.Literal
|
||||
}
|
||||
|
||||
func (cmd *Append) Command() *imap.Command {
|
||||
var args []interface{}
|
||||
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
args = append(args, imap.FormatMailboxName(mailbox))
|
||||
|
||||
if cmd.Flags != nil {
|
||||
flags := make([]interface{}, len(cmd.Flags))
|
||||
for i, flag := range cmd.Flags {
|
||||
flags[i] = imap.RawString(flag)
|
||||
}
|
||||
args = append(args, flags)
|
||||
}
|
||||
|
||||
if !cmd.Date.IsZero() {
|
||||
args = append(args, cmd.Date)
|
||||
}
|
||||
|
||||
args = append(args, cmd.Message)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "APPEND",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Append) Parse(fields []interface{}) (err error) {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
// Parse mailbox name
|
||||
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)
|
||||
}
|
||||
|
||||
// Parse message literal
|
||||
litIndex := len(fields) - 1
|
||||
var ok bool
|
||||
if cmd.Message, ok = fields[litIndex].(imap.Literal); !ok {
|
||||
return errors.New("Message must be a literal")
|
||||
}
|
||||
|
||||
// Remaining fields a optional
|
||||
fields = fields[1:litIndex]
|
||||
if len(fields) > 0 {
|
||||
// Parse flags list
|
||||
if flags, ok := fields[0].([]interface{}); ok {
|
||||
if cmd.Flags, err = imap.ParseStringList(flags); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, flag := range cmd.Flags {
|
||||
cmd.Flags[i] = imap.CanonicalFlag(flag)
|
||||
}
|
||||
|
||||
fields = fields[1:]
|
||||
}
|
||||
|
||||
// Parse date
|
||||
if len(fields) > 0 {
|
||||
if date, ok := fields[0].(string); !ok {
|
||||
return errors.New("Date must be a string")
|
||||
} else if cmd.Date, err = time.Parse(imap.DateTimeLayout, date); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
124
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
Normal file
124
vendor/github.com/emersion/go-imap/commands/authenticate.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-sasl"
|
||||
)
|
||||
|
||||
// AuthenticateConn is a connection that supports IMAP authentication.
|
||||
type AuthenticateConn interface {
|
||||
io.Reader
|
||||
|
||||
// WriteResp writes an IMAP response to this connection.
|
||||
WriteResp(res imap.WriterTo) error
|
||||
}
|
||||
|
||||
// Authenticate is an AUTHENTICATE command, as defined in RFC 3501 section
|
||||
// 6.2.2.
|
||||
type Authenticate struct {
|
||||
Mechanism string
|
||||
InitialResponse []byte
|
||||
}
|
||||
|
||||
func (cmd *Authenticate) Command() *imap.Command {
|
||||
args := []interface{}{imap.RawString(cmd.Mechanism)}
|
||||
if cmd.InitialResponse != nil {
|
||||
var encodedResponse string
|
||||
if len(cmd.InitialResponse) == 0 {
|
||||
// Empty initial response should be encoded as "=", not empty
|
||||
// string.
|
||||
encodedResponse = "="
|
||||
} else {
|
||||
encodedResponse = base64.StdEncoding.EncodeToString(cmd.InitialResponse)
|
||||
}
|
||||
|
||||
args = append(args, imap.RawString(encodedResponse))
|
||||
}
|
||||
return &imap.Command{
|
||||
Name: "AUTHENTICATE",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Authenticate) Parse(fields []interface{}) error {
|
||||
if len(fields) < 1 {
|
||||
return errors.New("Not enough arguments")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if cmd.Mechanism, ok = fields[0].(string); !ok {
|
||||
return errors.New("Mechanism must be a string")
|
||||
}
|
||||
cmd.Mechanism = strings.ToUpper(cmd.Mechanism)
|
||||
|
||||
if len(fields) != 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
encodedResponse, ok := fields[1].(string)
|
||||
if !ok {
|
||||
return errors.New("Initial response must be a string")
|
||||
}
|
||||
if encodedResponse == "=" {
|
||||
cmd.InitialResponse = []byte{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
cmd.InitialResponse, err = base64.StdEncoding.DecodeString(encodedResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *Authenticate) Handle(mechanisms map[string]sasl.Server, conn AuthenticateConn) error {
|
||||
sasl, ok := mechanisms[cmd.Mechanism]
|
||||
if !ok {
|
||||
return errors.New("Unsupported mechanism")
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(conn)
|
||||
|
||||
response := cmd.InitialResponse
|
||||
for {
|
||||
challenge, done, err := sasl.Next(response)
|
||||
if err != nil || done {
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(challenge)
|
||||
cont := &imap.ContinuationReq{Info: encoded}
|
||||
if err := conn.WriteResp(cont); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("unexpected EOF")
|
||||
}
|
||||
|
||||
encoded = scanner.Text()
|
||||
if encoded != "" {
|
||||
if encoded == "*" {
|
||||
return &imap.ErrStatusResp{Resp: &imap.StatusResp{
|
||||
Type: imap.StatusRespBad,
|
||||
Info: "negotiation cancelled",
|
||||
}}
|
||||
}
|
||||
response, err = base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/capability.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Capability is a CAPABILITY command, as defined in RFC 3501 section 6.1.1.
|
||||
type Capability struct{}
|
||||
|
||||
func (c *Capability) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "CAPABILITY",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Capability) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/check.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Check is a CHECK command, as defined in RFC 3501 section 6.4.1.
|
||||
type Check struct{}
|
||||
|
||||
func (cmd *Check) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "CHECK",
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Check) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/close.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Close is a CLOSE command, as defined in RFC 3501 section 6.4.2.
|
||||
type Close struct{}
|
||||
|
||||
func (cmd *Close) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "CLOSE",
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Close) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
2
vendor/github.com/emersion/go-imap/commands/commands.go
generated
vendored
Normal file
2
vendor/github.com/emersion/go-imap/commands/commands.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package commands implements IMAP commands defined in RFC 3501.
|
||||
package commands
|
||||
47
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
Normal file
47
vendor/github.com/emersion/go-imap/commands/copy.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Copy is a COPY command, as defined in RFC 3501 section 6.4.7.
|
||||
type Copy struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Copy) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "COPY",
|
||||
Arguments: []interface{}{cmd.SeqSet, imap.FormatMailboxName(mailbox)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Copy) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
if seqSet, ok := fields[0].(string); !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
} else if seqSet, err := imap.ParseSeqSet(seqSet); err != nil {
|
||||
return err
|
||||
} else {
|
||||
cmd.SeqSet = seqSet
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
38
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
Normal file
38
vendor/github.com/emersion/go-imap/commands/create.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Create is a CREATE command, as defined in RFC 3501 section 6.3.3.
|
||||
type Create struct {
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Create) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "CREATE",
|
||||
Arguments: []interface{}{mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Create) Parse(fields []interface{}) error {
|
||||
if len(fields) < 1 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
38
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
Normal file
38
vendor/github.com/emersion/go-imap/commands/delete.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Delete is a DELETE command, as defined in RFC 3501 section 6.3.3.
|
||||
type Delete struct {
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Delete) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "DELETE",
|
||||
Arguments: []interface{}{imap.FormatMailboxName(mailbox)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Delete) Parse(fields []interface{}) error {
|
||||
if len(fields) < 1 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
vendor/github.com/emersion/go-imap/commands/enable.go
generated
vendored
Normal file
23
vendor/github.com/emersion/go-imap/commands/enable.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// An ENABLE command, defined in RFC 5161 section 3.1.
|
||||
type Enable struct {
|
||||
Caps []string
|
||||
}
|
||||
|
||||
func (cmd *Enable) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "ENABLE",
|
||||
Arguments: imap.FormatStringList(cmd.Caps),
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Enable) Parse(fields []interface{}) error {
|
||||
var err error
|
||||
cmd.Caps, err = imap.ParseStringList(fields)
|
||||
return err
|
||||
}
|
||||
16
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
Normal file
16
vendor/github.com/emersion/go-imap/commands/expunge.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Expunge is an EXPUNGE command, as defined in RFC 3501 section 6.4.3.
|
||||
type Expunge struct{}
|
||||
|
||||
func (cmd *Expunge) Command() *imap.Command {
|
||||
return &imap.Command{Name: "EXPUNGE"}
|
||||
}
|
||||
|
||||
func (cmd *Expunge) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
55
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
Normal file
55
vendor/github.com/emersion/go-imap/commands/fetch.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Fetch is a FETCH command, as defined in RFC 3501 section 6.4.5.
|
||||
type Fetch struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Items []imap.FetchItem
|
||||
}
|
||||
|
||||
func (cmd *Fetch) Command() *imap.Command {
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, item := range cmd.Items {
|
||||
items[i] = imap.RawString(item)
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: "FETCH",
|
||||
Arguments: []interface{}{cmd.SeqSet, items},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Fetch) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
var err error
|
||||
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
|
||||
cmd.Items = imap.FetchItem(strings.ToUpper(items)).Expand()
|
||||
case []interface{}: // A list of items
|
||||
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")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
17
vendor/github.com/emersion/go-imap/commands/idle.go
generated
vendored
Normal file
17
vendor/github.com/emersion/go-imap/commands/idle.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// An IDLE command.
|
||||
// Se RFC 2177 section 3.
|
||||
type Idle struct{}
|
||||
|
||||
func (cmd *Idle) Command() *imap.Command {
|
||||
return &imap.Command{Name: "IDLE"}
|
||||
}
|
||||
|
||||
func (cmd *Idle) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
60
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
Normal file
60
vendor/github.com/emersion/go-imap/commands/list.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// List is a LIST command, as defined in RFC 3501 section 6.3.8. If Subscribed
|
||||
// is set to true, LSUB will be used instead.
|
||||
type List struct {
|
||||
Reference string
|
||||
Mailbox string
|
||||
|
||||
Subscribed bool
|
||||
}
|
||||
|
||||
func (cmd *List) Command() *imap.Command {
|
||||
name := "LIST"
|
||||
if cmd.Subscribed {
|
||||
name = "LSUB"
|
||||
}
|
||||
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
ref, _ := enc.String(cmd.Reference)
|
||||
mailbox, _ := enc.String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
Arguments: []interface{}{ref, mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *List) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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, 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
36
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
Normal file
36
vendor/github.com/emersion/go-imap/commands/login.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Login is a LOGIN command, as defined in RFC 3501 section 6.2.2.
|
||||
type Login struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (cmd *Login) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "LOGIN",
|
||||
Arguments: []interface{}{cmd.Username, cmd.Password},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Login) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("Not enough arguments")
|
||||
}
|
||||
|
||||
var err error
|
||||
if cmd.Username, err = imap.ParseString(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Password, err = imap.ParseString(fields[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/logout.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Logout is a LOGOUT command, as defined in RFC 3501 section 6.1.3.
|
||||
type Logout struct{}
|
||||
|
||||
func (c *Logout) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "LOGOUT",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Logout) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
48
vendor/github.com/emersion/go-imap/commands/move.go
generated
vendored
Normal file
48
vendor/github.com/emersion/go-imap/commands/move.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// A MOVE command.
|
||||
// See RFC 6851 section 3.1.
|
||||
type Move struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Move) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "MOVE",
|
||||
Arguments: []interface{}{cmd.SeqSet, mailbox},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Move) Parse(fields []interface{}) (err error) {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
seqset, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
}
|
||||
if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mailbox, ok := fields[1].(string)
|
||||
if !ok {
|
||||
return errors.New("Mailbox name must be a string")
|
||||
}
|
||||
if cmd.Mailbox, err = utf7.Encoding.NewDecoder().String(mailbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/noop.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Noop is a NOOP command, as defined in RFC 3501 section 6.1.2.
|
||||
type Noop struct{}
|
||||
|
||||
func (c *Noop) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "NOOP",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Noop) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
51
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
Normal file
51
vendor/github.com/emersion/go-imap/commands/rename.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Rename is a RENAME command, as defined in RFC 3501 section 6.3.5.
|
||||
type Rename struct {
|
||||
Existing string
|
||||
New string
|
||||
}
|
||||
|
||||
func (cmd *Rename) Command() *imap.Command {
|
||||
enc := utf7.Encoding.NewEncoder()
|
||||
existingName, _ := enc.String(cmd.Existing)
|
||||
newName, _ := enc.String(cmd.New)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "RENAME",
|
||||
Arguments: []interface{}{imap.FormatMailboxName(existingName), imap.FormatMailboxName(newName)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Rename) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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, 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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
57
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-imap/commands/search.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Search is a SEARCH command, as defined in RFC 3501 section 6.4.4.
|
||||
type Search struct {
|
||||
Charset string
|
||||
Criteria *imap.SearchCriteria
|
||||
}
|
||||
|
||||
func (cmd *Search) Command() *imap.Command {
|
||||
var args []interface{}
|
||||
if cmd.Charset != "" {
|
||||
args = append(args, imap.RawString("CHARSET"), imap.RawString(cmd.Charset))
|
||||
}
|
||||
args = append(args, cmd.Criteria.Format()...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "SEARCH",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Search) Parse(fields []interface{}) error {
|
||||
if len(fields) == 0 {
|
||||
return errors.New("Missing search criteria")
|
||||
}
|
||||
|
||||
// Parse charset
|
||||
if f, ok := fields[0].(string); ok && strings.EqualFold(f, "CHARSET") {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("Missing CHARSET value")
|
||||
}
|
||||
if cmd.Charset, ok = fields[1].(string); !ok {
|
||||
return errors.New("Charset must be a string")
|
||||
}
|
||||
fields = fields[2:]
|
||||
}
|
||||
|
||||
var charsetReader func(io.Reader) io.Reader
|
||||
charset := strings.ToLower(cmd.Charset)
|
||||
if charset != "utf-8" && charset != "us-ascii" && charset != "" {
|
||||
charsetReader = func(r io.Reader) io.Reader {
|
||||
r, _ = imap.CharsetReader(charset, r)
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Criteria = new(imap.SearchCriteria)
|
||||
return cmd.Criteria.ParseWithCharset(fields, charsetReader)
|
||||
}
|
||||
45
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
Normal file
45
vendor/github.com/emersion/go-imap/commands/select.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Select is a SELECT command, as defined in RFC 3501 section 6.3.1. If ReadOnly
|
||||
// is set to true, the EXAMINE command will be used instead.
|
||||
type Select struct {
|
||||
Mailbox string
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
func (cmd *Select) Command() *imap.Command {
|
||||
name := "SELECT"
|
||||
if cmd.ReadOnly {
|
||||
name = "EXAMINE"
|
||||
}
|
||||
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: name,
|
||||
Arguments: []interface{}{imap.FormatMailboxName(mailbox)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Select) Parse(fields []interface{}) error {
|
||||
if len(fields) < 1 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
Normal file
18
vendor/github.com/emersion/go-imap/commands/starttls.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// StartTLS is a STARTTLS command, as defined in RFC 3501 section 6.2.1.
|
||||
type StartTLS struct{}
|
||||
|
||||
func (cmd *StartTLS) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "STARTTLS",
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *StartTLS) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
58
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
Normal file
58
vendor/github.com/emersion/go-imap/commands/status.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Status is a STATUS command, as defined in RFC 3501 section 6.3.10.
|
||||
type Status struct {
|
||||
Mailbox string
|
||||
Items []imap.StatusItem
|
||||
}
|
||||
|
||||
func (cmd *Status) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
items := make([]interface{}, len(cmd.Items))
|
||||
for i, item := range cmd.Items {
|
||||
items[i] = imap.RawString(item)
|
||||
}
|
||||
|
||||
return &imap.Command{
|
||||
Name: "STATUS",
|
||||
Arguments: []interface{}{imap.FormatMailboxName(mailbox), items},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Status) Parse(fields []interface{}) error {
|
||||
if len(fields) < 2 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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[i] = imap.StatusItem(strings.ToUpper(s))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
50
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
Normal file
50
vendor/github.com/emersion/go-imap/commands/store.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Store is a STORE command, as defined in RFC 3501 section 6.4.6.
|
||||
type Store struct {
|
||||
SeqSet *imap.SeqSet
|
||||
Item imap.StoreItem
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (cmd *Store) Command() *imap.Command {
|
||||
return &imap.Command{
|
||||
Name: "STORE",
|
||||
Arguments: []interface{}{cmd.SeqSet, imap.RawString(cmd.Item), cmd.Value},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Store) Parse(fields []interface{}) error {
|
||||
if len(fields) < 3 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
seqset, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Invalid sequence set")
|
||||
}
|
||||
var err error
|
||||
if cmd.SeqSet, err = imap.ParseSeqSet(seqset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if item, ok := fields[1].(string); !ok {
|
||||
return errors.New("Item name must be a string")
|
||||
} else {
|
||||
cmd.Item = imap.StoreItem(strings.ToUpper(item))
|
||||
}
|
||||
|
||||
if len(fields[2:]) == 1 {
|
||||
cmd.Value = fields[2]
|
||||
} else {
|
||||
cmd.Value = fields[2:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
63
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
Normal file
63
vendor/github.com/emersion/go-imap/commands/subscribe.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// Subscribe is a SUBSCRIBE command, as defined in RFC 3501 section 6.3.6.
|
||||
type Subscribe struct {
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "SUBSCRIBE",
|
||||
Arguments: []interface{}{imap.FormatMailboxName(mailbox)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Subscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enough arguments")
|
||||
}
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
// An UNSUBSCRIBE command.
|
||||
// See RFC 3501 section 6.3.7
|
||||
type Unsubscribe struct {
|
||||
Mailbox string
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Command() *imap.Command {
|
||||
mailbox, _ := utf7.Encoding.NewEncoder().String(cmd.Mailbox)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "UNSUBSCRIBE",
|
||||
Arguments: []interface{}{imap.FormatMailboxName(mailbox)},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Unsubscribe) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No enogh arguments")
|
||||
}
|
||||
|
||||
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 nil
|
||||
}
|
||||
44
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
Normal file
44
vendor/github.com/emersion/go-imap/commands/uid.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// Uid is a UID command, as defined in RFC 3501 section 6.4.8. It wraps another
|
||||
// command (e.g. wrapping a Fetch command will result in a UID FETCH).
|
||||
type Uid struct {
|
||||
Cmd imap.Commander
|
||||
}
|
||||
|
||||
func (cmd *Uid) Command() *imap.Command {
|
||||
inner := cmd.Cmd.Command()
|
||||
|
||||
args := []interface{}{imap.RawString(inner.Name)}
|
||||
args = append(args, inner.Arguments...)
|
||||
|
||||
return &imap.Command{
|
||||
Name: "UID",
|
||||
Arguments: args,
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *Uid) Parse(fields []interface{}) error {
|
||||
if len(fields) < 0 {
|
||||
return errors.New("No command name specified")
|
||||
}
|
||||
|
||||
name, ok := fields[0].(string)
|
||||
if !ok {
|
||||
return errors.New("Command name must be a string")
|
||||
}
|
||||
|
||||
cmd.Cmd = &imap.Command{
|
||||
Name: strings.ToUpper(name), // Command names are case-insensitive
|
||||
Arguments: fields[1:],
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
17
vendor/github.com/emersion/go-imap/commands/unselect.go
generated
vendored
Normal file
17
vendor/github.com/emersion/go-imap/commands/unselect.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// An UNSELECT command.
|
||||
// See RFC 3691 section 2.
|
||||
type Unselect struct{}
|
||||
|
||||
func (cmd *Unselect) Command() *imap.Command {
|
||||
return &imap.Command{Name: "UNSELECT"}
|
||||
}
|
||||
|
||||
func (cmd *Unselect) Parse(fields []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
284
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
Normal file
284
vendor/github.com/emersion/go-imap/conn.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A connection state.
|
||||
// See RFC 3501 section 3.
|
||||
type ConnState int
|
||||
|
||||
const (
|
||||
// In the connecting state, the server has not yet sent a greeting and no
|
||||
// command can be issued.
|
||||
ConnectingState = 0
|
||||
|
||||
// In the not authenticated state, the client MUST supply
|
||||
// authentication credentials before most commands will be
|
||||
// permitted. This state is entered when a connection starts
|
||||
// unless the connection has been pre-authenticated.
|
||||
NotAuthenticatedState ConnState = 1 << 0
|
||||
|
||||
// In the authenticated state, the client is authenticated and MUST
|
||||
// select a mailbox to access before commands that affect messages
|
||||
// will be permitted. This state is entered when a
|
||||
// pre-authenticated connection starts, when acceptable
|
||||
// authentication credentials have been provided, after an error in
|
||||
// selecting a mailbox, or after a successful CLOSE command.
|
||||
AuthenticatedState = 1 << 1
|
||||
|
||||
// In a selected state, a mailbox has been selected to access.
|
||||
// This state is entered when a mailbox has been successfully
|
||||
// selected.
|
||||
SelectedState = AuthenticatedState + 1<<2
|
||||
|
||||
// In the logout state, the connection is being terminated. This
|
||||
// state can be entered as a result of a client request (via the
|
||||
// LOGOUT command) or by unilateral action on the part of either
|
||||
// the client or server.
|
||||
LogoutState = 1 << 3
|
||||
|
||||
// ConnectedState is either NotAuthenticatedState, AuthenticatedState or
|
||||
// SelectedState.
|
||||
ConnectedState = NotAuthenticatedState | AuthenticatedState | SelectedState
|
||||
)
|
||||
|
||||
// A function that upgrades a connection.
|
||||
//
|
||||
// This should only be used by libraries implementing an IMAP extension (e.g.
|
||||
// COMPRESS).
|
||||
type ConnUpgrader func(conn net.Conn) (net.Conn, error)
|
||||
|
||||
type Waiter struct {
|
||||
start sync.WaitGroup
|
||||
end sync.WaitGroup
|
||||
finished bool
|
||||
}
|
||||
|
||||
func NewWaiter() *Waiter {
|
||||
w := &Waiter{finished: false}
|
||||
w.start.Add(1)
|
||||
w.end.Add(1)
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *Waiter) Wait() {
|
||||
if !w.finished {
|
||||
// Signal that we are ready for upgrade to continue.
|
||||
w.start.Done()
|
||||
// Wait for upgrade to finish.
|
||||
w.end.Wait()
|
||||
w.finished = true
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Waiter) WaitReady() {
|
||||
if !w.finished {
|
||||
// Wait for reader/writer goroutine to be ready for upgrade.
|
||||
w.start.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Waiter) Close() {
|
||||
if !w.finished {
|
||||
// Upgrade is finished, close chanel to release reader/writer
|
||||
w.end.Done()
|
||||
}
|
||||
}
|
||||
|
||||
type LockedWriter struct {
|
||||
lock sync.Mutex
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// NewLockedWriter - goroutine safe writer.
|
||||
func NewLockedWriter(w io.Writer) io.Writer {
|
||||
return &LockedWriter{writer: w}
|
||||
}
|
||||
|
||||
func (w *LockedWriter) Write(b []byte) (int, error) {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
return w.writer.Write(b)
|
||||
}
|
||||
|
||||
type debugWriter struct {
|
||||
io.Writer
|
||||
|
||||
local io.Writer
|
||||
remote io.Writer
|
||||
}
|
||||
|
||||
// NewDebugWriter creates a new io.Writer that will write local network activity
|
||||
// to local and remote network activity to remote.
|
||||
func NewDebugWriter(local, remote io.Writer) io.Writer {
|
||||
return &debugWriter{Writer: local, local: local, remote: remote}
|
||||
}
|
||||
|
||||
type multiFlusher struct {
|
||||
flushers []flusher
|
||||
}
|
||||
|
||||
func (mf *multiFlusher) Flush() error {
|
||||
for _, f := range mf.flushers {
|
||||
if err := f.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMultiFlusher(flushers ...flusher) flusher {
|
||||
return &multiFlusher{flushers}
|
||||
}
|
||||
|
||||
// Underlying connection state information.
|
||||
type ConnInfo struct {
|
||||
RemoteAddr net.Addr
|
||||
LocalAddr net.Addr
|
||||
|
||||
// nil if connection is not using TLS.
|
||||
TLS *tls.ConnectionState
|
||||
}
|
||||
|
||||
// An IMAP connection.
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
*Reader
|
||||
*Writer
|
||||
|
||||
br *bufio.Reader
|
||||
bw *bufio.Writer
|
||||
|
||||
waiter *Waiter
|
||||
|
||||
// Print all commands and responses to this io.Writer.
|
||||
debug io.Writer
|
||||
}
|
||||
|
||||
// NewConn creates a new IMAP connection.
|
||||
func NewConn(conn net.Conn, r *Reader, w *Writer) *Conn {
|
||||
c := &Conn{Conn: conn, Reader: r, Writer: w}
|
||||
|
||||
c.init()
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Conn) createWaiter() *Waiter {
|
||||
// create new waiter each time.
|
||||
w := NewWaiter()
|
||||
c.waiter = w
|
||||
return w
|
||||
}
|
||||
|
||||
func (c *Conn) init() {
|
||||
r := io.Reader(c.Conn)
|
||||
w := io.Writer(c.Conn)
|
||||
|
||||
if c.debug != nil {
|
||||
localDebug, remoteDebug := c.debug, c.debug
|
||||
if debug, ok := c.debug.(*debugWriter); ok {
|
||||
localDebug, remoteDebug = debug.local, debug.remote
|
||||
}
|
||||
// If local and remote are the same, then we need a LockedWriter.
|
||||
if localDebug == remoteDebug {
|
||||
localDebug = NewLockedWriter(localDebug)
|
||||
remoteDebug = localDebug
|
||||
}
|
||||
|
||||
if localDebug != nil {
|
||||
w = io.MultiWriter(c.Conn, localDebug)
|
||||
}
|
||||
if remoteDebug != nil {
|
||||
r = io.TeeReader(c.Conn, remoteDebug)
|
||||
}
|
||||
}
|
||||
|
||||
if c.br == nil {
|
||||
c.br = bufio.NewReader(r)
|
||||
c.Reader.reader = c.br
|
||||
} else {
|
||||
c.br.Reset(r)
|
||||
}
|
||||
|
||||
if c.bw == nil {
|
||||
c.bw = bufio.NewWriter(w)
|
||||
c.Writer.Writer = c.bw
|
||||
} else {
|
||||
c.bw.Reset(w)
|
||||
}
|
||||
|
||||
if f, ok := c.Conn.(flusher); ok {
|
||||
c.Writer.Writer = struct {
|
||||
io.Writer
|
||||
flusher
|
||||
}{
|
||||
c.bw,
|
||||
newMultiFlusher(c.bw, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) Info() *ConnInfo {
|
||||
info := &ConnInfo{
|
||||
RemoteAddr: c.RemoteAddr(),
|
||||
LocalAddr: c.LocalAddr(),
|
||||
}
|
||||
|
||||
tlsConn, ok := c.Conn.(*tls.Conn)
|
||||
if ok {
|
||||
state := tlsConn.ConnectionState()
|
||||
info.TLS = &state
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
return c.Writer.Write(b)
|
||||
}
|
||||
|
||||
// Flush writes any buffered data to the underlying connection.
|
||||
func (c *Conn) Flush() error {
|
||||
return c.Writer.Flush()
|
||||
}
|
||||
|
||||
// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
|
||||
// tunnel.
|
||||
func (c *Conn) Upgrade(upgrader ConnUpgrader) error {
|
||||
// Block reads and writes during the upgrading process
|
||||
w := c.createWaiter()
|
||||
defer w.Close()
|
||||
|
||||
upgraded, err := upgrader(c.Conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Conn = upgraded
|
||||
c.init()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Called by reader/writer goroutines to wait for Upgrade to finish
|
||||
func (c *Conn) Wait() {
|
||||
c.waiter.Wait()
|
||||
}
|
||||
|
||||
// Called by Upgrader to wait for reader/writer goroutines to be ready for
|
||||
// upgrade.
|
||||
func (c *Conn) WaitReady() {
|
||||
c.waiter.WaitReady()
|
||||
}
|
||||
|
||||
// SetDebug defines an io.Writer to which all network activity will be logged.
|
||||
// If nil is provided, network activity will not be logged.
|
||||
func (c *Conn) SetDebug(w io.Writer) {
|
||||
c.debug = w
|
||||
c.init()
|
||||
}
|
||||
71
vendor/github.com/emersion/go-imap/date.go
generated
vendored
Normal file
71
vendor/github.com/emersion/go-imap/date.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Date and time layouts.
|
||||
// Dovecot adds a leading zero to dates:
|
||||
// https://github.com/dovecot/core/blob/4fbd5c5e113078e72f29465ccc96d44955ceadc2/src/lib-imap/imap-date.c#L166
|
||||
// Cyrus adds a leading space to dates:
|
||||
// https://github.com/cyrusimap/cyrus-imapd/blob/1cb805a3bffbdf829df0964f3b802cdc917e76db/lib/times.c#L543
|
||||
// GMail doesn't support leading spaces in dates used in SEARCH commands.
|
||||
const (
|
||||
// Defined in RFC 3501 as date-text on page 83.
|
||||
DateLayout = "_2-Jan-2006"
|
||||
// Defined in RFC 3501 as date-time on page 83.
|
||||
DateTimeLayout = "_2-Jan-2006 15:04:05 -0700"
|
||||
// Defined in RFC 5322 section 3.3, mentioned as env-date in RFC 3501 page 84.
|
||||
envelopeDateTimeLayout = "Mon, 02 Jan 2006 15:04:05 -0700"
|
||||
// Use as an example in RFC 3501 page 54.
|
||||
searchDateLayout = "2-Jan-2006"
|
||||
)
|
||||
|
||||
// time.Time with a specific layout.
|
||||
type (
|
||||
Date time.Time
|
||||
DateTime time.Time
|
||||
envelopeDateTime time.Time
|
||||
searchDate time.Time
|
||||
)
|
||||
|
||||
// Permutations of the layouts defined in RFC 5322, section 3.3.
|
||||
var envelopeDateTimeLayouts = [...]string{
|
||||
envelopeDateTimeLayout, // popular, try it first
|
||||
"_2 Jan 2006 15:04:05 -0700",
|
||||
"_2 Jan 2006 15:04:05 MST",
|
||||
"_2 Jan 2006 15:04 -0700",
|
||||
"_2 Jan 2006 15:04 MST",
|
||||
"_2 Jan 06 15:04:05 -0700",
|
||||
"_2 Jan 06 15:04:05 MST",
|
||||
"_2 Jan 06 15:04 -0700",
|
||||
"_2 Jan 06 15:04 MST",
|
||||
"Mon, _2 Jan 2006 15:04:05 -0700",
|
||||
"Mon, _2 Jan 2006 15:04:05 MST",
|
||||
"Mon, _2 Jan 2006 15:04 -0700",
|
||||
"Mon, _2 Jan 2006 15:04 MST",
|
||||
"Mon, _2 Jan 06 15:04:05 -0700",
|
||||
"Mon, _2 Jan 06 15:04:05 MST",
|
||||
"Mon, _2 Jan 06 15:04 -0700",
|
||||
"Mon, _2 Jan 06 15:04 MST",
|
||||
}
|
||||
|
||||
// TODO: this is a blunt way to strip any trailing CFWS (comment). A sharper
|
||||
// one would strip multiple CFWS, and only if really valid according to
|
||||
// RFC5322.
|
||||
var commentRE = regexp.MustCompile(`[ \t]+\(.*\)$`)
|
||||
|
||||
// Try parsing the date based on the layouts defined in RFC 5322, section 3.3.
|
||||
// Inspired by https://github.com/golang/go/blob/master/src/net/mail/message.go
|
||||
func parseMessageDateTime(maybeDate string) (time.Time, error) {
|
||||
maybeDate = commentRE.ReplaceAllString(maybeDate, "")
|
||||
for _, layout := range envelopeDateTimeLayouts {
|
||||
parsed, err := time.Parse(layout, maybeDate)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("date %s could not be parsed", maybeDate)
|
||||
}
|
||||
108
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
Normal file
108
vendor/github.com/emersion/go-imap/imap.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Package imap implements IMAP4rev1 (RFC 3501).
|
||||
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 StatusItem = "RECENT"
|
||||
StatusUidNext StatusItem = "UIDNEXT"
|
||||
StatusUidValidity StatusItem = "UIDVALIDITY"
|
||||
StatusUnseen StatusItem = "UNSEEN"
|
||||
|
||||
StatusAppendLimit StatusItem = "APPENDLIMIT"
|
||||
)
|
||||
|
||||
// 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 FetchItem = "FAST"
|
||||
FetchFull FetchItem = "FULL"
|
||||
|
||||
// Items
|
||||
FetchBody FetchItem = "BODY"
|
||||
FetchBodyStructure FetchItem = "BODYSTRUCTURE"
|
||||
FetchEnvelope FetchItem = "ENVELOPE"
|
||||
FetchFlags FetchItem = "FLAGS"
|
||||
FetchInternalDate FetchItem = "INTERNALDATE"
|
||||
FetchRFC822 FetchItem = "RFC822"
|
||||
FetchRFC822Header FetchItem = "RFC822.HEADER"
|
||||
FetchRFC822Size FetchItem = "RFC822.SIZE"
|
||||
FetchRFC822Text FetchItem = "RFC822.TEXT"
|
||||
FetchUid FetchItem = "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
|
||||
|
||||
const (
|
||||
// SetFlags replaces existing flags by new ones.
|
||||
SetFlags FlagsOp = "FLAGS"
|
||||
// AddFlags adds new flags.
|
||||
AddFlags = "+FLAGS"
|
||||
// RemoveFlags removes existing flags.
|
||||
RemoveFlags = "-FLAGS"
|
||||
)
|
||||
|
||||
// silentOp can be appended to a FlagsOp to prevent the operation from
|
||||
// triggering unilateral message updates.
|
||||
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
|
||||
// lower-case. utf-8 and us-ascii charsets are handled by default. One of the
|
||||
// the CharsetReader's result values must be non-nil.
|
||||
var CharsetReader func(charset string, r io.Reader) (io.Reader, error)
|
||||
13
vendor/github.com/emersion/go-imap/literal.go
generated
vendored
Normal file
13
vendor/github.com/emersion/go-imap/literal.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// A literal, as defined in RFC 3501 section 4.3.
|
||||
type Literal interface {
|
||||
io.Reader
|
||||
|
||||
// Len returns the number of bytes of the literal.
|
||||
Len() int
|
||||
}
|
||||
8
vendor/github.com/emersion/go-imap/logger.go
generated
vendored
Normal file
8
vendor/github.com/emersion/go-imap/logger.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package imap
|
||||
|
||||
// Logger is the behaviour used by server/client to
|
||||
// report errors for accepting connections and unexpected behavior from handlers.
|
||||
type Logger interface {
|
||||
Printf(format string, v ...interface{})
|
||||
Println(v ...interface{})
|
||||
}
|
||||
314
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
Normal file
314
vendor/github.com/emersion/go-imap/mailbox.go
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emersion/go-imap/utf7"
|
||||
)
|
||||
|
||||
// The primary mailbox, as defined in RFC 3501 section 5.1.
|
||||
const InboxName = "INBOX"
|
||||
|
||||
// CanonicalMailboxName returns the canonical form of a mailbox name. Mailbox names can be
|
||||
// case-sensitive or case-insensitive depending on the backend implementation.
|
||||
// The special INBOX mailbox is case-insensitive.
|
||||
func CanonicalMailboxName(name string) string {
|
||||
if strings.ToUpper(name) == InboxName {
|
||||
return InboxName
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Mailbox attributes definied in RFC 3501 section 7.2.2.
|
||||
const (
|
||||
// It is not possible for any child levels of hierarchy to exist under this\
|
||||
// name; no child levels exist now and none can be created in the future.
|
||||
NoInferiorsAttr = "\\Noinferiors"
|
||||
// It is not possible to use this name as a selectable mailbox.
|
||||
NoSelectAttr = "\\Noselect"
|
||||
// The mailbox has been marked "interesting" by the server; the mailbox
|
||||
// probably contains messages that have been added since the last time the
|
||||
// mailbox was selected.
|
||||
MarkedAttr = "\\Marked"
|
||||
// The mailbox does not contain any additional messages since the last time
|
||||
// the mailbox was selected.
|
||||
UnmarkedAttr = "\\Unmarked"
|
||||
)
|
||||
|
||||
// Mailbox attributes defined in RFC 6154 section 2 (SPECIAL-USE extension).
|
||||
const (
|
||||
// This mailbox presents all messages in the user's message store.
|
||||
AllAttr = "\\All"
|
||||
// This mailbox is used to archive messages.
|
||||
ArchiveAttr = "\\Archive"
|
||||
// This mailbox is used to hold draft messages -- typically, messages that are
|
||||
// being composed but have not yet been sent.
|
||||
DraftsAttr = "\\Drafts"
|
||||
// This mailbox presents all messages marked in some way as "important".
|
||||
FlaggedAttr = "\\Flagged"
|
||||
// This mailbox is where messages deemed to be junk mail are held.
|
||||
JunkAttr = "\\Junk"
|
||||
// This mailbox is used to hold copies of messages that have been sent.
|
||||
SentAttr = "\\Sent"
|
||||
// This mailbox is used to hold messages that have been deleted or marked for
|
||||
// deletion.
|
||||
TrashAttr = "\\Trash"
|
||||
)
|
||||
|
||||
// Mailbox attributes defined in RFC 3348 (CHILDREN extension)
|
||||
const (
|
||||
// The presence of this attribute indicates that the mailbox has child
|
||||
// mailboxes.
|
||||
HasChildrenAttr = "\\HasChildren"
|
||||
// The presence of this attribute indicates that the mailbox has no child
|
||||
// mailboxes.
|
||||
HasNoChildrenAttr = "\\HasNoChildren"
|
||||
)
|
||||
|
||||
// This mailbox attribute is a signal that the mailbox contains messages that
|
||||
// are likely important to the user. This attribute is defined in RFC 8457
|
||||
// section 3.
|
||||
const ImportantAttr = "\\Important"
|
||||
|
||||
// Basic mailbox info.
|
||||
type MailboxInfo struct {
|
||||
// The mailbox attributes.
|
||||
Attributes []string
|
||||
// The server's path separator.
|
||||
Delimiter string
|
||||
// The mailbox name.
|
||||
Name string
|
||||
}
|
||||
|
||||
// Parse mailbox info from fields.
|
||||
func (info *MailboxInfo) Parse(fields []interface{}) error {
|
||||
if len(fields) < 3 {
|
||||
return errors.New("Mailbox info needs at least 3 fields")
|
||||
}
|
||||
|
||||
var err error
|
||||
if info.Attributes, err = ParseStringList(fields[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if info.Delimiter, ok = fields[1].(string); !ok {
|
||||
// The delimiter may be specified as NIL, which gets converted to a nil interface.
|
||||
if fields[1] != nil {
|
||||
return errors.New("Mailbox delimiter must be a string")
|
||||
}
|
||||
info.Delimiter = ""
|
||||
}
|
||||
|
||||
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.Encoding.NewEncoder().String(info.Name)
|
||||
attrs := make([]interface{}, len(info.Attributes))
|
||||
for i, attr := range info.Attributes {
|
||||
attrs[i] = RawString(attr)
|
||||
}
|
||||
|
||||
// If the delimiter is NIL, we need to treat it specially by inserting
|
||||
// a nil field (so that it's later converted to an unquoted NIL atom).
|
||||
var del interface{}
|
||||
|
||||
if info.Delimiter != "" {
|
||||
del = info.Delimiter
|
||||
}
|
||||
|
||||
// Thunderbird doesn't understand delimiters if not quoted
|
||||
return []interface{}{attrs, del, FormatMailboxName(name)}
|
||||
}
|
||||
|
||||
// TODO: optimize this
|
||||
func (info *MailboxInfo) match(name, pattern string) bool {
|
||||
i := strings.IndexAny(pattern, "*%")
|
||||
if i == -1 {
|
||||
// No more wildcards
|
||||
return name == pattern
|
||||
}
|
||||
|
||||
// Get parts before and after wildcard
|
||||
chunk, wildcard, rest := pattern[0:i], pattern[i], pattern[i+1:]
|
||||
|
||||
// Check that name begins with chunk
|
||||
if len(chunk) > 0 && !strings.HasPrefix(name, chunk) {
|
||||
return false
|
||||
}
|
||||
name = strings.TrimPrefix(name, chunk)
|
||||
|
||||
// Expand wildcard
|
||||
var j int
|
||||
for j = 0; j < len(name); j++ {
|
||||
if wildcard == '%' && string(name[j]) == info.Delimiter {
|
||||
break // Stop on delimiter if wildcard is %
|
||||
}
|
||||
// Try to match the rest from here
|
||||
if info.match(name[j:], rest) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return info.match(name[j:], rest)
|
||||
}
|
||||
|
||||
// Match checks if a reference and a pattern matches this mailbox name, as
|
||||
// defined in RFC 3501 section 6.3.8.
|
||||
func (info *MailboxInfo) Match(reference, pattern string) bool {
|
||||
name := info.Name
|
||||
|
||||
if info.Delimiter != "" && strings.HasPrefix(pattern, info.Delimiter) {
|
||||
reference = ""
|
||||
pattern = strings.TrimPrefix(pattern, info.Delimiter)
|
||||
}
|
||||
if reference != "" {
|
||||
if info.Delimiter != "" && !strings.HasSuffix(reference, info.Delimiter) {
|
||||
reference += info.Delimiter
|
||||
}
|
||||
if !strings.HasPrefix(name, reference) {
|
||||
return false
|
||||
}
|
||||
name = strings.TrimPrefix(name, reference)
|
||||
}
|
||||
|
||||
return info.match(name, pattern)
|
||||
}
|
||||
|
||||
// A mailbox status.
|
||||
type MailboxStatus struct {
|
||||
// The mailbox name.
|
||||
Name string
|
||||
// True if the mailbox is open in read-only mode.
|
||||
ReadOnly bool
|
||||
// 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[StatusItem]interface{}
|
||||
|
||||
// The Items map may be accessed in different goroutines. Protect
|
||||
// concurrent writes.
|
||||
ItemsLocker sync.Mutex
|
||||
|
||||
// The mailbox flags.
|
||||
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
|
||||
// The number of messages not seen since the last time the mailbox was opened.
|
||||
Recent uint32
|
||||
// The number of unread messages.
|
||||
Unseen uint32
|
||||
// The next UID.
|
||||
UidNext uint32
|
||||
// Together with a UID, it is a unique identifier for a message.
|
||||
// Must be greater than or equal to 1.
|
||||
UidValidity uint32
|
||||
|
||||
// Per-mailbox limit of message size. Set only if server supports the
|
||||
// APPENDLIMIT extension.
|
||||
AppendLimit uint32
|
||||
}
|
||||
|
||||
// Create a new mailbox status that will contain the specified items.
|
||||
func NewMailboxStatus(name string, items []StatusItem) *MailboxStatus {
|
||||
status := &MailboxStatus{
|
||||
Name: name,
|
||||
Items: make(map[StatusItem]interface{}),
|
||||
}
|
||||
|
||||
for _, k := range items {
|
||||
status.Items[k] = nil
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func (status *MailboxStatus) Parse(fields []interface{}) error {
|
||||
status.Items = make(map[StatusItem]interface{})
|
||||
|
||||
var k StatusItem
|
||||
for i, f := range fields {
|
||||
if i%2 == 0 {
|
||||
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))
|
||||
}
|
||||
} else {
|
||||
status.Items[k] = nil
|
||||
|
||||
var err error
|
||||
switch k {
|
||||
case StatusMessages:
|
||||
status.Messages, err = ParseNumber(f)
|
||||
case StatusRecent:
|
||||
status.Recent, err = ParseNumber(f)
|
||||
case StatusUnseen:
|
||||
status.Unseen, err = ParseNumber(f)
|
||||
case StatusUidNext:
|
||||
status.UidNext, err = ParseNumber(f)
|
||||
case StatusUidValidity:
|
||||
status.UidValidity, err = ParseNumber(f)
|
||||
case StatusAppendLimit:
|
||||
status.AppendLimit, err = ParseNumber(f)
|
||||
default:
|
||||
status.Items[k] = f
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (status *MailboxStatus) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
for k, v := range status.Items {
|
||||
switch k {
|
||||
case StatusMessages:
|
||||
v = status.Messages
|
||||
case StatusRecent:
|
||||
v = status.Recent
|
||||
case StatusUnseen:
|
||||
v = status.Unseen
|
||||
case StatusUidNext:
|
||||
v = status.UidNext
|
||||
case StatusUidValidity:
|
||||
v = status.UidValidity
|
||||
case StatusAppendLimit:
|
||||
v = status.AppendLimit
|
||||
}
|
||||
|
||||
fields = append(fields, RawString(k), v)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func FormatMailboxName(name string) interface{} {
|
||||
// Some e-mails servers don't handle quoted INBOX names correctly so we special-case it.
|
||||
if strings.EqualFold(name, "INBOX") {
|
||||
return RawString(name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
1187
vendor/github.com/emersion/go-imap/message.go
generated
vendored
Normal file
1187
vendor/github.com/emersion/go-imap/message.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
467
vendor/github.com/emersion/go-imap/read.go
generated
vendored
Normal file
467
vendor/github.com/emersion/go-imap/read.go
generated
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
sp = ' '
|
||||
cr = '\r'
|
||||
lf = '\n'
|
||||
dquote = '"'
|
||||
literalStart = '{'
|
||||
literalEnd = '}'
|
||||
listStart = '('
|
||||
listEnd = ')'
|
||||
respCodeStart = '['
|
||||
respCodeEnd = ']'
|
||||
)
|
||||
|
||||
const (
|
||||
crlf = "\r\n"
|
||||
nilAtom = "NIL"
|
||||
)
|
||||
|
||||
// TODO: add CTL to atomSpecials
|
||||
var (
|
||||
quotedSpecials = string([]rune{dquote, '\\'})
|
||||
respSpecials = string([]rune{respCodeEnd})
|
||||
atomSpecials = string([]rune{listStart, listEnd, literalStart, sp, '%', '*'}) + quotedSpecials + respSpecials
|
||||
)
|
||||
|
||||
type parseError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func newParseError(text string) error {
|
||||
return &parseError{errors.New(text)}
|
||||
}
|
||||
|
||||
// IsParseError returns true if the provided error is a parse error produced by
|
||||
// Reader.
|
||||
func IsParseError(err error) bool {
|
||||
_, ok := err.(*parseError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// A string reader.
|
||||
type StringReader interface {
|
||||
// ReadString reads until the first occurrence of delim in the input,
|
||||
// returning a string containing the data up to and including the delimiter.
|
||||
// See https://golang.org/pkg/bufio/#Reader.ReadString
|
||||
ReadString(delim byte) (line string, err error)
|
||||
}
|
||||
|
||||
type reader interface {
|
||||
io.Reader
|
||||
io.RuneScanner
|
||||
StringReader
|
||||
}
|
||||
|
||||
// ParseNumber parses a number.
|
||||
func ParseNumber(f interface{}) (uint32, error) {
|
||||
// Useful for tests
|
||||
if n, ok := f.(uint32); ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var s string
|
||||
switch f := f.(type) {
|
||||
case RawString:
|
||||
s = string(f)
|
||||
case string:
|
||||
s = f
|
||||
default:
|
||||
return 0, newParseError("expected a number, got a non-atom")
|
||||
}
|
||||
|
||||
nbr, err := strconv.ParseUint(string(s), 10, 32)
|
||||
if err != nil {
|
||||
return 0, &parseError{err}
|
||||
}
|
||||
|
||||
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 a, ok := f.(RawString); 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("expected a string list, got a non-list")
|
||||
}
|
||||
|
||||
list := make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
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
|
||||
}
|
||||
|
||||
func trimSuffix(str string, suffix rune) string {
|
||||
return str[:len(str)-1]
|
||||
}
|
||||
|
||||
// An IMAP reader.
|
||||
type Reader struct {
|
||||
MaxLiteralSize uint32 // The maximum literal size.
|
||||
|
||||
reader
|
||||
|
||||
continues chan<- bool
|
||||
|
||||
brackets int
|
||||
inRespCode bool
|
||||
}
|
||||
|
||||
func (r *Reader) ReadSp() error {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if char != sp {
|
||||
return newParseError("expected a space")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reader) ReadCrlf() (err error) {
|
||||
var char rune
|
||||
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
if char == lf {
|
||||
return
|
||||
}
|
||||
if char != cr {
|
||||
err = newParseError("line doesn't end with a CR")
|
||||
return
|
||||
}
|
||||
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
if char != lf {
|
||||
err = newParseError("line doesn't end with a LF")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadAtom() (interface{}, error) {
|
||||
r.brackets = 0
|
||||
|
||||
var atom string
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: list-wildcards and \
|
||||
if r.brackets == 0 && (char == listStart || char == literalStart || char == dquote) {
|
||||
return nil, newParseError("atom contains forbidden char: " + string(char))
|
||||
}
|
||||
if char == cr || char == lf {
|
||||
break
|
||||
}
|
||||
if r.brackets == 0 && (char == sp || char == listEnd) {
|
||||
break
|
||||
}
|
||||
if char == respCodeEnd {
|
||||
if r.brackets == 0 {
|
||||
if r.inRespCode {
|
||||
break
|
||||
} else {
|
||||
return nil, newParseError("atom contains bad brackets nesting")
|
||||
}
|
||||
}
|
||||
r.brackets--
|
||||
}
|
||||
if char == respCodeStart {
|
||||
r.brackets++
|
||||
}
|
||||
|
||||
atom += string(char)
|
||||
}
|
||||
|
||||
r.UnreadRune()
|
||||
|
||||
if atom == nilAtom {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return atom, nil
|
||||
}
|
||||
|
||||
func (r *Reader) ReadLiteral() (Literal, error) {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if char != literalStart {
|
||||
return nil, newParseError("literal string doesn't start with an open brace")
|
||||
}
|
||||
|
||||
lstr, err := r.ReadString(byte(literalEnd))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lstr = trimSuffix(lstr, literalEnd)
|
||||
|
||||
nonSync := strings.HasSuffix(lstr, "+")
|
||||
if nonSync {
|
||||
lstr = trimSuffix(lstr, '+')
|
||||
}
|
||||
|
||||
n, err := strconv.ParseUint(lstr, 10, 32)
|
||||
if err != nil {
|
||||
return nil, newParseError("cannot parse literal length: " + err.Error())
|
||||
}
|
||||
if r.MaxLiteralSize > 0 && uint32(n) > r.MaxLiteralSize {
|
||||
return nil, newParseError("literal exceeding maximum size")
|
||||
}
|
||||
|
||||
if err := r.ReadCrlf(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send continuation request if necessary
|
||||
if r.continues != nil && !nonSync {
|
||||
r.continues <- true
|
||||
}
|
||||
|
||||
// Read literal
|
||||
b := make([]byte, n)
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.NewBuffer(b), nil
|
||||
}
|
||||
|
||||
func (r *Reader) ReadQuotedString() (string, error) {
|
||||
if char, _, err := r.ReadRune(); err != nil {
|
||||
return "", err
|
||||
} else if char != dquote {
|
||||
return "", newParseError("quoted string doesn't start with a double quote")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var escaped bool
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if char == '\\' && !escaped {
|
||||
escaped = true
|
||||
} else {
|
||||
if char == cr || char == lf {
|
||||
r.UnreadRune()
|
||||
return "", newParseError("CR or LF not allowed in quoted string")
|
||||
}
|
||||
if char == dquote && !escaped {
|
||||
break
|
||||
}
|
||||
|
||||
if !strings.ContainsRune(quotedSpecials, char) && escaped {
|
||||
return "", newParseError("quoted string cannot contain backslash followed by a non-quoted-specials char")
|
||||
}
|
||||
|
||||
buf.WriteRune(char)
|
||||
escaped = false
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (r *Reader) ReadFields() (fields []interface{}, err error) {
|
||||
var char rune
|
||||
for {
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = r.UnreadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var field interface{}
|
||||
ok := true
|
||||
switch char {
|
||||
case literalStart:
|
||||
field, err = r.ReadLiteral()
|
||||
case dquote:
|
||||
field, err = r.ReadQuotedString()
|
||||
case listStart:
|
||||
field, err = r.ReadList()
|
||||
case listEnd:
|
||||
ok = false
|
||||
case cr:
|
||||
return
|
||||
default:
|
||||
field, err = r.ReadAtom()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
if char == cr || char == lf || char == listEnd || char == respCodeEnd {
|
||||
if char == cr || char == lf {
|
||||
r.UnreadRune()
|
||||
}
|
||||
return
|
||||
}
|
||||
if char == listStart {
|
||||
r.UnreadRune()
|
||||
continue
|
||||
}
|
||||
if char != sp {
|
||||
err = newParseError("fields are not separated by a space")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) ReadList() (fields []interface{}, err error) {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if char != listStart {
|
||||
err = newParseError("list doesn't start with an open parenthesis")
|
||||
return
|
||||
}
|
||||
|
||||
fields, err = r.ReadFields()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.UnreadRune()
|
||||
if char, _, err = r.ReadRune(); err != nil {
|
||||
return
|
||||
}
|
||||
if char != listEnd {
|
||||
err = newParseError("list doesn't end with a close parenthesis")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadLine() (fields []interface{}, err error) {
|
||||
fields, err = r.ReadFields()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.UnreadRune()
|
||||
err = r.ReadCrlf()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadRespCode() (code StatusRespCode, fields []interface{}, err error) {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if char != respCodeStart {
|
||||
err = newParseError("response code doesn't start with an open bracket")
|
||||
return
|
||||
}
|
||||
|
||||
r.inRespCode = true
|
||||
fields, err = r.ReadFields()
|
||||
r.inRespCode = false
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
err = newParseError("response code doesn't contain any field")
|
||||
return
|
||||
}
|
||||
|
||||
codeStr, ok := fields[0].(string)
|
||||
if !ok {
|
||||
err = newParseError("response code doesn't start with a string atom")
|
||||
return
|
||||
}
|
||||
if codeStr == "" {
|
||||
err = newParseError("response code is empty")
|
||||
return
|
||||
}
|
||||
code = StatusRespCode(strings.ToUpper(codeStr))
|
||||
|
||||
fields = fields[1:]
|
||||
|
||||
r.UnreadRune()
|
||||
char, _, err = r.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if char != respCodeEnd {
|
||||
err = newParseError("response code doesn't end with a close bracket")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) ReadInfo() (info string, err error) {
|
||||
info, err = r.ReadString(byte(lf))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
info = strings.TrimSuffix(info, string(lf))
|
||||
info = strings.TrimSuffix(info, string(cr))
|
||||
info = strings.TrimLeft(info, " ")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func NewReader(r reader) *Reader {
|
||||
return &Reader{reader: r}
|
||||
}
|
||||
|
||||
func NewServerReader(r reader, continues chan<- bool) *Reader {
|
||||
return &Reader{reader: r, continues: continues}
|
||||
}
|
||||
|
||||
type Parser interface {
|
||||
Parse(fields []interface{}) error
|
||||
}
|
||||
181
vendor/github.com/emersion/go-imap/response.go
generated
vendored
Normal file
181
vendor/github.com/emersion/go-imap/response.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Resp is an IMAP response. It is either a *DataResp, a
|
||||
// *ContinuationReq or a *StatusResp.
|
||||
type Resp interface {
|
||||
resp()
|
||||
}
|
||||
|
||||
// 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
|
||||
// The parsed response fields.
|
||||
Fields []interface{}
|
||||
}
|
||||
|
||||
// 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 := RawString(r.Tag)
|
||||
if tag == "" {
|
||||
tag = RawString("*")
|
||||
}
|
||||
|
||||
fields := []interface{}{RawString(tag)}
|
||||
fields = append(fields, r.Fields...)
|
||||
return w.writeLine(fields...)
|
||||
}
|
||||
|
||||
// ContinuationReq is a continuation request response.
|
||||
type ContinuationReq struct {
|
||||
// The info message sent with the continuation request.
|
||||
Info string
|
||||
}
|
||||
|
||||
func (r *ContinuationReq) resp() {}
|
||||
|
||||
func (r *ContinuationReq) WriteTo(w *Writer) error {
|
||||
if err := w.writeString("+"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.Info != "" {
|
||||
if err := w.writeString(string(sp) + r.Info); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return w.writeCrlf()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
return strings.ToUpper(name), data.Fields[1:], true
|
||||
}
|
||||
61
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
Normal file
61
vendor/github.com/emersion/go-imap/responses/authenticate.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-sasl"
|
||||
)
|
||||
|
||||
// An AUTHENTICATE response.
|
||||
type Authenticate struct {
|
||||
Mechanism sasl.Client
|
||||
InitialResponse []byte
|
||||
RepliesCh chan []byte
|
||||
}
|
||||
|
||||
// Implements
|
||||
func (r *Authenticate) Replies() <-chan []byte {
|
||||
return r.RepliesCh
|
||||
}
|
||||
|
||||
func (r *Authenticate) writeLine(l string) error {
|
||||
r.RepliesCh <- []byte(l + "\r\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Authenticate) cancel() error {
|
||||
return r.writeLine("*")
|
||||
}
|
||||
|
||||
func (r *Authenticate) Handle(resp imap.Resp) error {
|
||||
cont, ok := resp.(*imap.ContinuationReq)
|
||||
if !ok {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
// 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 := r.writeLine(encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
r.InitialResponse = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
challenge, err := base64.StdEncoding.DecodeString(cont.Info)
|
||||
if err != nil {
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
reply, err := r.Mechanism.Next(challenge)
|
||||
if err != nil {
|
||||
r.cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(reply)
|
||||
return r.writeLine(encoded)
|
||||
}
|
||||
20
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
Normal file
20
vendor/github.com/emersion/go-imap/responses/capability.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// A CAPABILITY response.
|
||||
// See RFC 3501 section 7.2.1
|
||||
type Capability struct {
|
||||
Caps []string
|
||||
}
|
||||
|
||||
func (r *Capability) WriteTo(w *imap.Writer) error {
|
||||
fields := []interface{}{imap.RawString("CAPABILITY")}
|
||||
for _, cap := range r.Caps {
|
||||
fields = append(fields, imap.RawString(cap))
|
||||
}
|
||||
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
33
vendor/github.com/emersion/go-imap/responses/enabled.go
generated
vendored
Normal file
33
vendor/github.com/emersion/go-imap/responses/enabled.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// An ENABLED response, defined in RFC 5161 section 3.2.
|
||||
type Enabled struct {
|
||||
Caps []string
|
||||
}
|
||||
|
||||
func (r *Enabled) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != "ENABLED" {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
if caps, err := imap.ParseStringList(fields); err != nil {
|
||||
return err
|
||||
} else {
|
||||
r.Caps = append(r.Caps, caps...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Enabled) WriteTo(w *imap.Writer) error {
|
||||
fields := []interface{}{imap.RawString("ENABLED")}
|
||||
for _, cap := range r.Caps {
|
||||
fields = append(fields, imap.RawString(cap))
|
||||
}
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
43
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
Normal file
43
vendor/github.com/emersion/go-imap/responses/expunge.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package responses
|
||||
|
||||
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) 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
|
||||
}
|
||||
|
||||
r.SeqNums <- seqNum
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Expunge) WriteTo(w *imap.Writer) error {
|
||||
for seqNum := range r.SeqNums {
|
||||
resp := imap.NewUntaggedResp([]interface{}{seqNum, imap.RawString(expungeName)})
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
70
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
Normal file
70
vendor/github.com/emersion/go-imap/responses/fetch.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package responses
|
||||
|
||||
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
|
||||
SeqSet *imap.SeqSet
|
||||
Uid bool
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if r.Uid && msg.Uid == 0 {
|
||||
// we requested UIDs and got a message without one --> unilateral update --> ignore
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
var num uint32
|
||||
if r.Uid {
|
||||
num = msg.Uid
|
||||
} else {
|
||||
num = seqNum
|
||||
}
|
||||
|
||||
// Check whether we obtained a result we requested with our SeqSet
|
||||
// If the result is not contained in our SeqSet we have to handle an additional special case:
|
||||
// In case we requested UIDs with a dynamic sequence (i.e. * or n:*) and the maximum UID of the mailbox
|
||||
// is less then our n, the server will supply us with the max UID (cf. RFC 3501 §6.4.8 and §9 `seq-range`).
|
||||
// Thus, such a result is correct and has to be returned by us.
|
||||
if !r.SeqSet.Contains(num) && (!r.Uid || !r.SeqSet.Dynamic()) {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
r.Messages <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Fetch) WriteTo(w *imap.Writer) error {
|
||||
var err error
|
||||
for msg := range r.Messages {
|
||||
resp := imap.NewUntaggedResp([]interface{}{msg.SeqNum, imap.RawString(fetchName), msg.Format()})
|
||||
if err == nil {
|
||||
err = resp.WriteTo(w)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
38
vendor/github.com/emersion/go-imap/responses/idle.go
generated
vendored
Normal file
38
vendor/github.com/emersion/go-imap/responses/idle.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// An IDLE response.
|
||||
type Idle struct {
|
||||
RepliesCh chan []byte
|
||||
Stop <-chan struct{}
|
||||
|
||||
gotContinuationReq bool
|
||||
}
|
||||
|
||||
func (r *Idle) Replies() <-chan []byte {
|
||||
return r.RepliesCh
|
||||
}
|
||||
|
||||
func (r *Idle) stop() {
|
||||
r.RepliesCh <- []byte("DONE\r\n")
|
||||
}
|
||||
|
||||
func (r *Idle) Handle(resp imap.Resp) error {
|
||||
// Wait for a continuation request
|
||||
if _, ok := resp.(*imap.ContinuationReq); ok && !r.gotContinuationReq {
|
||||
r.gotContinuationReq = true
|
||||
|
||||
// We got a continuation request, wait for r.Stop to be closed
|
||||
go func() {
|
||||
<-r.Stop
|
||||
r.stop()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrUnhandled
|
||||
}
|
||||
57
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
Normal file
57
vendor/github.com/emersion/go-imap/responses/list.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package responses
|
||||
|
||||
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
|
||||
type List struct {
|
||||
Mailboxes chan *imap.MailboxInfo
|
||||
Subscribed bool
|
||||
}
|
||||
|
||||
func (r *List) Name() string {
|
||||
if r.Subscribed {
|
||||
return lsubName
|
||||
} else {
|
||||
return listName
|
||||
}
|
||||
}
|
||||
|
||||
func (r *List) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != r.Name() {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
mbox := &imap.MailboxInfo{}
|
||||
if err := mbox.Parse(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Mailboxes <- mbox
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *List) WriteTo(w *imap.Writer) error {
|
||||
respName := r.Name()
|
||||
|
||||
for mbox := range r.Mailboxes {
|
||||
fields := []interface{}{imap.RawString(respName)}
|
||||
fields = append(fields, mbox.Format()...)
|
||||
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
if err := resp.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
35
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
Normal file
35
vendor/github.com/emersion/go-imap/responses/responses.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Replier is a Handler that needs to send raw data (for instance
|
||||
// AUTHENTICATE).
|
||||
type Replier interface {
|
||||
Handler
|
||||
Replies() <-chan []byte
|
||||
}
|
||||
41
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
Normal file
41
vendor/github.com/emersion/go-imap/responses/search.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package responses
|
||||
|
||||
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) Handle(resp imap.Resp) error {
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != searchName {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
func (r *Search) WriteTo(w *imap.Writer) (err error) {
|
||||
fields := []interface{}{imap.RawString(searchName)}
|
||||
for _, id := range r.Ids {
|
||||
fields = append(fields, id)
|
||||
}
|
||||
|
||||
resp := imap.NewUntaggedResp(fields)
|
||||
return resp.WriteTo(w)
|
||||
}
|
||||
142
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
Normal file
142
vendor/github.com/emersion/go-imap/responses/select.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
)
|
||||
|
||||
// A SELECT response.
|
||||
type Select struct {
|
||||
Mailbox *imap.MailboxStatus
|
||||
}
|
||||
|
||||
func (r *Select) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{Items: make(map[imap.StatusItem]interface{})}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
|
||||
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)
|
||||
case *imap.StatusResp:
|
||||
if len(resp.Arguments) < 1 {
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
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)
|
||||
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:
|
||||
return ErrUnhandled
|
||||
}
|
||||
|
||||
if item != "" {
|
||||
mbox.ItemsLocker.Lock()
|
||||
mbox.Items[item] = nil
|
||||
mbox.ItemsLocker.Unlock()
|
||||
}
|
||||
default:
|
||||
return ErrUnhandled
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Select) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
|
||||
if mbox.Flags != nil {
|
||||
flags := make([]interface{}, len(mbox.Flags))
|
||||
for i, f := range mbox.Flags {
|
||||
flags[i] = imap.RawString(f)
|
||||
}
|
||||
res := imap.NewUntaggedResp([]interface{}{imap.RawString("FLAGS"), flags})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if mbox.PermanentFlags != nil {
|
||||
flags := make([]interface{}, len(mbox.PermanentFlags))
|
||||
for i, f := range mbox.PermanentFlags {
|
||||
flags[i] = imap.RawString(f)
|
||||
}
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodePermanentFlags,
|
||||
Arguments: []interface{}{flags},
|
||||
Info: "Flags permitted.",
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if mbox.UnseenSeqNum > 0 {
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUnseen,
|
||||
Arguments: []interface{}{mbox.UnseenSeqNum},
|
||||
Info: fmt.Sprintf("Message %d is first unseen", mbox.UnseenSeqNum),
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for k := range r.Mailbox.Items {
|
||||
switch k {
|
||||
case imap.StatusMessages:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Messages, imap.RawString("EXISTS")})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusRecent:
|
||||
res := imap.NewUntaggedResp([]interface{}{mbox.Recent, imap.RawString("RECENT")})
|
||||
if err := res.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusUidNext:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidNext,
|
||||
Arguments: []interface{}{mbox.UidNext},
|
||||
Info: "Predicted next UID",
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
case imap.StatusUidValidity:
|
||||
statusRes := &imap.StatusResp{
|
||||
Type: imap.StatusRespOk,
|
||||
Code: imap.CodeUidValidity,
|
||||
Arguments: []interface{}{mbox.UidValidity},
|
||||
Info: "UIDs valid",
|
||||
}
|
||||
if err := statusRes.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
53
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
Normal file
53
vendor/github.com/emersion/go-imap/responses/status.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"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) Handle(resp imap.Resp) error {
|
||||
if r.Mailbox == nil {
|
||||
r.Mailbox = &imap.MailboxStatus{}
|
||||
}
|
||||
mbox := r.Mailbox
|
||||
|
||||
name, fields, ok := imap.ParseNamedResp(resp)
|
||||
if !ok || name != statusName {
|
||||
return ErrUnhandled
|
||||
} else if len(fields) < 2 {
|
||||
return errNotEnoughFields
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var items []interface{}
|
||||
if items, ok = fields[1].([]interface{}); !ok {
|
||||
return errors.New("STATUS response expects a list as second argument")
|
||||
}
|
||||
|
||||
mbox.Items = nil
|
||||
return mbox.Parse(items)
|
||||
}
|
||||
|
||||
func (r *Status) WriteTo(w *imap.Writer) error {
|
||||
mbox := r.Mailbox
|
||||
name, _ := utf7.Encoding.NewEncoder().String(mbox.Name)
|
||||
fields := []interface{}{imap.RawString(statusName), imap.FormatMailboxName(name), mbox.Format()}
|
||||
return imap.NewUntaggedResp(fields).WriteTo(w)
|
||||
}
|
||||
371
vendor/github.com/emersion/go-imap/search.go
generated
vendored
Normal file
371
vendor/github.com/emersion/go-imap/search.go
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func maybeString(mystery interface{}) string {
|
||||
if s, ok := mystery.(string); ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func convertField(f interface{}, charsetReader func(io.Reader) io.Reader) string {
|
||||
// An IMAP string contains only 7-bit data, no need to decode it
|
||||
if s, ok := f.(string); ok {
|
||||
return s
|
||||
}
|
||||
|
||||
// If no charset is provided, getting directly the string is faster
|
||||
if charsetReader == nil {
|
||||
if stringer, ok := f.(fmt.Stringer); ok {
|
||||
return stringer.String()
|
||||
}
|
||||
}
|
||||
|
||||
// Not a string, it must be a literal
|
||||
l, ok := f.(Literal)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
var r io.Reader = l
|
||||
if charsetReader != nil {
|
||||
if dec := charsetReader(r); dec != nil {
|
||||
r = dec
|
||||
}
|
||||
}
|
||||
|
||||
b := make([]byte, l.Len())
|
||||
if _, err := io.ReadFull(r, b); err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func popSearchField(fields []interface{}) (interface{}, []interface{}, error) {
|
||||
if len(fields) == 0 {
|
||||
return nil, nil, errors.New("imap: no enough fields for search key")
|
||||
}
|
||||
return fields[0], fields[1:], nil
|
||||
}
|
||||
|
||||
// SearchCriteria is a search criteria. A message matches the criteria if and
|
||||
// only if it matches each one of its fields.
|
||||
type SearchCriteria struct {
|
||||
SeqNum *SeqSet // Sequence number is in sequence set
|
||||
Uid *SeqSet // UID is in sequence set
|
||||
|
||||
// Time and timezone are ignored
|
||||
Since time.Time // Internal date is since this date
|
||||
Before time.Time // Internal date is before this date
|
||||
SentSince time.Time // Date header field is since this date
|
||||
SentBefore time.Time // Date header field is before this date
|
||||
|
||||
Header textproto.MIMEHeader // Each header field value is present
|
||||
Body []string // Each string is in the body
|
||||
Text []string // Each string is in the text (header + body)
|
||||
|
||||
WithFlags []string // Each flag is present
|
||||
WithoutFlags []string // Each flag is not present
|
||||
|
||||
Larger uint32 // Size is larger than this number
|
||||
Smaller uint32 // Size is smaller than this number
|
||||
|
||||
Not []*SearchCriteria // Each criteria doesn't match
|
||||
Or [][2]*SearchCriteria // Each criteria pair has at least one match of two
|
||||
}
|
||||
|
||||
// NewSearchCriteria creates a new search criteria.
|
||||
func NewSearchCriteria() *SearchCriteria {
|
||||
return &SearchCriteria{Header: make(textproto.MIMEHeader)}
|
||||
}
|
||||
|
||||
func (c *SearchCriteria) parseField(fields []interface{}, charsetReader func(io.Reader) io.Reader) ([]interface{}, error) {
|
||||
if len(fields) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f := fields[0]
|
||||
fields = fields[1:]
|
||||
|
||||
if subfields, ok := f.([]interface{}); ok {
|
||||
return fields, c.ParseWithCharset(subfields, charsetReader)
|
||||
}
|
||||
|
||||
key, ok := f.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("imap: invalid search criteria field type: %T", f)
|
||||
}
|
||||
key = strings.ToUpper(key)
|
||||
|
||||
var err error
|
||||
switch key {
|
||||
case "ALL":
|
||||
// Nothing to do
|
||||
case "ANSWERED", "DELETED", "DRAFT", "FLAGGED", "RECENT", "SEEN":
|
||||
c.WithFlags = append(c.WithFlags, CanonicalFlag("\\"+key))
|
||||
case "BCC", "CC", "FROM", "SUBJECT", "TO":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.Header == nil {
|
||||
c.Header = make(textproto.MIMEHeader)
|
||||
}
|
||||
c.Header.Add(key, convertField(f, charsetReader))
|
||||
case "BEFORE":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else if c.Before.IsZero() || t.Before(c.Before) {
|
||||
c.Before = t
|
||||
}
|
||||
case "BODY":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.Body = append(c.Body, convertField(f, charsetReader))
|
||||
}
|
||||
case "HEADER":
|
||||
var f1, f2 interface{}
|
||||
if f1, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if f2, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if c.Header == nil {
|
||||
c.Header = make(textproto.MIMEHeader)
|
||||
}
|
||||
c.Header.Add(maybeString(f1), convertField(f2, charsetReader))
|
||||
}
|
||||
case "KEYWORD":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.WithFlags = append(c.WithFlags, CanonicalFlag(maybeString(f)))
|
||||
}
|
||||
case "LARGER":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if n, err := ParseNumber(f); err != nil {
|
||||
return nil, err
|
||||
} else if c.Larger == 0 || n > c.Larger {
|
||||
c.Larger = n
|
||||
}
|
||||
case "NEW":
|
||||
c.WithFlags = append(c.WithFlags, RecentFlag)
|
||||
c.WithoutFlags = append(c.WithoutFlags, SeenFlag)
|
||||
case "NOT":
|
||||
not := new(SearchCriteria)
|
||||
if fields, err = not.parseField(fields, charsetReader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Not = append(c.Not, not)
|
||||
case "OLD":
|
||||
c.WithoutFlags = append(c.WithoutFlags, RecentFlag)
|
||||
case "ON":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.Since = t
|
||||
c.Before = t.Add(24 * time.Hour)
|
||||
}
|
||||
case "OR":
|
||||
c1, c2 := new(SearchCriteria), new(SearchCriteria)
|
||||
if fields, err = c1.parseField(fields, charsetReader); err != nil {
|
||||
return nil, err
|
||||
} else if fields, err = c2.parseField(fields, charsetReader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Or = append(c.Or, [2]*SearchCriteria{c1, c2})
|
||||
case "SENTBEFORE":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else if c.SentBefore.IsZero() || t.Before(c.SentBefore) {
|
||||
c.SentBefore = t
|
||||
}
|
||||
case "SENTON":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.SentSince = t
|
||||
c.SentBefore = t.Add(24 * time.Hour)
|
||||
}
|
||||
case "SENTSINCE":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else if c.SentSince.IsZero() || t.After(c.SentSince) {
|
||||
c.SentSince = t
|
||||
}
|
||||
case "SINCE":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if t, err := time.Parse(DateLayout, maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
} else if c.Since.IsZero() || t.After(c.Since) {
|
||||
c.Since = t
|
||||
}
|
||||
case "SMALLER":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if n, err := ParseNumber(f); err != nil {
|
||||
return nil, err
|
||||
} else if c.Smaller == 0 || n < c.Smaller {
|
||||
c.Smaller = n
|
||||
}
|
||||
case "TEXT":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.Text = append(c.Text, convertField(f, charsetReader))
|
||||
}
|
||||
case "UID":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else if c.Uid, err = ParseSeqSet(maybeString(f)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "UNANSWERED", "UNDELETED", "UNDRAFT", "UNFLAGGED", "UNSEEN":
|
||||
unflag := strings.TrimPrefix(key, "UN")
|
||||
c.WithoutFlags = append(c.WithoutFlags, CanonicalFlag("\\"+unflag))
|
||||
case "UNKEYWORD":
|
||||
if f, fields, err = popSearchField(fields); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
c.WithoutFlags = append(c.WithoutFlags, CanonicalFlag(maybeString(f)))
|
||||
}
|
||||
default: // Try to parse a sequence set
|
||||
if c.SeqNum, err = ParseSeqSet(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// ParseWithCharset parses a search criteria from the provided fields.
|
||||
// charsetReader is an optional function that converts from the fields charset
|
||||
// to UTF-8.
|
||||
func (c *SearchCriteria) ParseWithCharset(fields []interface{}, charsetReader func(io.Reader) io.Reader) error {
|
||||
for len(fields) > 0 {
|
||||
var err error
|
||||
if fields, err = c.parseField(fields, charsetReader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format formats search criteria to fields. UTF-8 is used.
|
||||
func (c *SearchCriteria) Format() []interface{} {
|
||||
var fields []interface{}
|
||||
|
||||
if c.SeqNum != nil {
|
||||
fields = append(fields, c.SeqNum)
|
||||
}
|
||||
if c.Uid != nil {
|
||||
fields = append(fields, RawString("UID"), c.Uid)
|
||||
}
|
||||
|
||||
if !c.Since.IsZero() && !c.Before.IsZero() && c.Before.Sub(c.Since) == 24*time.Hour {
|
||||
fields = append(fields, RawString("ON"), searchDate(c.Since))
|
||||
} else {
|
||||
if !c.Since.IsZero() {
|
||||
fields = append(fields, RawString("SINCE"), searchDate(c.Since))
|
||||
}
|
||||
if !c.Before.IsZero() {
|
||||
fields = append(fields, RawString("BEFORE"), searchDate(c.Before))
|
||||
}
|
||||
}
|
||||
if !c.SentSince.IsZero() && !c.SentBefore.IsZero() && c.SentBefore.Sub(c.SentSince) == 24*time.Hour {
|
||||
fields = append(fields, RawString("SENTON"), searchDate(c.SentSince))
|
||||
} else {
|
||||
if !c.SentSince.IsZero() {
|
||||
fields = append(fields, RawString("SENTSINCE"), searchDate(c.SentSince))
|
||||
}
|
||||
if !c.SentBefore.IsZero() {
|
||||
fields = append(fields, RawString("SENTBEFORE"), searchDate(c.SentBefore))
|
||||
}
|
||||
}
|
||||
|
||||
for key, values := range c.Header {
|
||||
var prefields []interface{}
|
||||
switch key {
|
||||
case "Bcc", "Cc", "From", "Subject", "To":
|
||||
prefields = []interface{}{RawString(strings.ToUpper(key))}
|
||||
default:
|
||||
prefields = []interface{}{RawString("HEADER"), key}
|
||||
}
|
||||
for _, value := range values {
|
||||
fields = append(fields, prefields...)
|
||||
fields = append(fields, value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, value := range c.Body {
|
||||
fields = append(fields, RawString("BODY"), value)
|
||||
}
|
||||
for _, value := range c.Text {
|
||||
fields = append(fields, RawString("TEXT"), value)
|
||||
}
|
||||
|
||||
for _, flag := range c.WithFlags {
|
||||
var subfields []interface{}
|
||||
switch flag {
|
||||
case AnsweredFlag, DeletedFlag, DraftFlag, FlaggedFlag, RecentFlag, SeenFlag:
|
||||
subfields = []interface{}{RawString(strings.ToUpper(strings.TrimPrefix(flag, "\\")))}
|
||||
default:
|
||||
subfields = []interface{}{RawString("KEYWORD"), RawString(flag)}
|
||||
}
|
||||
fields = append(fields, subfields...)
|
||||
}
|
||||
for _, flag := range c.WithoutFlags {
|
||||
var subfields []interface{}
|
||||
switch flag {
|
||||
case AnsweredFlag, DeletedFlag, DraftFlag, FlaggedFlag, SeenFlag:
|
||||
subfields = []interface{}{RawString("UN" + strings.ToUpper(strings.TrimPrefix(flag, "\\")))}
|
||||
case RecentFlag:
|
||||
subfields = []interface{}{RawString("OLD")}
|
||||
default:
|
||||
subfields = []interface{}{RawString("UNKEYWORD"), RawString(flag)}
|
||||
}
|
||||
fields = append(fields, subfields...)
|
||||
}
|
||||
|
||||
if c.Larger > 0 {
|
||||
fields = append(fields, RawString("LARGER"), c.Larger)
|
||||
}
|
||||
if c.Smaller > 0 {
|
||||
fields = append(fields, RawString("SMALLER"), c.Smaller)
|
||||
}
|
||||
|
||||
for _, not := range c.Not {
|
||||
fields = append(fields, RawString("NOT"), not.Format())
|
||||
}
|
||||
|
||||
for _, or := range c.Or {
|
||||
fields = append(fields, RawString("OR"), or[0].Format(), or[1].Format())
|
||||
}
|
||||
|
||||
// Not a single criteria given, add ALL criteria as fallback
|
||||
if len(fields) == 0 {
|
||||
fields = append(fields, RawString("ALL"))
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
289
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
Normal file
289
vendor/github.com/emersion/go-imap/seqset.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrBadSeqSet is used to report problems with the format of a sequence set
|
||||
// value.
|
||||
type ErrBadSeqSet string
|
||||
|
||||
func (err ErrBadSeqSet) Error() string {
|
||||
return fmt.Sprintf("imap: bad sequence set value %q", string(err))
|
||||
}
|
||||
|
||||
// Seq represents a single seq-number or seq-range value (RFC 3501 ABNF). Values
|
||||
// may be static (e.g. "1", "2:4") or dynamic (e.g. "*", "1:*"). A seq-number is
|
||||
// represented by setting Start = Stop. Zero is used to represent "*", which is
|
||||
// safe because seq-number uses nz-number rule. The order of values is always
|
||||
// Start <= Stop, except when representing "n:*", where Start = n and Stop = 0.
|
||||
type Seq struct {
|
||||
Start, Stop uint32
|
||||
}
|
||||
|
||||
// parseSeqNumber parses a single seq-number value (non-zero uint32 or "*").
|
||||
func parseSeqNumber(v string) (uint32, error) {
|
||||
if n, err := strconv.ParseUint(v, 10, 32); err == nil && v[0] != '0' {
|
||||
return uint32(n), nil
|
||||
} else if v == "*" {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, ErrBadSeqSet(v)
|
||||
}
|
||||
|
||||
// parseSeq creates a new seq instance by parsing strings in the format "n" or
|
||||
// "n:m", where n and/or m may be "*". An error is returned for invalid values.
|
||||
func parseSeq(v string) (s Seq, err error) {
|
||||
if sep := strings.IndexRune(v, ':'); sep < 0 {
|
||||
s.Start, err = parseSeqNumber(v)
|
||||
s.Stop = s.Start
|
||||
return
|
||||
} else if s.Start, err = parseSeqNumber(v[:sep]); err == nil {
|
||||
if s.Stop, err = parseSeqNumber(v[sep+1:]); err == nil {
|
||||
if (s.Stop < s.Start && s.Stop != 0) || s.Start == 0 {
|
||||
s.Start, s.Stop = s.Stop, s.Start
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return s, ErrBadSeqSet(v)
|
||||
}
|
||||
|
||||
// Contains returns true if the seq-number q is contained in sequence value s.
|
||||
// The dynamic value "*" contains only other "*" values, the dynamic range "n:*"
|
||||
// contains "*" and all numbers >= n.
|
||||
func (s Seq) Contains(q uint32) bool {
|
||||
if q == 0 {
|
||||
return s.Stop == 0 // "*" is contained only in "*" and "n:*"
|
||||
}
|
||||
return s.Start != 0 && s.Start <= q && (q <= s.Stop || s.Stop == 0)
|
||||
}
|
||||
|
||||
// Less returns true if s precedes and does not contain seq-number q.
|
||||
func (s Seq) Less(q uint32) bool {
|
||||
return (s.Stop < q || q == 0) && s.Stop != 0
|
||||
}
|
||||
|
||||
// Merge combines sequence values s and t into a single union if the two
|
||||
// intersect or one is a superset of the other. The order of s and t does not
|
||||
// matter. If the values cannot be merged, s is returned unmodified and ok is
|
||||
// set to false.
|
||||
func (s Seq) Merge(t Seq) (union Seq, ok bool) {
|
||||
if union = s; s == t {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
if s.Start != 0 && t.Start != 0 {
|
||||
// s and t are any combination of "n", "n:m", or "n:*"
|
||||
if s.Start > t.Start {
|
||||
s, t = t, s
|
||||
}
|
||||
// s starts at or before t, check where it ends
|
||||
if (s.Stop >= t.Stop && t.Stop != 0) || s.Stop == 0 {
|
||||
return s, true // s is a superset of t
|
||||
}
|
||||
// s is "n" or "n:m", if m == ^uint32(0) then t is "n:*"
|
||||
if s.Stop+1 >= t.Start || s.Stop == ^uint32(0) {
|
||||
return Seq{s.Start, t.Stop}, true // s intersects or touches t
|
||||
}
|
||||
return
|
||||
}
|
||||
// exactly one of s and t is "*"
|
||||
if s.Start == 0 {
|
||||
if t.Stop == 0 {
|
||||
return t, true // s is "*", t is "n:*"
|
||||
}
|
||||
} else if s.Stop == 0 {
|
||||
return s, true // s is "n:*", t is "*"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// String returns sequence value s as a seq-number or seq-range string.
|
||||
func (s Seq) String() string {
|
||||
if s.Start == s.Stop {
|
||||
if s.Start == 0 {
|
||||
return "*"
|
||||
}
|
||||
return strconv.FormatUint(uint64(s.Start), 10)
|
||||
}
|
||||
b := strconv.AppendUint(make([]byte, 0, 24), uint64(s.Start), 10)
|
||||
if s.Stop == 0 {
|
||||
return string(append(b, ':', '*'))
|
||||
}
|
||||
return string(strconv.AppendUint(append(b, ':'), uint64(s.Stop), 10))
|
||||
}
|
||||
|
||||
// SeqSet is used to represent a set of message sequence numbers or UIDs (see
|
||||
// sequence-set ABNF rule). The zero value is an empty set.
|
||||
type SeqSet struct {
|
||||
Set []Seq
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Add inserts new sequence values into the set. The string format is described
|
||||
// by RFC 3501 sequence-set ABNF rule. If an error is encountered, all values
|
||||
// inserted successfully prior to the error remain in the set.
|
||||
func (s *SeqSet) Add(set string) error {
|
||||
for _, sv := range strings.Split(set, ",") {
|
||||
v, err := parseSeq(sv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.insert(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddNum inserts new sequence numbers into the set. The value 0 represents "*".
|
||||
func (s *SeqSet) AddNum(q ...uint32) {
|
||||
for _, v := range q {
|
||||
s.insert(Seq{v, v})
|
||||
}
|
||||
}
|
||||
|
||||
// AddRange inserts a new sequence range into the set.
|
||||
func (s *SeqSet) AddRange(Start, Stop uint32) {
|
||||
if (Stop < Start && Stop != 0) || Start == 0 {
|
||||
s.insert(Seq{Stop, Start})
|
||||
} else {
|
||||
s.insert(Seq{Start, Stop})
|
||||
}
|
||||
}
|
||||
|
||||
// AddSet inserts all values from t into s.
|
||||
func (s *SeqSet) AddSet(t *SeqSet) {
|
||||
for _, v := range t.Set {
|
||||
s.insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes all values from the set.
|
||||
func (s *SeqSet) Clear() {
|
||||
s.Set = s.Set[:0]
|
||||
}
|
||||
|
||||
// Empty returns true if the sequence set does not contain any values.
|
||||
func (s SeqSet) Empty() bool {
|
||||
return len(s.Set) == 0
|
||||
}
|
||||
|
||||
// Dynamic returns true if the set contains "*" or "n:*" values.
|
||||
func (s SeqSet) Dynamic() bool {
|
||||
return len(s.Set) > 0 && s.Set[len(s.Set)-1].Stop == 0
|
||||
}
|
||||
|
||||
// Contains returns true if the non-zero sequence number or UID q is contained
|
||||
// in the set. The dynamic range "n:*" contains all q >= n. It is the caller's
|
||||
// responsibility to handle the special case where q is the maximum UID in the
|
||||
// mailbox and q < n (i.e. the set cannot match UIDs against "*:n" or "*" since
|
||||
// it doesn't know what the maximum value is).
|
||||
func (s SeqSet) Contains(q uint32) bool {
|
||||
if _, ok := s.search(q); ok {
|
||||
return q != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns a sorted representation of all contained sequence values.
|
||||
func (s SeqSet) String() string {
|
||||
if len(s.Set) == 0 {
|
||||
return ""
|
||||
}
|
||||
b := make([]byte, 0, 64)
|
||||
for _, v := range s.Set {
|
||||
b = append(b, ',')
|
||||
if v.Start == 0 {
|
||||
b = append(b, '*')
|
||||
continue
|
||||
}
|
||||
b = strconv.AppendUint(b, uint64(v.Start), 10)
|
||||
if v.Start != v.Stop {
|
||||
if v.Stop == 0 {
|
||||
b = append(b, ':', '*')
|
||||
continue
|
||||
}
|
||||
b = strconv.AppendUint(append(b, ':'), uint64(v.Stop), 10)
|
||||
}
|
||||
}
|
||||
return string(b[1:])
|
||||
}
|
||||
|
||||
// insert adds sequence value v to the set.
|
||||
func (s *SeqSet) insert(v Seq) {
|
||||
i, _ := s.search(v.Start)
|
||||
merged := false
|
||||
if i > 0 {
|
||||
// try merging with the preceding entry (e.g. "1,4".insert(2), i == 1)
|
||||
s.Set[i-1], merged = s.Set[i-1].Merge(v)
|
||||
}
|
||||
if i == len(s.Set) {
|
||||
// v was either merged with the last entry or needs to be appended
|
||||
if !merged {
|
||||
s.insertAt(i, v)
|
||||
}
|
||||
return
|
||||
} else if merged {
|
||||
i--
|
||||
} else if s.Set[i], merged = s.Set[i].Merge(v); !merged {
|
||||
s.insertAt(i, v) // insert in the middle (e.g. "1,5".insert(3), i == 1)
|
||||
return
|
||||
}
|
||||
// v was merged with s.Set[i], continue trying to merge until the end
|
||||
for j := i + 1; j < len(s.Set); j++ {
|
||||
if s.Set[i], merged = s.Set[i].Merge(s.Set[j]); !merged {
|
||||
if j > i+1 {
|
||||
// cut out all entries between i and j that were merged
|
||||
s.Set = append(s.Set[:i+1], s.Set[j:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// everything after s.Set[i] was merged
|
||||
s.Set = s.Set[:i+1]
|
||||
}
|
||||
|
||||
// insertAt inserts a new sequence value v at index i, resizing s.Set as needed.
|
||||
func (s *SeqSet) insertAt(i int, v Seq) {
|
||||
if n := len(s.Set); i == n {
|
||||
// insert at the end
|
||||
s.Set = append(s.Set, v)
|
||||
return
|
||||
} else if n < cap(s.Set) {
|
||||
// enough space, shift everything at and after i to the right
|
||||
s.Set = s.Set[:n+1]
|
||||
copy(s.Set[i+1:], s.Set[i:])
|
||||
} else {
|
||||
// allocate new slice and copy everything, n is at least 1
|
||||
set := make([]Seq, n+1, n*2)
|
||||
copy(set, s.Set[:i])
|
||||
copy(set[i+1:], s.Set[i:])
|
||||
s.Set = set
|
||||
}
|
||||
s.Set[i] = v
|
||||
}
|
||||
|
||||
// search attempts to find the index of the sequence set value that contains q.
|
||||
// If no values contain q, the returned index is the position where q should be
|
||||
// inserted and ok is set to false.
|
||||
func (s SeqSet) search(q uint32) (i int, ok bool) {
|
||||
min, max := 0, len(s.Set)-1
|
||||
for min < max {
|
||||
if mid := (min + max) >> 1; s.Set[mid].Less(q) {
|
||||
min = mid + 1
|
||||
} else {
|
||||
max = mid
|
||||
}
|
||||
}
|
||||
if max < 0 || s.Set[min].Less(q) {
|
||||
return len(s.Set), false // q is the new largest value
|
||||
}
|
||||
return min, s.Set[min].Contains(q)
|
||||
}
|
||||
136
vendor/github.com/emersion/go-imap/status.go
generated
vendored
Normal file
136
vendor/github.com/emersion/go-imap/status.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// A status response type.
|
||||
type StatusRespType string
|
||||
|
||||
// Status response types defined in RFC 3501 section 7.1.
|
||||
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.
|
||||
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.
|
||||
StatusRespNo StatusRespType = "NO"
|
||||
|
||||
// The BAD response indicates an error message from the server. When
|
||||
// tagged, it reports a protocol-level error in the client's command;
|
||||
// the tag indicates the command that caused the error. The untagged
|
||||
// form indicates a protocol-level error for which the associated
|
||||
// command can not be determined; it can also indicate an internal
|
||||
// server failure.
|
||||
StatusRespBad StatusRespType = "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.
|
||||
StatusRespPreauth StatusRespType = "PREAUTH"
|
||||
|
||||
// The BYE response is always untagged, and indicates that the server
|
||||
// is about to close the connection.
|
||||
StatusRespBye StatusRespType = "BYE"
|
||||
)
|
||||
|
||||
type StatusRespCode string
|
||||
|
||||
// Status response codes defined in RFC 3501 section 7.1.
|
||||
const (
|
||||
CodeAlert StatusRespCode = "ALERT"
|
||||
CodeBadCharset StatusRespCode = "BADCHARSET"
|
||||
CodeCapability StatusRespCode = "CAPABILITY"
|
||||
CodeParse StatusRespCode = "PARSE"
|
||||
CodePermanentFlags StatusRespCode = "PERMANENTFLAGS"
|
||||
CodeReadOnly StatusRespCode = "READ-ONLY"
|
||||
CodeReadWrite StatusRespCode = "READ-WRITE"
|
||||
CodeTryCreate StatusRespCode = "TRYCREATE"
|
||||
CodeUidNext StatusRespCode = "UIDNEXT"
|
||||
CodeUidValidity StatusRespCode = "UIDVALIDITY"
|
||||
CodeUnseen StatusRespCode = "UNSEEN"
|
||||
)
|
||||
|
||||
// A status response.
|
||||
// See RFC 3501 section 7.1
|
||||
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 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 {
|
||||
if r == nil {
|
||||
// No status response, connection closed before we get one
|
||||
return errors.New("imap: connection closed during command execution")
|
||||
}
|
||||
|
||||
if r.Type == StatusRespNo || r.Type == StatusRespBad {
|
||||
return errors.New(r.Info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *StatusResp) WriteTo(w *Writer) error {
|
||||
tag := RawString(r.Tag)
|
||||
if tag == "" {
|
||||
tag = "*"
|
||||
}
|
||||
|
||||
if err := w.writeFields([]interface{}{RawString(tag), RawString(r.Type)}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeString(string(sp)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.Code != "" {
|
||||
if err := w.writeRespCode(r.Code, r.Arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeString(string(sp)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.writeString(r.Info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.writeCrlf()
|
||||
}
|
||||
|
||||
// ErrStatusResp can be returned by a server.Handler to replace the default status
|
||||
// response. The response tag must be empty.
|
||||
//
|
||||
// To suppress default response, Resp should be set to nil.
|
||||
type ErrStatusResp struct {
|
||||
// Response to send instead of default.
|
||||
Resp *StatusResp
|
||||
}
|
||||
|
||||
func (err *ErrStatusResp) Error() string {
|
||||
if err.Resp == nil {
|
||||
return "imap: suppressed response"
|
||||
}
|
||||
return err.Resp.Info
|
||||
}
|
||||
149
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
Normal file
149
vendor/github.com/emersion/go-imap/utf7/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// ErrInvalidUTF7 means that a transformer encountered invalid UTF-7.
|
||||
var ErrInvalidUTF7 = errors.New("utf7: invalid UTF-7")
|
||||
|
||||
type decoder struct {
|
||||
ascii bool
|
||||
}
|
||||
|
||||
func (d *decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
ch := src[i]
|
||||
|
||||
if ch < min || ch > max { // Illegal code point in ASCII mode
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
if ch != '&' {
|
||||
if nDst+1 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
return
|
||||
}
|
||||
|
||||
nSrc++
|
||||
|
||||
dst[nDst] = ch
|
||||
nDst++
|
||||
|
||||
d.ascii = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the end of the Base64 or "&-" segment
|
||||
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 = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(src) { // Implicit shift ("&...")
|
||||
if atEOF {
|
||||
err = ErrInvalidUTF7
|
||||
} else {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var b []byte
|
||||
if i == start { // Escape sequence "&-"
|
||||
b = []byte{'&'}
|
||||
d.ascii = true
|
||||
} else { // Control or non-ASCII code points in base64
|
||||
if !d.ascii { // Null shift ("&...-&...-")
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
b = decode(src[start:i])
|
||||
d.ascii = false
|
||||
}
|
||||
|
||||
if len(b) == 0 { // Bad encoding
|
||||
err = ErrInvalidUTF7
|
||||
return
|
||||
}
|
||||
|
||||
if nDst+len(b) > len(dst) {
|
||||
d.ascii = true
|
||||
err = transform.ErrShortDst
|
||||
return
|
||||
}
|
||||
|
||||
nSrc = i + 1
|
||||
|
||||
for _, ch := range b {
|
||||
dst[nDst] = ch
|
||||
nDst++
|
||||
}
|
||||
}
|
||||
|
||||
if atEOF {
|
||||
d.ascii = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *decoder) Reset() {
|
||||
d.ascii = true
|
||||
}
|
||||
|
||||
// Extracts UTF-16-BE bytes from base64 data and converts them to UTF-8.
|
||||
// A nil slice is returned if the encoding is invalid.
|
||||
func decode(b64 []byte) []byte {
|
||||
var b []byte
|
||||
|
||||
// Allocate a single block of memory large enough to store the Base64 data
|
||||
// (if padding is required), UTF-16-BE bytes, and decoded UTF-8 bytes.
|
||||
// Since a 2-byte UTF-16 sequence may expand into a 3-byte UTF-8 sequence,
|
||||
// double the space allocation for UTF-8.
|
||||
if n := len(b64); b64[n-1] == '=' {
|
||||
return nil
|
||||
} else if n&3 == 0 {
|
||||
b = make([]byte, b64Enc.DecodedLen(n)*3)
|
||||
} else {
|
||||
n += 4 - 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 := b64Enc.Decode(b, b64)
|
||||
if err != nil || n&1 == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode UTF-16-BE into the remaining 2/3rds of b
|
||||
b, s := b[:n], b[n:]
|
||||
j := 0
|
||||
for i := 0; i < n; i += 2 {
|
||||
r := rune(b[i])<<8 | rune(b[i+1])
|
||||
if utf16.IsSurrogate(r) {
|
||||
if i += 2; i == n {
|
||||
return nil
|
||||
}
|
||||
r2 := rune(b[i])<<8 | rune(b[i+1])
|
||||
if r = utf16.DecodeRune(r, r2); r == repl {
|
||||
return nil
|
||||
}
|
||||
} else if min <= r && r <= max {
|
||||
return nil
|
||||
}
|
||||
j += utf8.EncodeRune(s[j:], r)
|
||||
}
|
||||
return s[:j]
|
||||
}
|
||||
91
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
Normal file
91
vendor/github.com/emersion/go-imap/utf7/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
type encoder struct{}
|
||||
|
||||
func (e *encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for i := 0; i < len(src); {
|
||||
ch := src[i]
|
||||
|
||||
var b []byte
|
||||
if min <= ch && ch <= max {
|
||||
b = []byte{ch}
|
||||
if ch == '&' {
|
||||
b = append(b, '-')
|
||||
}
|
||||
|
||||
i++
|
||||
} else {
|
||||
start := i
|
||||
|
||||
// Find the next printable ASCII code point
|
||||
i++
|
||||
for i < len(src) && (src[i] < min || src[i] > max) {
|
||||
i++
|
||||
}
|
||||
|
||||
if !atEOF && i == len(src) {
|
||||
err = transform.ErrShortSrc
|
||||
return
|
||||
}
|
||||
|
||||
b = encode(src[start:i])
|
||||
}
|
||||
|
||||
if nDst+len(b) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
return
|
||||
}
|
||||
|
||||
nSrc = i
|
||||
|
||||
for _, ch := range b {
|
||||
dst[nDst] = ch
|
||||
nDst++
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *encoder) Reset() {}
|
||||
|
||||
// Converts string s from UTF-8 to UTF-16-BE, encodes the result as base64,
|
||||
// removes the padding, and adds UTF-7 shifts.
|
||||
func encode(s []byte) []byte {
|
||||
// len(s) is sufficient for UTF-8 to UTF-16 conversion if there are no
|
||||
// control code points (see table below).
|
||||
b := make([]byte, 0, len(s)+4)
|
||||
for len(s) > 0 {
|
||||
r, size := utf8.DecodeRune(s)
|
||||
if r > utf8.MaxRune {
|
||||
r, size = utf8.RuneError, 1 // Bug fix (issue 3785)
|
||||
}
|
||||
s = s[size:]
|
||||
if r1, r2 := utf16.EncodeRune(r); r1 != repl {
|
||||
b = append(b, byte(r1>>8), byte(r1))
|
||||
r = r2
|
||||
}
|
||||
b = append(b, byte(r>>8), byte(r))
|
||||
}
|
||||
|
||||
// Encode as base64
|
||||
n := b64Enc.EncodedLen(len(b)) + 2
|
||||
b64 := make([]byte, n)
|
||||
b64Enc.Encode(b64[1:], b)
|
||||
|
||||
// Strip padding
|
||||
n -= 2 - (len(b)+2)%3
|
||||
b64 = b64[:n]
|
||||
|
||||
// Add UTF-7 shifts
|
||||
b64[0] = '&'
|
||||
b64[n-1] = '-'
|
||||
return b64
|
||||
}
|
||||
34
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
Normal file
34
vendor/github.com/emersion/go-imap/utf7/utf7.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Package utf7 implements modified UTF-7 encoding defined in RFC 3501 section 5.1.3
|
||||
package utf7
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
)
|
||||
|
||||
const (
|
||||
min = 0x20 // Minimum self-representing UTF-7 value
|
||||
max = 0x7E // Maximum self-representing UTF-7 value
|
||||
|
||||
repl = '\uFFFD' // Unicode replacement code point
|
||||
)
|
||||
|
||||
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{}
|
||||
255
vendor/github.com/emersion/go-imap/write.go
generated
vendored
Normal file
255
vendor/github.com/emersion/go-imap/write.go
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
package imap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
type (
|
||||
// A raw string.
|
||||
RawString string
|
||||
)
|
||||
|
||||
type WriterTo interface {
|
||||
WriteTo(w *Writer) error
|
||||
}
|
||||
|
||||
func formatNumber(num uint32) string {
|
||||
return strconv.FormatUint(uint64(num), 10)
|
||||
}
|
||||
|
||||
// Convert a string list to a field list.
|
||||
func FormatStringList(list []string) (fields []interface{}) {
|
||||
fields = make([]interface{}, len(list))
|
||||
for i, v := range list {
|
||||
fields[i] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if a string is 8-bit clean.
|
||||
func isAscii(s string) bool {
|
||||
for _, c := range s {
|
||||
if c > unicode.MaxASCII || unicode.IsControl(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// An IMAP writer.
|
||||
type Writer struct {
|
||||
io.Writer
|
||||
|
||||
AllowAsyncLiterals bool
|
||||
|
||||
continues <-chan bool
|
||||
}
|
||||
|
||||
// Helper function to write a string to w.
|
||||
func (w *Writer) writeString(s string) error {
|
||||
_, err := io.WriteString(w.Writer, s)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Writer) writeCrlf() error {
|
||||
if err := w.writeString(crlf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func (w *Writer) writeNumber(num uint32) error {
|
||||
return w.writeString(formatNumber(num))
|
||||
}
|
||||
|
||||
func (w *Writer) writeQuoted(s string) error {
|
||||
return w.writeString(strconv.Quote(s))
|
||||
}
|
||||
|
||||
func (w *Writer) writeQuotedOrLiteral(s string) error {
|
||||
if !isAscii(s) {
|
||||
// IMAP doesn't allow 8-bit data outside literals
|
||||
return w.writeLiteral(bytes.NewBufferString(s))
|
||||
}
|
||||
|
||||
return w.writeQuoted(s)
|
||||
}
|
||||
|
||||
func (w *Writer) writeDateTime(t time.Time, layout string) error {
|
||||
if t.IsZero() {
|
||||
return w.writeString(nilAtom)
|
||||
}
|
||||
return w.writeQuoted(t.Format(layout))
|
||||
}
|
||||
|
||||
func (w *Writer) writeFields(fields []interface{}) error {
|
||||
for i, field := range fields {
|
||||
if i > 0 { // Write separator
|
||||
if err := w.writeString(string(sp)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.writeField(field); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) writeList(fields []interface{}) error {
|
||||
if err := w.writeString(string(listStart)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeFields(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.writeString(string(listEnd))
|
||||
}
|
||||
|
||||
// LiteralLengthErr is returned when the Len() of the Literal object does not
|
||||
// match the actual length of the byte stream.
|
||||
type LiteralLengthErr struct {
|
||||
Actual int
|
||||
Expected int
|
||||
}
|
||||
|
||||
func (e LiteralLengthErr) Error() string {
|
||||
return fmt.Sprintf("imap: size of Literal is not equal to Len() (%d != %d)", e.Expected, e.Actual)
|
||||
}
|
||||
|
||||
func (w *Writer) writeLiteral(l Literal) error {
|
||||
if l == nil {
|
||||
return w.writeString(nilAtom)
|
||||
}
|
||||
|
||||
unsyncLiteral := w.AllowAsyncLiterals && l.Len() <= 4096
|
||||
|
||||
header := string(literalStart) + strconv.Itoa(l.Len())
|
||||
if unsyncLiteral {
|
||||
header += string('+')
|
||||
}
|
||||
header += string(literalEnd) + crlf
|
||||
if err := w.writeString(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If a channel is available, wait for a continuation request before sending data
|
||||
if !unsyncLiteral && w.continues != nil {
|
||||
// Make sure to flush the writer, otherwise we may never receive a continuation request
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !<-w.continues {
|
||||
return fmt.Errorf("imap: cannot send literal: no continuation request received")
|
||||
}
|
||||
}
|
||||
|
||||
// In case of bufio.Buffer, it will be 0 after io.Copy.
|
||||
literalLen := int64(l.Len())
|
||||
|
||||
n, err := io.CopyN(w, l, literalLen)
|
||||
if err != nil {
|
||||
if err == io.EOF && n != literalLen {
|
||||
return LiteralLengthErr{int(n), l.Len()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
extra, _ := io.Copy(ioutil.Discard, l)
|
||||
if extra != 0 {
|
||||
return LiteralLengthErr{int(n + extra), l.Len()}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) writeField(field interface{}) error {
|
||||
if field == nil {
|
||||
return w.writeString(nilAtom)
|
||||
}
|
||||
|
||||
switch field := field.(type) {
|
||||
case RawString:
|
||||
return w.writeString(string(field))
|
||||
case string:
|
||||
return w.writeQuotedOrLiteral(field)
|
||||
case int:
|
||||
return w.writeNumber(uint32(field))
|
||||
case uint32:
|
||||
return w.writeNumber(field)
|
||||
case Literal:
|
||||
return w.writeLiteral(field)
|
||||
case []interface{}:
|
||||
return w.writeList(field)
|
||||
case envelopeDateTime:
|
||||
return w.writeDateTime(time.Time(field), envelopeDateTimeLayout)
|
||||
case searchDate:
|
||||
return w.writeDateTime(time.Time(field), searchDateLayout)
|
||||
case Date:
|
||||
return w.writeDateTime(time.Time(field), DateLayout)
|
||||
case DateTime:
|
||||
return w.writeDateTime(time.Time(field), DateTimeLayout)
|
||||
case time.Time:
|
||||
return w.writeDateTime(field, DateTimeLayout)
|
||||
case *SeqSet:
|
||||
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(string(field.FetchItem()))
|
||||
}
|
||||
|
||||
return fmt.Errorf("imap: cannot format field: %v", field)
|
||||
}
|
||||
|
||||
func (w *Writer) writeRespCode(code StatusRespCode, args []interface{}) error {
|
||||
if err := w.writeString(string(respCodeStart)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := []interface{}{RawString(code)}
|
||||
fields = append(fields, args...)
|
||||
|
||||
if err := w.writeFields(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.writeString(string(respCodeEnd))
|
||||
}
|
||||
|
||||
func (w *Writer) writeLine(fields ...interface{}) error {
|
||||
if err := w.writeFields(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.writeCrlf()
|
||||
}
|
||||
|
||||
func (w *Writer) Flush() error {
|
||||
if f, ok := w.Writer.(flusher); ok {
|
||||
return f.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{Writer: w}
|
||||
}
|
||||
|
||||
func NewClientWriter(w io.Writer, continues <-chan bool) *Writer {
|
||||
return &Writer{Writer: w, continues: continues}
|
||||
}
|
||||
19
vendor/github.com/emersion/go-sasl/.build.yml
generated
vendored
Normal file
19
vendor/github.com/emersion/go-sasl/.build.yml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
image: alpine/latest
|
||||
packages:
|
||||
- go
|
||||
# Required by codecov
|
||||
- bash
|
||||
- findutils
|
||||
sources:
|
||||
- https://github.com/emersion/go-sasl
|
||||
tasks:
|
||||
- build: |
|
||||
cd go-sasl
|
||||
go build -v ./...
|
||||
- test: |
|
||||
cd go-sasl
|
||||
go test -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- upload-coverage: |
|
||||
cd go-sasl
|
||||
export CODECOV_TOKEN=3f257f71-a128-4834-8f68-2b534e9f4cb1
|
||||
curl -s https://codecov.io/bash | bash
|
||||
24
vendor/github.com/emersion/go-sasl/.gitignore
generated
vendored
Normal file
24
vendor/github.com/emersion/go-sasl/.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
|
||||
21
vendor/github.com/emersion/go-sasl/LICENSE
generated
vendored
Normal file
21
vendor/github.com/emersion/go-sasl/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.
|
||||
17
vendor/github.com/emersion/go-sasl/README.md
generated
vendored
Normal file
17
vendor/github.com/emersion/go-sasl/README.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# go-sasl
|
||||
|
||||
[](https://godoc.org/github.com/emersion/go-sasl)
|
||||
[](https://travis-ci.org/emersion/go-sasl)
|
||||
|
||||
A [SASL](https://tools.ietf.org/html/rfc4422) library written in Go.
|
||||
|
||||
Implemented mechanisms:
|
||||
* [ANONYMOUS](https://tools.ietf.org/html/rfc4505)
|
||||
* [EXTERNAL](https://tools.ietf.org/html/rfc4422#appendix-A)
|
||||
* [LOGIN](https://tools.ietf.org/html/draft-murchison-sasl-login-00) (obsolete, use PLAIN instead)
|
||||
* [PLAIN](https://tools.ietf.org/html/rfc4616)
|
||||
* [OAUTHBEARER](https://tools.ietf.org/html/rfc7628)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
56
vendor/github.com/emersion/go-sasl/anonymous.go
generated
vendored
Normal file
56
vendor/github.com/emersion/go-sasl/anonymous.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package sasl
|
||||
|
||||
// The ANONYMOUS mechanism name.
|
||||
const Anonymous = "ANONYMOUS"
|
||||
|
||||
type anonymousClient struct {
|
||||
Trace string
|
||||
}
|
||||
|
||||
func (c *anonymousClient) Start() (mech string, ir []byte, err error) {
|
||||
mech = Anonymous
|
||||
ir = []byte(c.Trace)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *anonymousClient) Next(challenge []byte) (response []byte, err error) {
|
||||
return nil, ErrUnexpectedServerChallenge
|
||||
}
|
||||
|
||||
// A client implementation of the ANONYMOUS authentication mechanism, as
|
||||
// described in RFC 4505.
|
||||
func NewAnonymousClient(trace string) Client {
|
||||
return &anonymousClient{trace}
|
||||
}
|
||||
|
||||
// Get trace information from clients logging in anonymously.
|
||||
type AnonymousAuthenticator func(trace string) error
|
||||
|
||||
type anonymousServer struct {
|
||||
done bool
|
||||
authenticate AnonymousAuthenticator
|
||||
}
|
||||
|
||||
func (s *anonymousServer) Next(response []byte) (challenge []byte, done bool, err error) {
|
||||
if s.done {
|
||||
err = ErrUnexpectedClientResponse
|
||||
return
|
||||
}
|
||||
|
||||
// No initial response, send an empty challenge
|
||||
if response == nil {
|
||||
return []byte{}, false, nil
|
||||
}
|
||||
|
||||
s.done = true
|
||||
|
||||
err = s.authenticate(string(response))
|
||||
done = true
|
||||
return
|
||||
}
|
||||
|
||||
// A server implementation of the ANONYMOUS authentication mechanism, as
|
||||
// described in RFC 4505.
|
||||
func NewAnonymousServer(authenticator AnonymousAuthenticator) Server {
|
||||
return &anonymousServer{authenticate: authenticator}
|
||||
}
|
||||
26
vendor/github.com/emersion/go-sasl/external.go
generated
vendored
Normal file
26
vendor/github.com/emersion/go-sasl/external.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package sasl
|
||||
|
||||
// The EXTERNAL mechanism name.
|
||||
const External = "EXTERNAL"
|
||||
|
||||
type externalClient struct {
|
||||
Identity string
|
||||
}
|
||||
|
||||
func (a *externalClient) Start() (mech string, ir []byte, err error) {
|
||||
mech = External
|
||||
ir = []byte(a.Identity)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *externalClient) Next(challenge []byte) (response []byte, err error) {
|
||||
return nil, ErrUnexpectedServerChallenge
|
||||
}
|
||||
|
||||
// An implementation of the EXTERNAL authentication mechanism, as described in
|
||||
// RFC 4422. Authorization identity may be left blank to indicate that the
|
||||
// client is requesting to act as the identity associated with the
|
||||
// authentication credentials.
|
||||
func NewExternalClient(identity string) Client {
|
||||
return &externalClient{identity}
|
||||
}
|
||||
89
vendor/github.com/emersion/go-sasl/login.go
generated
vendored
Normal file
89
vendor/github.com/emersion/go-sasl/login.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// The LOGIN mechanism name.
|
||||
const Login = "LOGIN"
|
||||
|
||||
var expectedChallenge = []byte("Password:")
|
||||
|
||||
type loginClient struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (a *loginClient) Start() (mech string, ir []byte, err error) {
|
||||
mech = "LOGIN"
|
||||
ir = []byte(a.Username)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *loginClient) Next(challenge []byte) (response []byte, err error) {
|
||||
if bytes.Compare(challenge, expectedChallenge) != 0 {
|
||||
return nil, ErrUnexpectedServerChallenge
|
||||
} else {
|
||||
return []byte(a.Password), nil
|
||||
}
|
||||
}
|
||||
|
||||
// A client implementation of the LOGIN authentication mechanism for SMTP,
|
||||
// as described in http://www.iana.org/go/draft-murchison-sasl-login
|
||||
//
|
||||
// It is considered obsolete, and should not be used when other mechanisms are
|
||||
// available. For plaintext password authentication use PLAIN mechanism.
|
||||
func NewLoginClient(username, password string) Client {
|
||||
return &loginClient{username, password}
|
||||
}
|
||||
|
||||
// Authenticates users with an username and a password.
|
||||
type LoginAuthenticator func(username, password string) error
|
||||
|
||||
type loginState int
|
||||
|
||||
const (
|
||||
loginNotStarted loginState = iota
|
||||
loginWaitingUsername
|
||||
loginWaitingPassword
|
||||
)
|
||||
|
||||
type loginServer struct {
|
||||
state loginState
|
||||
username, password string
|
||||
authenticate LoginAuthenticator
|
||||
}
|
||||
|
||||
// A server implementation of the LOGIN authentication mechanism, as described
|
||||
// in https://tools.ietf.org/html/draft-murchison-sasl-login-00.
|
||||
//
|
||||
// LOGIN is obsolete and should only be enabled for legacy clients that cannot
|
||||
// be updated to use PLAIN.
|
||||
func NewLoginServer(authenticator LoginAuthenticator) Server {
|
||||
return &loginServer{authenticate: authenticator}
|
||||
}
|
||||
|
||||
func (a *loginServer) Next(response []byte) (challenge []byte, done bool, err error) {
|
||||
switch a.state {
|
||||
case loginNotStarted:
|
||||
// Check for initial response field, as per RFC4422 section 3
|
||||
if response == nil {
|
||||
challenge = []byte("Username:")
|
||||
break
|
||||
}
|
||||
a.state++
|
||||
fallthrough
|
||||
case loginWaitingUsername:
|
||||
a.username = string(response)
|
||||
challenge = []byte("Password:")
|
||||
case loginWaitingPassword:
|
||||
a.password = string(response)
|
||||
err = a.authenticate(a.username, a.password)
|
||||
done = true
|
||||
default:
|
||||
err = ErrUnexpectedClientResponse
|
||||
}
|
||||
|
||||
a.state++
|
||||
return
|
||||
}
|
||||
191
vendor/github.com/emersion/go-sasl/oauthbearer.go
generated
vendored
Normal file
191
vendor/github.com/emersion/go-sasl/oauthbearer.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The OAUTHBEARER mechanism name.
|
||||
const OAuthBearer = "OAUTHBEARER"
|
||||
|
||||
type OAuthBearerError struct {
|
||||
Status string `json:"status"`
|
||||
Schemes string `json:"schemes"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type OAuthBearerOptions struct {
|
||||
Username string
|
||||
Token string
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
// Implements error
|
||||
func (err *OAuthBearerError) Error() string {
|
||||
return fmt.Sprintf("OAUTHBEARER authentication error (%v)", err.Status)
|
||||
}
|
||||
|
||||
type oauthBearerClient struct {
|
||||
OAuthBearerOptions
|
||||
}
|
||||
|
||||
func (a *oauthBearerClient) Start() (mech string, ir []byte, err error) {
|
||||
mech = OAuthBearer
|
||||
var str = "n,a=" + a.Username + ","
|
||||
|
||||
if a.Host != "" {
|
||||
str += "\x01host=" + a.Host
|
||||
}
|
||||
|
||||
if a.Port != 0 {
|
||||
str += "\x01port=" + strconv.Itoa(a.Port)
|
||||
}
|
||||
str += "\x01auth=Bearer " + a.Token + "\x01\x01"
|
||||
ir = []byte(str)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *oauthBearerClient) Next(challenge []byte) ([]byte, error) {
|
||||
authBearerErr := &OAuthBearerError{}
|
||||
if err := json.Unmarshal(challenge, authBearerErr); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return nil, authBearerErr
|
||||
}
|
||||
}
|
||||
|
||||
// An implementation of the OAUTHBEARER authentication mechanism, as
|
||||
// described in RFC 7628.
|
||||
func NewOAuthBearerClient(opt *OAuthBearerOptions) Client {
|
||||
return &oauthBearerClient{*opt}
|
||||
}
|
||||
|
||||
type OAuthBearerAuthenticator func(opts OAuthBearerOptions) *OAuthBearerError
|
||||
|
||||
type oauthBearerServer struct {
|
||||
done bool
|
||||
failErr error
|
||||
authenticate OAuthBearerAuthenticator
|
||||
}
|
||||
|
||||
func (a *oauthBearerServer) fail(descr string) ([]byte, bool, error) {
|
||||
blob, err := json.Marshal(OAuthBearerError{
|
||||
Status: "invalid_request",
|
||||
Schemes: "bearer",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err) // wtf
|
||||
}
|
||||
a.failErr = errors.New(descr)
|
||||
return blob, false, nil
|
||||
}
|
||||
|
||||
func (a *oauthBearerServer) Next(response []byte) (challenge []byte, done bool, err error) {
|
||||
// Per RFC, we cannot just send an error, we need to return JSON-structured
|
||||
// value as a challenge and then after getting dummy response from the
|
||||
// client stop the exchange.
|
||||
if a.failErr != nil {
|
||||
// Server libraries (go-smtp, go-imap) will not call Next on
|
||||
// protocol-specific SASL cancel response ('*'). However, GS2 (and
|
||||
// indirectly OAUTHBEARER) defines a protocol-independent way to do so
|
||||
// using 0x01.
|
||||
if len(response) != 1 && response[0] != 0x01 {
|
||||
return nil, true, errors.New("unexpected response")
|
||||
}
|
||||
return nil, true, a.failErr
|
||||
}
|
||||
|
||||
if a.done {
|
||||
err = ErrUnexpectedClientResponse
|
||||
return
|
||||
}
|
||||
|
||||
// Generate empty challenge.
|
||||
if response == nil {
|
||||
return []byte{}, false, nil
|
||||
}
|
||||
|
||||
a.done = true
|
||||
|
||||
// Cut n,a=username,\x01host=...\x01auth=...
|
||||
// into
|
||||
// n
|
||||
// a=username
|
||||
// \x01host=...\x01auth=...\x01\x01
|
||||
parts := bytes.SplitN(response, []byte{','}, 3)
|
||||
if len(parts) != 3 {
|
||||
return a.fail("Invalid response")
|
||||
}
|
||||
if !bytes.Equal(parts[0], []byte{'n'}) {
|
||||
return a.fail("Invalid response, missing 'n'")
|
||||
}
|
||||
opts := OAuthBearerOptions{}
|
||||
if !bytes.HasPrefix(parts[1], []byte("a=")) {
|
||||
return a.fail("Invalid response, missing 'a'")
|
||||
}
|
||||
opts.Username = string(bytes.TrimPrefix(parts[1], []byte("a=")))
|
||||
|
||||
// Cut \x01host=...\x01auth=...\x01\x01
|
||||
// into
|
||||
// *empty*
|
||||
// host=...
|
||||
// auth=...
|
||||
// *empty*
|
||||
//
|
||||
// Note that this code does not do a lot of checks to make sure the input
|
||||
// follows the exact format specified by RFC.
|
||||
params := bytes.Split(parts[2], []byte{0x01})
|
||||
for _, p := range params {
|
||||
// Skip empty fields (one at start and end).
|
||||
if len(p) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
pParts := bytes.SplitN(p, []byte{'='}, 2)
|
||||
if len(pParts) != 2 {
|
||||
return a.fail("Invalid response, missing '='")
|
||||
}
|
||||
|
||||
switch string(pParts[0]) {
|
||||
case "host":
|
||||
opts.Host = string(pParts[1])
|
||||
case "port":
|
||||
port, err := strconv.ParseUint(string(pParts[1]), 10, 16)
|
||||
if err != nil {
|
||||
return a.fail("Invalid response, malformed 'port' value")
|
||||
}
|
||||
opts.Port = int(port)
|
||||
case "auth":
|
||||
const prefix = "bearer "
|
||||
strValue := string(pParts[1])
|
||||
// Token type is case-insensitive.
|
||||
if !strings.HasPrefix(strings.ToLower(strValue), prefix) {
|
||||
return a.fail("Unsupported token type")
|
||||
}
|
||||
opts.Token = strValue[len(prefix):]
|
||||
default:
|
||||
return a.fail("Invalid response, unknown parameter: " + string(pParts[0]))
|
||||
}
|
||||
}
|
||||
|
||||
authzErr := a.authenticate(opts)
|
||||
if authzErr != nil {
|
||||
blob, err := json.Marshal(authzErr)
|
||||
if err != nil {
|
||||
panic(err) // wtf
|
||||
}
|
||||
a.failErr = authzErr
|
||||
return blob, false, nil
|
||||
}
|
||||
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
func NewOAuthBearerServer(auth OAuthBearerAuthenticator) Server {
|
||||
return &oauthBearerServer{authenticate: auth}
|
||||
}
|
||||
77
vendor/github.com/emersion/go-sasl/plain.go
generated
vendored
Normal file
77
vendor/github.com/emersion/go-sasl/plain.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package sasl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// The PLAIN mechanism name.
|
||||
const Plain = "PLAIN"
|
||||
|
||||
type plainClient struct {
|
||||
Identity string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (a *plainClient) Start() (mech string, ir []byte, err error) {
|
||||
mech = "PLAIN"
|
||||
ir = []byte(a.Identity + "\x00" + a.Username + "\x00" + a.Password)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *plainClient) Next(challenge []byte) (response []byte, err error) {
|
||||
return nil, ErrUnexpectedServerChallenge
|
||||
}
|
||||
|
||||
// A client implementation of the PLAIN authentication mechanism, as described
|
||||
// in RFC 4616. Authorization identity may be left blank to indicate that it is
|
||||
// the same as the username.
|
||||
func NewPlainClient(identity, username, password string) Client {
|
||||
return &plainClient{identity, username, password}
|
||||
}
|
||||
|
||||
// Authenticates users with an identity, a username and a password. If the
|
||||
// identity is left blank, it indicates that it is the same as the username.
|
||||
// If identity is not empty and the server doesn't support it, an error must be
|
||||
// returned.
|
||||
type PlainAuthenticator func(identity, username, password string) error
|
||||
|
||||
type plainServer struct {
|
||||
done bool
|
||||
authenticate PlainAuthenticator
|
||||
}
|
||||
|
||||
func (a *plainServer) Next(response []byte) (challenge []byte, done bool, err error) {
|
||||
if a.done {
|
||||
err = ErrUnexpectedClientResponse
|
||||
return
|
||||
}
|
||||
|
||||
// No initial response, send an empty challenge
|
||||
if response == nil {
|
||||
return []byte{}, false, nil
|
||||
}
|
||||
|
||||
a.done = true
|
||||
|
||||
parts := bytes.Split(response, []byte("\x00"))
|
||||
if len(parts) != 3 {
|
||||
err = errors.New("Invalid response")
|
||||
return
|
||||
}
|
||||
|
||||
identity := string(parts[0])
|
||||
username := string(parts[1])
|
||||
password := string(parts[2])
|
||||
|
||||
err = a.authenticate(identity, username, password)
|
||||
done = true
|
||||
return
|
||||
}
|
||||
|
||||
// A server implementation of the PLAIN authentication mechanism, as described
|
||||
// in RFC 4616.
|
||||
func NewPlainServer(authenticator PlainAuthenticator) Server {
|
||||
return &plainServer{authenticate: authenticator}
|
||||
}
|
||||
45
vendor/github.com/emersion/go-sasl/sasl.go
generated
vendored
Normal file
45
vendor/github.com/emersion/go-sasl/sasl.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Library for Simple Authentication and Security Layer (SASL) defined in RFC 4422.
|
||||
package sasl
|
||||
|
||||
// Note:
|
||||
// Most of this code was copied, with some modifications, from net/smtp. It
|
||||
// would be better if Go provided a standard package (e.g. crypto/sasl) that
|
||||
// could be shared by SMTP, IMAP, and other packages.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Common SASL errors.
|
||||
var (
|
||||
ErrUnexpectedClientResponse = errors.New("sasl: unexpected client response")
|
||||
ErrUnexpectedServerChallenge = errors.New("sasl: unexpected server challenge")
|
||||
)
|
||||
|
||||
// Client interface to perform challenge-response authentication.
|
||||
type Client interface {
|
||||
// Begins SASL authentication with the server. It returns the
|
||||
// authentication mechanism name and "initial response" data (if required by
|
||||
// the selected mechanism). A non-nil error causes the client to abort the
|
||||
// authentication attempt.
|
||||
//
|
||||
// A nil ir value is different from a zero-length value. The nil value
|
||||
// indicates that the selected mechanism does not use an initial response,
|
||||
// while a zero-length value indicates an empty initial response, which must
|
||||
// be sent to the server.
|
||||
Start() (mech string, ir []byte, err error)
|
||||
|
||||
// Continues challenge-response authentication. A non-nil error causes
|
||||
// the client to abort the authentication attempt.
|
||||
Next(challenge []byte) (response []byte, err error)
|
||||
}
|
||||
|
||||
// Server interface to perform challenge-response authentication.
|
||||
type Server interface {
|
||||
// Begins or continues challenge-response authentication. If the client
|
||||
// supplies an initial response, response is non-nil.
|
||||
//
|
||||
// If the authentication is finished, done is set to true. If the
|
||||
// authentication has failed, an error is returned.
|
||||
Next(response []byte) (challenge []byte, done bool, err error)
|
||||
}
|
||||
115
vendor/github.com/taknb2nch/go-pop3/README.md
generated
vendored
115
vendor/github.com/taknb2nch/go-pop3/README.md
generated
vendored
@@ -1,115 +0,0 @@
|
||||
go-pop3
|
||||
==========
|
||||
|
||||
This is a simple POP3 client package in Go language.
|
||||
|
||||
##Usage
|
||||
```go
|
||||
if err := pop3.ReceiveMail(address, user, pass,
|
||||
func(number int, uid, data string, err error) (bool, error) {
|
||||
log.Printf("%d, %s\n", number, uid)
|
||||
|
||||
// implement your own logic here
|
||||
|
||||
return false, nil
|
||||
}); err != nil {
|
||||
log.Fatalf("%v\n", err)
|
||||
}
|
||||
```
|
||||
or use the method that implements the command
|
||||
```go
|
||||
client, err := pop3.Dial(address)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error: %v\n", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
client.Quit()
|
||||
client.Close()
|
||||
}()
|
||||
|
||||
if err = client.User(user); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = client.Pass(pass); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var count int
|
||||
var size uint64
|
||||
|
||||
if count, size, err = client.Stat(); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Count: %d, Size: %d\n", count, size)
|
||||
|
||||
if count, size, err = client.List(6); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Number: %d, Size: %d\n", count, size)
|
||||
|
||||
var mis []pop3.MessageInfo
|
||||
|
||||
if mis, err = client.ListAll(); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, mi := range mis {
|
||||
log.Printf("Number: %d, Size: %d\n", mi.Number, mi.Size)
|
||||
}
|
||||
|
||||
var number int
|
||||
var uid string
|
||||
|
||||
if number, uid, err = client.Uidl(6); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Number: %d, Uid: %s\n", number, uid)
|
||||
|
||||
if mis, err = client.UidlAll(); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, mi := range mis {
|
||||
log.Printf("Number: %d, Uid: %s\n", mi.Number, mi.Uid)
|
||||
}
|
||||
|
||||
var content string
|
||||
|
||||
if content, err = client.Retr(8); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Content:\n%s\n", content)
|
||||
|
||||
if err = client.Dele(6); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = client.Noop(); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = client.Rset(); err != nil {
|
||||
log.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
##License
|
||||
[MIT License](https://github.com/taknb2nch/go-pop3/blob/master/LICENSE)
|
||||
400
vendor/github.com/taknb2nch/go-pop3/pop3.go
generated
vendored
400
vendor/github.com/taknb2nch/go-pop3/pop3.go
generated
vendored
@@ -1,400 +0,0 @@
|
||||
// Package pop3 provides simple POP3 client.
|
||||
package pop3
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
EOF = errors.New("skip the all mail remaining")
|
||||
)
|
||||
|
||||
// MessageInfo has Number, Size, and Uid fields,
|
||||
// and used as a return value of ListAll and UidlAll.
|
||||
// When used as the return value of the method ListAll,
|
||||
// MessageInfo contain only the Number and Size values.
|
||||
// When used as the return value of the method UidlAll,
|
||||
// MessageInfo contain only the Number and Uid values.
|
||||
type MessageInfo struct {
|
||||
Number int
|
||||
Size uint64
|
||||
Uid string
|
||||
}
|
||||
|
||||
// A Client represents a client connection to an POP server.
|
||||
type Client struct {
|
||||
// Text is the pop3.Conn used by the Client.
|
||||
Text *Conn
|
||||
// keep a reference to the connection so it can be used to create a TLS
|
||||
// connection later
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// Dial returns a new Client connected to an POP server at addr.
|
||||
// The addr must include a port number.
|
||||
func Dial(addr string) (*Client, error) {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewClient(conn)
|
||||
}
|
||||
|
||||
// NewClient returns a new Client using an existing connection.
|
||||
func NewClient(conn net.Conn) (*Client, error) {
|
||||
text := NewConn(conn)
|
||||
|
||||
_, err := text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{Text: text, conn: conn}, nil
|
||||
}
|
||||
|
||||
// User issues a USER command to the server using the provided user name.
|
||||
func (c *Client) User(user string) error {
|
||||
return c.cmdSimple("USER %s", user)
|
||||
}
|
||||
|
||||
// Pass issues a PASS command to the server using the provided password.
|
||||
func (c *Client) Pass(pass string) error {
|
||||
return c.cmdSimple("PASS %s", pass)
|
||||
}
|
||||
|
||||
// Stat issues a STAT command to the server
|
||||
// and returns mail count and total size.
|
||||
func (c *Client) Stat() (int, uint64, error) {
|
||||
return c.cmdStatOrList("STAT", "STAT")
|
||||
}
|
||||
|
||||
// Retr issues a RETR command to the server using the provided mail number
|
||||
// and returns mail data.
|
||||
func (c *Client) Retr(number int) (string, error) {
|
||||
var err error
|
||||
|
||||
err = c.Text.WriteLine("RETR %d", number)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = c.Text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return c.Text.ReadToPeriod()
|
||||
}
|
||||
|
||||
// List issues a LIST command to the server using the provided mail number
|
||||
// and returns mail number and size.
|
||||
func (c *Client) List(number int) (int, uint64, error) {
|
||||
return c.cmdStatOrList("LIST", "LIST %d", number)
|
||||
}
|
||||
|
||||
// List issues a LIST command to the server
|
||||
// and returns array of MessageInfo.
|
||||
func (c *Client) ListAll() ([]MessageInfo, error) {
|
||||
list := make([]MessageInfo, 0)
|
||||
|
||||
err := c.cmdReadLines("LIST", func(line string) error {
|
||||
number, size, err := c.convertNumberAndSize(line)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list = append(list, MessageInfo{Number: number, Size: size})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Uidl issues a UIDL command to the server using the provided mail number
|
||||
// and returns mail number and unique id.
|
||||
func (c *Client) Uidl(number int) (int, string, error) {
|
||||
var err error
|
||||
|
||||
err = c.Text.WriteLine("UIDL %d", number)
|
||||
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
var msg string
|
||||
|
||||
msg, err = c.Text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
var val int
|
||||
var uid string
|
||||
|
||||
val, uid, err = c.convertNumberAndUid(msg)
|
||||
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
return val, uid, nil
|
||||
}
|
||||
|
||||
// Uidl issues a UIDL command to the server
|
||||
// and returns array of MessageInfo.
|
||||
func (c *Client) UidlAll() ([]MessageInfo, error) {
|
||||
list := make([]MessageInfo, 0)
|
||||
|
||||
err := c.cmdReadLines("UIDL", func(line string) error {
|
||||
number, uid, err := c.convertNumberAndUid(line)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list = append(list, MessageInfo{Number: number, Uid: uid})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Dele issues a DELE command to the server using the provided mail number.
|
||||
func (c *Client) Dele(number int) error {
|
||||
return c.cmdSimple("DELE %d", number)
|
||||
}
|
||||
|
||||
// Noop issues a NOOP command to the server.
|
||||
func (c *Client) Noop() error {
|
||||
return c.cmdSimple("NOOP")
|
||||
}
|
||||
|
||||
// Rset issues a RSET command to the server.
|
||||
func (c *Client) Rset() error {
|
||||
return c.cmdSimple("RSET")
|
||||
}
|
||||
|
||||
// Quit issues a QUIT command to the server.
|
||||
func (c *Client) Quit() error {
|
||||
return c.cmdSimple("QUIT")
|
||||
}
|
||||
|
||||
// ReceiveMail connects to the server at addr,
|
||||
// and authenticates with user and pass,
|
||||
// and calling receiveFn for each mail.
|
||||
func ReceiveMail(addr, user, pass string, receiveFn ReceiveMailFunc) error {
|
||||
c, err := Dial(addr)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil && err != EOF {
|
||||
c.Rset()
|
||||
}
|
||||
|
||||
c.Quit()
|
||||
c.Close()
|
||||
}()
|
||||
|
||||
if err = c.User(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.Pass(pass); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mis []MessageInfo
|
||||
|
||||
if mis, err = c.UidlAll(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, mi := range mis {
|
||||
var data string
|
||||
|
||||
data, err = c.Retr(mi.Number)
|
||||
|
||||
del, err := receiveFn(mi.Number, mi.Uid, data, err)
|
||||
|
||||
if err != nil && err != EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if del {
|
||||
if err = c.Dele(mi.Number); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err == EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReceiveMailFunc is the type of the function called for each mail.
|
||||
// Its arguments are mail's number, uid, data, and mail receiving error.
|
||||
// if this function returns false value, the mail will be deleted,
|
||||
// if its returns EOF, skip the all mail of remaining.
|
||||
// (after deleting mail, if necessary)
|
||||
type ReceiveMailFunc func(number int, uid, data string, err error) (bool, error)
|
||||
|
||||
func (c *Client) cmdSimple(format string, args ...interface{}) error {
|
||||
var err error
|
||||
|
||||
err = c.Text.WriteLine(format, args...)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.Text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) cmdStatOrList(name, format string, args ...interface{}) (int, uint64, error) {
|
||||
var err error
|
||||
|
||||
err = c.Text.WriteLine(format, args...)
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
var msg string
|
||||
|
||||
msg, err = c.Text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
s := strings.Split(msg, " ")
|
||||
|
||||
if len(s) < 2 {
|
||||
return 0, 0, ResponseError(fmt.Sprintf("invalid response format: %s", msg))
|
||||
}
|
||||
|
||||
var val int
|
||||
var size uint64
|
||||
|
||||
val, size, err = c.convertNumberAndSize(msg)
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return val, size, nil
|
||||
}
|
||||
|
||||
func (c *Client) cmdReadLines(cmnd string, lineFn lineFunc) error {
|
||||
var err error
|
||||
|
||||
err = c.Text.WriteLine(cmnd)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.Text.ReadResponse()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lines []string
|
||||
|
||||
lines, err = c.Text.ReadLines()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
err = lineFn(line)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type lineFunc func(line string) error
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return c.Text.Close()
|
||||
}
|
||||
|
||||
func (c *Client) convertNumberAndSize(line string) (int, uint64, error) {
|
||||
var err error
|
||||
|
||||
s := strings.Split(line, " ")
|
||||
|
||||
if len(s) < 2 {
|
||||
return 0, 0, errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
|
||||
}
|
||||
|
||||
var val int
|
||||
var size uint64
|
||||
|
||||
if val, err = strconv.Atoi(s[0]); err != nil {
|
||||
return 0, 0, errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
|
||||
}
|
||||
|
||||
if size, err = strconv.ParseUint(s[1], 10, 64); err != nil {
|
||||
return 0, 0, errors.New(fmt.Sprintf("can not convert element[1] to uint64 type: %s", line))
|
||||
}
|
||||
|
||||
return val, size, nil
|
||||
}
|
||||
|
||||
func (c *Client) convertNumberAndUid(line string) (int, string, error) {
|
||||
var err error
|
||||
|
||||
s := strings.Split(line, " ")
|
||||
|
||||
if len(s) < 2 {
|
||||
return 0, "", errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
|
||||
}
|
||||
|
||||
var val int
|
||||
|
||||
if val, err = strconv.Atoi(s[0]); err != nil {
|
||||
return 0, "", errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
|
||||
}
|
||||
|
||||
return val, s[1], nil
|
||||
}
|
||||
175
vendor/github.com/taknb2nch/go-pop3/pop3proto.go
generated
vendored
175
vendor/github.com/taknb2nch/go-pop3/pop3proto.go
generated
vendored
@@ -1,175 +0,0 @@
|
||||
package pop3
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
_ "log"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A ResponseError describes a protocol violation such as an invalid response or a hung-up connection.
|
||||
type ResponseError string
|
||||
|
||||
func (r ResponseError) Error() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// A Conn represents a textual network protocol connection for POP3.
|
||||
type Conn struct {
|
||||
Reader
|
||||
Writer
|
||||
conn io.ReadWriteCloser
|
||||
}
|
||||
|
||||
// NewConn returns a new Conn using conn for I/O.
|
||||
func NewConn(conn io.ReadWriteCloser) *Conn {
|
||||
return &Conn{
|
||||
Reader: Reader{R: textproto.NewReader(bufio.NewReader(conn))},
|
||||
Writer: Writer{W: bufio.NewWriter(conn)},
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *Conn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// A Reader implements convenience methods
|
||||
// for reading requests or responses from a text protocol network connection.
|
||||
type Reader struct {
|
||||
R *textproto.Reader
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader reading from r.
|
||||
func NewReader(r *bufio.Reader) *Reader {
|
||||
return &Reader{R: textproto.NewReader(r)}
|
||||
}
|
||||
|
||||
// ReadLine reads a single line from r,
|
||||
// eliding the final \n or \r\n from the returned string.
|
||||
// This calls textproto.Reader.ReadLine simply.
|
||||
func (r *Reader) ReadLine() (string, error) {
|
||||
return r.R.ReadLine()
|
||||
// for debug
|
||||
// l, err := r.R.ReadLine()
|
||||
// log.Printf("> %s\n", l)
|
||||
// return l, err
|
||||
}
|
||||
|
||||
// ReadLines reads a multiline until the last line of the only period,
|
||||
// and returns a each line at slice.
|
||||
// it does not contain last period.
|
||||
func (r *Reader) ReadLines() ([]string, error) {
|
||||
var lines []string
|
||||
var line string
|
||||
var err error
|
||||
|
||||
for {
|
||||
line, err = r.R.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if line == "." {
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadToPeriod reads a multiline until the last line of the only period,
|
||||
// and returns as a string.
|
||||
// it does not contain last period.
|
||||
func (r *Reader) ReadToPeriod() (string, error) {
|
||||
lines, err := r.ReadLines()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\r\n"), nil
|
||||
}
|
||||
|
||||
// ReadResponse reads a single line from r,
|
||||
// and parses reponse.
|
||||
// if the response is -ERR or has some other errors,
|
||||
// it returns error.
|
||||
func (r *Reader) ReadResponse() (string, error) {
|
||||
line, err := r.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return r.parseResponse(line)
|
||||
}
|
||||
|
||||
func (r *Reader) parseResponse(line string) (string, error) {
|
||||
s := strings.ToUpper(line)
|
||||
|
||||
if s == "+OK" {
|
||||
return "", nil
|
||||
} else if strings.HasPrefix(s, "+OK ") {
|
||||
return line[4:], nil
|
||||
} else if s == "-ERR" {
|
||||
return "", ResponseError("")
|
||||
} else if strings.HasPrefix(s, "-ERR ") {
|
||||
return "", ResponseError(line[5:])
|
||||
} else {
|
||||
return "", ResponseError(fmt.Sprintf("unknown response: %s", line))
|
||||
}
|
||||
}
|
||||
|
||||
var crnl = []byte{'\r', '\n'}
|
||||
|
||||
// A Writer implements convenience methods
|
||||
// for writing requests or responses to a text protocol network connection.
|
||||
type Writer struct {
|
||||
W *bufio.Writer
|
||||
}
|
||||
|
||||
// NewWriter returns a new Writer writing to w.
|
||||
func NewWriter(w *bufio.Writer) *Writer {
|
||||
return &Writer{W: w}
|
||||
}
|
||||
|
||||
// WriteLine writes the formatted output followed by \r\n.
|
||||
func (w *Writer) WriteLine(format string, args ...interface{}) error {
|
||||
var err error
|
||||
|
||||
if _, err = fmt.Fprintf(w.W, format, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = w.W.Write(crnl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.W.Flush()
|
||||
|
||||
// for debug
|
||||
// var err error
|
||||
|
||||
// l := fmt.Sprintf(format, args...)
|
||||
|
||||
// if _, err = fmt.Fprint(w.W, l); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if _, err = w.W.Write(crnl); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if err = w.W.Flush(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// log.Printf("< %s\n", l)
|
||||
|
||||
// return nil
|
||||
}
|
||||
526
vendor/golang.org/x/crypto/cast5/cast5.go
generated
vendored
526
vendor/golang.org/x/crypto/cast5/cast5.go
generated
vendored
@@ -1,526 +0,0 @@
|
||||
// Copyright 2010 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 cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common
|
||||
// OpenPGP cipher.
|
||||
package cast5 // import "golang.org/x/crypto/cast5"
|
||||
|
||||
import "errors"
|
||||
|
||||
const BlockSize = 8
|
||||
const KeySize = 16
|
||||
|
||||
type Cipher struct {
|
||||
masking [16]uint32
|
||||
rotate [16]uint8
|
||||
}
|
||||
|
||||
func NewCipher(key []byte) (c *Cipher, err error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("CAST5: keys must be 16 bytes")
|
||||
}
|
||||
|
||||
c = new(Cipher)
|
||||
c.keySchedule(key)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cipher) BlockSize() int {
|
||||
return BlockSize
|
||||
}
|
||||
|
||||
func (c *Cipher) Encrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||
|
||||
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||
|
||||
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||
|
||||
dst[0] = uint8(r >> 24)
|
||||
dst[1] = uint8(r >> 16)
|
||||
dst[2] = uint8(r >> 8)
|
||||
dst[3] = uint8(r)
|
||||
dst[4] = uint8(l >> 24)
|
||||
dst[5] = uint8(l >> 16)
|
||||
dst[6] = uint8(l >> 8)
|
||||
dst[7] = uint8(l)
|
||||
}
|
||||
|
||||
func (c *Cipher) Decrypt(dst, src []byte) {
|
||||
l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
|
||||
r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[15], c.rotate[15])
|
||||
l, r = r, l^f3(r, c.masking[14], c.rotate[14])
|
||||
l, r = r, l^f2(r, c.masking[13], c.rotate[13])
|
||||
l, r = r, l^f1(r, c.masking[12], c.rotate[12])
|
||||
|
||||
l, r = r, l^f3(r, c.masking[11], c.rotate[11])
|
||||
l, r = r, l^f2(r, c.masking[10], c.rotate[10])
|
||||
l, r = r, l^f1(r, c.masking[9], c.rotate[9])
|
||||
l, r = r, l^f3(r, c.masking[8], c.rotate[8])
|
||||
|
||||
l, r = r, l^f2(r, c.masking[7], c.rotate[7])
|
||||
l, r = r, l^f1(r, c.masking[6], c.rotate[6])
|
||||
l, r = r, l^f3(r, c.masking[5], c.rotate[5])
|
||||
l, r = r, l^f2(r, c.masking[4], c.rotate[4])
|
||||
|
||||
l, r = r, l^f1(r, c.masking[3], c.rotate[3])
|
||||
l, r = r, l^f3(r, c.masking[2], c.rotate[2])
|
||||
l, r = r, l^f2(r, c.masking[1], c.rotate[1])
|
||||
l, r = r, l^f1(r, c.masking[0], c.rotate[0])
|
||||
|
||||
dst[0] = uint8(r >> 24)
|
||||
dst[1] = uint8(r >> 16)
|
||||
dst[2] = uint8(r >> 8)
|
||||
dst[3] = uint8(r)
|
||||
dst[4] = uint8(l >> 24)
|
||||
dst[5] = uint8(l >> 16)
|
||||
dst[6] = uint8(l >> 8)
|
||||
dst[7] = uint8(l)
|
||||
}
|
||||
|
||||
type keyScheduleA [4][7]uint8
|
||||
type keyScheduleB [4][5]uint8
|
||||
|
||||
// keyScheduleRound contains the magic values for a round of the key schedule.
|
||||
// The keyScheduleA deals with the lines like:
|
||||
// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]
|
||||
// Conceptually, both x and z are in the same array, x first. The first
|
||||
// element describes which word of this array gets written to and the
|
||||
// second, which word gets read. So, for the line above, it's "4, 0", because
|
||||
// it's writing to the first word of z, which, being after x, is word 4, and
|
||||
// reading from the first word of x: word 0.
|
||||
//
|
||||
// Next are the indexes into the S-boxes. Now the array is treated as bytes. So
|
||||
// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear
|
||||
// that it's z that we're indexing.
|
||||
//
|
||||
// keyScheduleB deals with lines like:
|
||||
// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]
|
||||
// "K1" is ignored because key words are always written in order. So the five
|
||||
// elements are the S-box indexes. They use the same form as in keyScheduleA,
|
||||
// above.
|
||||
|
||||
type keyScheduleRound struct{}
|
||||
type keySchedule []keyScheduleRound
|
||||
|
||||
var schedule = []struct {
|
||||
a keyScheduleA
|
||||
b keyScheduleB
|
||||
}{
|
||||
{
|
||||
keyScheduleA{
|
||||
{4, 0, 0xd, 0xf, 0xc, 0xe, 0x8},
|
||||
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||
},
|
||||
keyScheduleB{
|
||||
{16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2},
|
||||
{16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6},
|
||||
{16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9},
|
||||
{16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||
},
|
||||
keyScheduleB{
|
||||
{3, 2, 0xc, 0xd, 8},
|
||||
{1, 0, 0xe, 0xf, 0xd},
|
||||
{7, 6, 8, 9, 3},
|
||||
{5, 4, 0xa, 0xb, 7},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{4, 0, 0xd, 0xf, 0xc, 0xe, 8},
|
||||
{5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa},
|
||||
{6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9},
|
||||
{7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb},
|
||||
},
|
||||
keyScheduleB{
|
||||
{16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9},
|
||||
{16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc},
|
||||
{16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2},
|
||||
{16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6},
|
||||
},
|
||||
},
|
||||
{
|
||||
keyScheduleA{
|
||||
{0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0},
|
||||
{1, 4, 0, 2, 1, 3, 16 + 2},
|
||||
{2, 5, 7, 6, 5, 4, 16 + 1},
|
||||
{3, 7, 0xa, 9, 0xb, 8, 16 + 3},
|
||||
},
|
||||
keyScheduleB{
|
||||
{8, 9, 7, 6, 3},
|
||||
{0xa, 0xb, 5, 4, 7},
|
||||
{0xc, 0xd, 3, 2, 8},
|
||||
{0xe, 0xf, 1, 0, 0xd},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (c *Cipher) keySchedule(in []byte) {
|
||||
var t [8]uint32
|
||||
var k [32]uint32
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
j := i * 4
|
||||
t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3])
|
||||
}
|
||||
|
||||
x := []byte{6, 7, 4, 5}
|
||||
ki := 0
|
||||
|
||||
for half := 0; half < 2; half++ {
|
||||
for _, round := range schedule {
|
||||
for j := 0; j < 4; j++ {
|
||||
var a [7]uint8
|
||||
copy(a[:], round.a[j][:])
|
||||
w := t[a[1]]
|
||||
w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff]
|
||||
w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff]
|
||||
w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff]
|
||||
w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff]
|
||||
w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff]
|
||||
t[a[0]] = w
|
||||
}
|
||||
|
||||
for j := 0; j < 4; j++ {
|
||||
var b [5]uint8
|
||||
copy(b[:], round.b[j][:])
|
||||
w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff]
|
||||
w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff]
|
||||
w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff]
|
||||
w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff]
|
||||
w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff]
|
||||
k[ki] = w
|
||||
ki++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
c.masking[i] = k[i]
|
||||
c.rotate[i] = uint8(k[16+i] & 0x1f)
|
||||
}
|
||||
}
|
||||
|
||||
// These are the three 'f' functions. See RFC 2144, section 2.2.
|
||||
func f1(d, m uint32, r uint8) uint32 {
|
||||
t := m + d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f2(d, m uint32, r uint8) uint32 {
|
||||
t := m ^ d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f3(d, m uint32, r uint8) uint32 {
|
||||
t := m - d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
var sBox = [8][256]uint32{
|
||||
{
|
||||
0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949,
|
||||
0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
|
||||
0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
|
||||
0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0,
|
||||
0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7,
|
||||
0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
|
||||
0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d,
|
||||
0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
|
||||
0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
|
||||
0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3,
|
||||
0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167,
|
||||
0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
|
||||
0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779,
|
||||
0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2,
|
||||
0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
|
||||
0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d,
|
||||
0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5,
|
||||
0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
|
||||
0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
|
||||
0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,
|
||||
0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
|
||||
0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96,
|
||||
0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,
|
||||
0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
|
||||
0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
|
||||
0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,
|
||||
0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
|
||||
0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872,
|
||||
0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,
|
||||
0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
|
||||
0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9,
|
||||
0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf,
|
||||
},
|
||||
{
|
||||
0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651,
|
||||
0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
|
||||
0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
|
||||
0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
|
||||
0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b,
|
||||
0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
|
||||
0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b,
|
||||
0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c,
|
||||
0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
|
||||
0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb,
|
||||
0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd,
|
||||
0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
|
||||
0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b,
|
||||
0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
|
||||
0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
|
||||
0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
|
||||
0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c,
|
||||
0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
|
||||
0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f,
|
||||
0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
|
||||
0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
|
||||
0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58,
|
||||
0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
|
||||
0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
|
||||
0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6,
|
||||
0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
|
||||
0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
|
||||
0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f,
|
||||
0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
|
||||
0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
|
||||
0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9,
|
||||
0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1,
|
||||
},
|
||||
{
|
||||
0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90,
|
||||
0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5,
|
||||
0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
|
||||
0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240,
|
||||
0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
|
||||
0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
|
||||
0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71,
|
||||
0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
|
||||
0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
|
||||
0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15,
|
||||
0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2,
|
||||
0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
|
||||
0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148,
|
||||
0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc,
|
||||
0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
|
||||
0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e,
|
||||
0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
|
||||
0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
|
||||
0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a,
|
||||
0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b,
|
||||
0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
|
||||
0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
|
||||
0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
|
||||
0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
|
||||
0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
|
||||
0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
|
||||
0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
|
||||
0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
|
||||
0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49,
|
||||
0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
|
||||
0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a,
|
||||
0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783,
|
||||
},
|
||||
{
|
||||
0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1,
|
||||
0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf,
|
||||
0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
|
||||
0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121,
|
||||
0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25,
|
||||
0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
|
||||
0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb,
|
||||
0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5,
|
||||
0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
|
||||
0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6,
|
||||
0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23,
|
||||
0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
|
||||
0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
|
||||
0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119,
|
||||
0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
|
||||
0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a,
|
||||
0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79,
|
||||
0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
|
||||
0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
|
||||
0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab,
|
||||
0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
|
||||
0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
|
||||
0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2,
|
||||
0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
|
||||
0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
|
||||
0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919,
|
||||
0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
|
||||
0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
|
||||
0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab,
|
||||
0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
|
||||
0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282,
|
||||
0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2,
|
||||
},
|
||||
{
|
||||
0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f,
|
||||
0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a,
|
||||
0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
|
||||
0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02,
|
||||
0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a,
|
||||
0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
|
||||
0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9,
|
||||
0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981,
|
||||
0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
|
||||
0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655,
|
||||
0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2,
|
||||
0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
|
||||
0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1,
|
||||
0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da,
|
||||
0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
|
||||
0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f,
|
||||
0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba,
|
||||
0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
|
||||
0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3,
|
||||
0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840,
|
||||
0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
|
||||
0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2,
|
||||
0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7,
|
||||
0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
|
||||
0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e,
|
||||
0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e,
|
||||
0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
|
||||
0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad,
|
||||
0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0,
|
||||
0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
|
||||
0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8,
|
||||
0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4,
|
||||
},
|
||||
{
|
||||
0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac,
|
||||
0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138,
|
||||
0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
|
||||
0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98,
|
||||
0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072,
|
||||
0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
|
||||
0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd,
|
||||
0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8,
|
||||
0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
|
||||
0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54,
|
||||
0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387,
|
||||
0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
|
||||
0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf,
|
||||
0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf,
|
||||
0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
|
||||
0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289,
|
||||
0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950,
|
||||
0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
|
||||
0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b,
|
||||
0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be,
|
||||
0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
|
||||
0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976,
|
||||
0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0,
|
||||
0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
|
||||
0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da,
|
||||
0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc,
|
||||
0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
|
||||
0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25,
|
||||
0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121,
|
||||
0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
|
||||
0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd,
|
||||
0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f,
|
||||
},
|
||||
{
|
||||
0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f,
|
||||
0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de,
|
||||
0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
|
||||
0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19,
|
||||
0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2,
|
||||
0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
|
||||
0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88,
|
||||
0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816,
|
||||
0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
|
||||
0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a,
|
||||
0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264,
|
||||
0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
|
||||
0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28,
|
||||
0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3,
|
||||
0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
|
||||
0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06,
|
||||
0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033,
|
||||
0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
|
||||
0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566,
|
||||
0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509,
|
||||
0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
|
||||
0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e,
|
||||
0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c,
|
||||
0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
|
||||
0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285,
|
||||
0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301,
|
||||
0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
|
||||
0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767,
|
||||
0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647,
|
||||
0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
|
||||
0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c,
|
||||
0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3,
|
||||
},
|
||||
{
|
||||
0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5,
|
||||
0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc,
|
||||
0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
|
||||
0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d,
|
||||
0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2,
|
||||
0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
|
||||
0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc,
|
||||
0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c,
|
||||
0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
|
||||
0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039,
|
||||
0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8,
|
||||
0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
|
||||
0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5,
|
||||
0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472,
|
||||
0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
|
||||
0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c,
|
||||
0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb,
|
||||
0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
|
||||
0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70,
|
||||
0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc,
|
||||
0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
|
||||
0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3,
|
||||
0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4,
|
||||
0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
|
||||
0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f,
|
||||
0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e,
|
||||
0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
|
||||
0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c,
|
||||
0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384,
|
||||
0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
|
||||
0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82,
|
||||
0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e,
|
||||
},
|
||||
}
|
||||
219
vendor/golang.org/x/crypto/openpgp/armor/armor.go
generated
vendored
219
vendor/golang.org/x/crypto/openpgp/armor/armor.go
generated
vendored
@@ -1,219 +0,0 @@
|
||||
// Copyright 2010 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 armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
|
||||
// very similar to PEM except that it has an additional CRC checksum.
|
||||
package armor // import "golang.org/x/crypto/openpgp/armor"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A Block represents an OpenPGP armored structure.
|
||||
//
|
||||
// The encoded form is:
|
||||
// -----BEGIN Type-----
|
||||
// Headers
|
||||
//
|
||||
// base64-encoded Bytes
|
||||
// '=' base64 encoded checksum
|
||||
// -----END Type-----
|
||||
// where Headers is a possibly empty sequence of Key: Value lines.
|
||||
//
|
||||
// Since the armored data can be very large, this package presents a streaming
|
||||
// interface.
|
||||
type Block struct {
|
||||
Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE").
|
||||
Header map[string]string // Optional headers.
|
||||
Body io.Reader // A Reader from which the contents can be read
|
||||
lReader lineReader
|
||||
oReader openpgpReader
|
||||
}
|
||||
|
||||
var ArmorCorrupt error = errors.StructuralError("armor invalid")
|
||||
|
||||
const crc24Init = 0xb704ce
|
||||
const crc24Poly = 0x1864cfb
|
||||
const crc24Mask = 0xffffff
|
||||
|
||||
// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
|
||||
func crc24(crc uint32, d []byte) uint32 {
|
||||
for _, b := range d {
|
||||
crc ^= uint32(b) << 16
|
||||
for i := 0; i < 8; i++ {
|
||||
crc <<= 1
|
||||
if crc&0x1000000 != 0 {
|
||||
crc ^= crc24Poly
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
var armorStart = []byte("-----BEGIN ")
|
||||
var armorEnd = []byte("-----END ")
|
||||
var armorEndOfLine = []byte("-----")
|
||||
|
||||
// lineReader wraps a line based reader. It watches for the end of an armor
|
||||
// block and records the expected CRC value.
|
||||
type lineReader struct {
|
||||
in *bufio.Reader
|
||||
buf []byte
|
||||
eof bool
|
||||
crc uint32
|
||||
}
|
||||
|
||||
func (l *lineReader) Read(p []byte) (n int, err error) {
|
||||
if l.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(l.buf) > 0 {
|
||||
n = copy(p, l.buf)
|
||||
l.buf = l.buf[n:]
|
||||
return
|
||||
}
|
||||
|
||||
line, isPrefix, err := l.in.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isPrefix {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
if len(line) == 5 && line[0] == '=' {
|
||||
// This is the checksum line
|
||||
var expectedBytes [3]byte
|
||||
var m int
|
||||
m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
|
||||
if m != 3 || err != nil {
|
||||
return
|
||||
}
|
||||
l.crc = uint32(expectedBytes[0])<<16 |
|
||||
uint32(expectedBytes[1])<<8 |
|
||||
uint32(expectedBytes[2])
|
||||
|
||||
line, _, err = l.in.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return
|
||||
}
|
||||
if !bytes.HasPrefix(line, armorEnd) {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
l.eof = true
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if len(line) > 96 {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
|
||||
n = copy(p, line)
|
||||
bytesToSave := len(line) - n
|
||||
if bytesToSave > 0 {
|
||||
if cap(l.buf) < bytesToSave {
|
||||
l.buf = make([]byte, 0, bytesToSave)
|
||||
}
|
||||
l.buf = l.buf[0:bytesToSave]
|
||||
copy(l.buf, line[n:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// openpgpReader passes Read calls to the underlying base64 decoder, but keeps
|
||||
// a running CRC of the resulting data and checks the CRC against the value
|
||||
// found by the lineReader at EOF.
|
||||
type openpgpReader struct {
|
||||
lReader *lineReader
|
||||
b64Reader io.Reader
|
||||
currentCRC uint32
|
||||
}
|
||||
|
||||
func (r *openpgpReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.b64Reader.Read(p)
|
||||
r.currentCRC = crc24(r.currentCRC, p[:n])
|
||||
|
||||
if err == io.EOF {
|
||||
if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Decode reads a PGP armored block from the given Reader. It will ignore
|
||||
// leading garbage. If it doesn't find a block, it will return nil, io.EOF. The
|
||||
// given Reader is not usable after calling this function: an arbitrary amount
|
||||
// of data may have been read past the end of the block.
|
||||
func Decode(in io.Reader) (p *Block, err error) {
|
||||
r := bufio.NewReaderSize(in, 100)
|
||||
var line []byte
|
||||
ignoreNext := false
|
||||
|
||||
TryNextBlock:
|
||||
p = nil
|
||||
|
||||
// Skip leading garbage
|
||||
for {
|
||||
ignoreThis := ignoreNext
|
||||
line, ignoreNext, err = r.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ignoreNext || ignoreThis {
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
p = new(Block)
|
||||
p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)])
|
||||
p.Header = make(map[string]string)
|
||||
nextIsContinuation := false
|
||||
var lastKey string
|
||||
|
||||
// Read headers
|
||||
for {
|
||||
isContinuation := nextIsContinuation
|
||||
line, nextIsContinuation, err = r.ReadLine()
|
||||
if err != nil {
|
||||
p = nil
|
||||
return
|
||||
}
|
||||
if isContinuation {
|
||||
p.Header[lastKey] += string(line)
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
i := bytes.Index(line, []byte(": "))
|
||||
if i == -1 {
|
||||
goto TryNextBlock
|
||||
}
|
||||
lastKey = string(line[:i])
|
||||
p.Header[lastKey] = string(line[i+2:])
|
||||
}
|
||||
|
||||
p.lReader.in = r
|
||||
p.oReader.currentCRC = crc24Init
|
||||
p.oReader.lReader = &p.lReader
|
||||
p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
|
||||
p.Body = &p.oReader
|
||||
|
||||
return
|
||||
}
|
||||
160
vendor/golang.org/x/crypto/openpgp/armor/encode.go
generated
vendored
160
vendor/golang.org/x/crypto/openpgp/armor/encode.go
generated
vendored
@@ -1,160 +0,0 @@
|
||||
// Copyright 2010 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 armor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
var armorHeaderSep = []byte(": ")
|
||||
var blockEnd = []byte("\n=")
|
||||
var newline = []byte("\n")
|
||||
var armorEndOfLineOut = []byte("-----\n")
|
||||
|
||||
// writeSlices writes its arguments to the given Writer.
|
||||
func writeSlices(out io.Writer, slices ...[]byte) (err error) {
|
||||
for _, s := range slices {
|
||||
_, err = out.Write(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lineBreaker breaks data across several lines, all of the same byte length
|
||||
// (except possibly the last). Lines are broken with a single '\n'.
|
||||
type lineBreaker struct {
|
||||
lineLength int
|
||||
line []byte
|
||||
used int
|
||||
out io.Writer
|
||||
haveWritten bool
|
||||
}
|
||||
|
||||
func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
|
||||
return &lineBreaker{
|
||||
lineLength: lineLength,
|
||||
line: make([]byte, lineLength),
|
||||
used: 0,
|
||||
out: out,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Write(b []byte) (n int, err error) {
|
||||
n = len(b)
|
||||
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if l.used == 0 && l.haveWritten {
|
||||
_, err = l.out.Write([]byte{'\n'})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.used+len(b) < l.lineLength {
|
||||
l.used += copy(l.line[l.used:], b)
|
||||
return
|
||||
}
|
||||
|
||||
l.haveWritten = true
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
excess := l.lineLength - l.used
|
||||
l.used = 0
|
||||
|
||||
_, err = l.out.Write(b[0:excess])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = l.Write(b[excess:])
|
||||
return
|
||||
}
|
||||
|
||||
func (l *lineBreaker) Close() (err error) {
|
||||
if l.used > 0 {
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// encoding keeps track of a running CRC24 over the data which has been written
|
||||
// to it and outputs a OpenPGP checksum when closed, followed by an armor
|
||||
// trailer.
|
||||
//
|
||||
// It's built into a stack of io.Writers:
|
||||
// encoding -> base64 encoder -> lineBreaker -> out
|
||||
type encoding struct {
|
||||
out io.Writer
|
||||
breaker *lineBreaker
|
||||
b64 io.WriteCloser
|
||||
crc uint32
|
||||
blockType []byte
|
||||
}
|
||||
|
||||
func (e *encoding) Write(data []byte) (n int, err error) {
|
||||
e.crc = crc24(e.crc, data)
|
||||
return e.b64.Write(data)
|
||||
}
|
||||
|
||||
func (e *encoding) Close() (err error) {
|
||||
err = e.b64.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.breaker.Close()
|
||||
|
||||
var checksumBytes [3]byte
|
||||
checksumBytes[0] = byte(e.crc >> 16)
|
||||
checksumBytes[1] = byte(e.crc >> 8)
|
||||
checksumBytes[2] = byte(e.crc)
|
||||
|
||||
var b64ChecksumBytes [4]byte
|
||||
base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
|
||||
|
||||
return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
|
||||
}
|
||||
|
||||
// Encode returns a WriteCloser which will encode the data written to it in
|
||||
// OpenPGP armor.
|
||||
func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
|
||||
bType := []byte(blockType)
|
||||
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(newline)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e := &encoding{
|
||||
out: out,
|
||||
breaker: newLineBreaker(out, 64),
|
||||
crc: crc24Init,
|
||||
blockType: bType,
|
||||
}
|
||||
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
|
||||
return e, nil
|
||||
}
|
||||
59
vendor/golang.org/x/crypto/openpgp/canonical_text.go
generated
vendored
59
vendor/golang.org/x/crypto/openpgp/canonical_text.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
// Copyright 2011 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 openpgp
|
||||
|
||||
import "hash"
|
||||
|
||||
// NewCanonicalTextHash reformats text written to it into the canonical
|
||||
// form and then applies the hash h. See RFC 4880, section 5.2.1.
|
||||
func NewCanonicalTextHash(h hash.Hash) hash.Hash {
|
||||
return &canonicalTextHash{h, 0}
|
||||
}
|
||||
|
||||
type canonicalTextHash struct {
|
||||
h hash.Hash
|
||||
s int
|
||||
}
|
||||
|
||||
var newline = []byte{'\r', '\n'}
|
||||
|
||||
func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
|
||||
start := 0
|
||||
|
||||
for i, c := range buf {
|
||||
switch cth.s {
|
||||
case 0:
|
||||
if c == '\r' {
|
||||
cth.s = 1
|
||||
} else if c == '\n' {
|
||||
cth.h.Write(buf[start:i])
|
||||
cth.h.Write(newline)
|
||||
start = i + 1
|
||||
}
|
||||
case 1:
|
||||
cth.s = 0
|
||||
}
|
||||
}
|
||||
|
||||
cth.h.Write(buf[start:])
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Sum(in []byte) []byte {
|
||||
return cth.h.Sum(in)
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Reset() {
|
||||
cth.h.Reset()
|
||||
cth.s = 0
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) Size() int {
|
||||
return cth.h.Size()
|
||||
}
|
||||
|
||||
func (cth *canonicalTextHash) BlockSize() int {
|
||||
return cth.h.BlockSize()
|
||||
}
|
||||
122
vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go
generated
vendored
122
vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go
generated
vendored
@@ -1,122 +0,0 @@
|
||||
// Copyright 2011 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 elgamal implements ElGamal encryption, suitable for OpenPGP,
|
||||
// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on
|
||||
// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31,
|
||||
// n. 4, 1985, pp. 469-472.
|
||||
//
|
||||
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
|
||||
// unsuitable for other protocols. RSA should be used in preference in any
|
||||
// case.
|
||||
package elgamal // import "golang.org/x/crypto/openpgp/elgamal"
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// PublicKey represents an ElGamal public key.
|
||||
type PublicKey struct {
|
||||
G, P, Y *big.Int
|
||||
}
|
||||
|
||||
// PrivateKey represents an ElGamal private key.
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
X *big.Int
|
||||
}
|
||||
|
||||
// Encrypt encrypts the given message to the given public key. The result is a
|
||||
// pair of integers. Errors can result from reading random, or because msg is
|
||||
// too large to be encrypted to the public key.
|
||||
func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) {
|
||||
pLen := (pub.P.BitLen() + 7) / 8
|
||||
if len(msg) > pLen-11 {
|
||||
err = errors.New("elgamal: message too long")
|
||||
return
|
||||
}
|
||||
|
||||
// EM = 0x02 || PS || 0x00 || M
|
||||
em := make([]byte, pLen-1)
|
||||
em[0] = 2
|
||||
ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
|
||||
err = nonZeroRandomBytes(ps, random)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
em[len(em)-len(msg)-1] = 0
|
||||
copy(mm, msg)
|
||||
|
||||
m := new(big.Int).SetBytes(em)
|
||||
|
||||
k, err := rand.Int(random, pub.P)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c1 = new(big.Int).Exp(pub.G, k, pub.P)
|
||||
s := new(big.Int).Exp(pub.Y, k, pub.P)
|
||||
c2 = s.Mul(s, m)
|
||||
c2.Mod(c2, pub.P)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt takes two integers, resulting from an ElGamal encryption, and
|
||||
// returns the plaintext of the message. An error can result only if the
|
||||
// ciphertext is invalid. Users should keep in mind that this is a padding
|
||||
// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
|
||||
// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks
|
||||
// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel
|
||||
// Bleichenbacher, Advances in Cryptology (Crypto '98),
|
||||
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
|
||||
s := new(big.Int).Exp(c1, priv.X, priv.P)
|
||||
s.ModInverse(s, priv.P)
|
||||
s.Mul(s, c2)
|
||||
s.Mod(s, priv.P)
|
||||
em := s.Bytes()
|
||||
|
||||
firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
|
||||
|
||||
// The remainder of the plaintext must be a string of non-zero random
|
||||
// octets, followed by a 0, followed by the message.
|
||||
// lookingForIndex: 1 iff we are still looking for the zero.
|
||||
// index: the offset of the first zero byte.
|
||||
var lookingForIndex, index int
|
||||
lookingForIndex = 1
|
||||
|
||||
for i := 1; i < len(em); i++ {
|
||||
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
|
||||
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
|
||||
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
|
||||
}
|
||||
|
||||
if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
|
||||
return nil, errors.New("elgamal: decryption error")
|
||||
}
|
||||
return em[index+1:], nil
|
||||
}
|
||||
|
||||
// nonZeroRandomBytes fills the given slice with non-zero random octets.
|
||||
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
|
||||
_, err = io.ReadFull(rand, s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
for s[i] == 0 {
|
||||
_, err = io.ReadFull(rand, s[i:i+1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
72
vendor/golang.org/x/crypto/openpgp/errors/errors.go
generated
vendored
72
vendor/golang.org/x/crypto/openpgp/errors/errors.go
generated
vendored
@@ -1,72 +0,0 @@
|
||||
// Copyright 2010 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 errors contains common error types for the OpenPGP packages.
|
||||
package errors // import "golang.org/x/crypto/openpgp/errors"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A StructuralError is returned when OpenPGP data is found to be syntactically
|
||||
// invalid.
|
||||
type StructuralError string
|
||||
|
||||
func (s StructuralError) Error() string {
|
||||
return "openpgp: invalid data: " + string(s)
|
||||
}
|
||||
|
||||
// UnsupportedError indicates that, although the OpenPGP data is valid, it
|
||||
// makes use of currently unimplemented features.
|
||||
type UnsupportedError string
|
||||
|
||||
func (s UnsupportedError) Error() string {
|
||||
return "openpgp: unsupported feature: " + string(s)
|
||||
}
|
||||
|
||||
// InvalidArgumentError indicates that the caller is in error and passed an
|
||||
// incorrect value.
|
||||
type InvalidArgumentError string
|
||||
|
||||
func (i InvalidArgumentError) Error() string {
|
||||
return "openpgp: invalid argument: " + string(i)
|
||||
}
|
||||
|
||||
// SignatureError indicates that a syntactically valid signature failed to
|
||||
// validate.
|
||||
type SignatureError string
|
||||
|
||||
func (b SignatureError) Error() string {
|
||||
return "openpgp: invalid signature: " + string(b)
|
||||
}
|
||||
|
||||
type keyIncorrectError int
|
||||
|
||||
func (ki keyIncorrectError) Error() string {
|
||||
return "openpgp: incorrect key"
|
||||
}
|
||||
|
||||
var ErrKeyIncorrect error = keyIncorrectError(0)
|
||||
|
||||
type unknownIssuerError int
|
||||
|
||||
func (unknownIssuerError) Error() string {
|
||||
return "openpgp: signature made by unknown entity"
|
||||
}
|
||||
|
||||
var ErrUnknownIssuer error = unknownIssuerError(0)
|
||||
|
||||
type keyRevokedError int
|
||||
|
||||
func (keyRevokedError) Error() string {
|
||||
return "openpgp: signature made by revoked key"
|
||||
}
|
||||
|
||||
var ErrKeyRevoked error = keyRevokedError(0)
|
||||
|
||||
type UnknownPacketTypeError uint8
|
||||
|
||||
func (upte UnknownPacketTypeError) Error() string {
|
||||
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
|
||||
}
|
||||
640
vendor/golang.org/x/crypto/openpgp/keys.go
generated
vendored
640
vendor/golang.org/x/crypto/openpgp/keys.go
generated
vendored
@@ -1,640 +0,0 @@
|
||||
// Copyright 2011 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 openpgp
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// PublicKeyType is the armor type for a PGP public key.
|
||||
var PublicKeyType = "PGP PUBLIC KEY BLOCK"
|
||||
|
||||
// PrivateKeyType is the armor type for a PGP private key.
|
||||
var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
|
||||
|
||||
// An Entity represents the components of an OpenPGP key: a primary public key
|
||||
// (which must be a signing key), one or more identities claimed by that key,
|
||||
// and zero or more subkeys, which may be encryption keys.
|
||||
type Entity struct {
|
||||
PrimaryKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Identities map[string]*Identity // indexed by Identity.Name
|
||||
Revocations []*packet.Signature
|
||||
Subkeys []Subkey
|
||||
}
|
||||
|
||||
// An Identity represents an identity claimed by an Entity and zero or more
|
||||
// assertions by other entities about that claim.
|
||||
type Identity struct {
|
||||
Name string // by convention, has the form "Full Name (comment) <email@example.com>"
|
||||
UserId *packet.UserId
|
||||
SelfSignature *packet.Signature
|
||||
Signatures []*packet.Signature
|
||||
}
|
||||
|
||||
// A Subkey is an additional public key in an Entity. Subkeys can be used for
|
||||
// encryption.
|
||||
type Subkey struct {
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
Sig *packet.Signature
|
||||
}
|
||||
|
||||
// A Key identifies a specific public key in an Entity. This is either the
|
||||
// Entity's primary key or a subkey.
|
||||
type Key struct {
|
||||
Entity *Entity
|
||||
PublicKey *packet.PublicKey
|
||||
PrivateKey *packet.PrivateKey
|
||||
SelfSignature *packet.Signature
|
||||
}
|
||||
|
||||
// A KeyRing provides access to public and private keys.
|
||||
type KeyRing interface {
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
KeysById(id uint64) []Key
|
||||
// KeysByIdAndUsage returns the set of keys with the given id
|
||||
// that also meet the key usage given by requiredUsage.
|
||||
// The requiredUsage is expressed as the bitwise-OR of
|
||||
// packet.KeyFlag* values.
|
||||
KeysByIdUsage(id uint64, requiredUsage byte) []Key
|
||||
// DecryptionKeys returns all private keys that are valid for
|
||||
// decryption.
|
||||
DecryptionKeys() []Key
|
||||
}
|
||||
|
||||
// primaryIdentity returns the Identity marked as primary or the first identity
|
||||
// if none are so marked.
|
||||
func (e *Entity) primaryIdentity() *Identity {
|
||||
var firstIdentity *Identity
|
||||
for _, ident := range e.Identities {
|
||||
if firstIdentity == nil {
|
||||
firstIdentity = ident
|
||||
}
|
||||
if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
return ident
|
||||
}
|
||||
}
|
||||
return firstIdentity
|
||||
}
|
||||
|
||||
// encryptionKey returns the best candidate Key for encrypting a message to the
|
||||
// given Entity.
|
||||
func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
|
||||
candidateSubkey := -1
|
||||
|
||||
// Iterate the keys to find the newest key
|
||||
var maxTime time.Time
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagEncryptCommunications &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!subkey.Sig.KeyExpired(now) &&
|
||||
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
|
||||
candidateSubkey = i
|
||||
maxTime = subkey.Sig.CreationTime
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
|
||||
}
|
||||
|
||||
// If we don't have any candidate subkeys for encryption and
|
||||
// the primary key doesn't have any usage metadata then we
|
||||
// assume that the primary key is ok. Or, if the primary key is
|
||||
// marked as ok to encrypt to, then we can obviously use it.
|
||||
i := e.primaryIdentity()
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications &&
|
||||
e.PrimaryKey.PubKeyAlgo.CanEncrypt() &&
|
||||
!i.SelfSignature.KeyExpired(now) {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
}
|
||||
|
||||
// This Entity appears to be signing only.
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// signingKey return the best candidate Key for signing a message with this
|
||||
// Entity.
|
||||
func (e *Entity) signingKey(now time.Time) (Key, bool) {
|
||||
candidateSubkey := -1
|
||||
|
||||
for i, subkey := range e.Subkeys {
|
||||
if subkey.Sig.FlagsValid &&
|
||||
subkey.Sig.FlagSign &&
|
||||
subkey.PublicKey.PubKeyAlgo.CanSign() &&
|
||||
!subkey.Sig.KeyExpired(now) {
|
||||
candidateSubkey = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if candidateSubkey != -1 {
|
||||
subkey := e.Subkeys[candidateSubkey]
|
||||
return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
|
||||
}
|
||||
|
||||
// If we have no candidate subkey then we assume that it's ok to sign
|
||||
// with the primary key.
|
||||
i := e.primaryIdentity()
|
||||
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
|
||||
!i.SelfSignature.KeyExpired(now) {
|
||||
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
|
||||
}
|
||||
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// An EntityList contains one or more Entities.
|
||||
type EntityList []*Entity
|
||||
|
||||
// KeysById returns the set of keys that have the given key id.
|
||||
func (el EntityList) KeysById(id uint64) (keys []Key) {
|
||||
for _, e := range el {
|
||||
if e.PrimaryKey.KeyId == id {
|
||||
var selfSig *packet.Signature
|
||||
for _, ident := range e.Identities {
|
||||
if selfSig == nil {
|
||||
selfSig = ident.SelfSignature
|
||||
} else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
selfSig = ident.SelfSignature
|
||||
break
|
||||
}
|
||||
}
|
||||
keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig})
|
||||
}
|
||||
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PublicKey.KeyId == id {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeysByIdAndUsage returns the set of keys with the given id that also meet
|
||||
// the key usage given by requiredUsage. The requiredUsage is expressed as
|
||||
// the bitwise-OR of packet.KeyFlag* values.
|
||||
func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
|
||||
for _, key := range el.KeysById(id) {
|
||||
if len(key.Entity.Revocations) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if key.SelfSignature.RevocationReason != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if key.SelfSignature.FlagsValid && requiredUsage != 0 {
|
||||
var usage byte
|
||||
if key.SelfSignature.FlagCertify {
|
||||
usage |= packet.KeyFlagCertify
|
||||
}
|
||||
if key.SelfSignature.FlagSign {
|
||||
usage |= packet.KeyFlagSign
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptCommunications {
|
||||
usage |= packet.KeyFlagEncryptCommunications
|
||||
}
|
||||
if key.SelfSignature.FlagEncryptStorage {
|
||||
usage |= packet.KeyFlagEncryptStorage
|
||||
}
|
||||
if usage&requiredUsage != requiredUsage {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptionKeys returns all private keys that are valid for decryption.
|
||||
func (el EntityList) DecryptionKeys() (keys []Key) {
|
||||
for _, e := range el {
|
||||
for _, subKey := range e.Subkeys {
|
||||
if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) {
|
||||
keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file.
|
||||
func ReadArmoredKeyRing(r io.Reader) (EntityList, error) {
|
||||
block, err := armor.Decode(r)
|
||||
if err == io.EOF {
|
||||
return nil, errors.InvalidArgumentError("no armored data found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block.Type != PublicKeyType && block.Type != PrivateKeyType {
|
||||
return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type)
|
||||
}
|
||||
|
||||
return ReadKeyRing(block.Body)
|
||||
}
|
||||
|
||||
// ReadKeyRing reads one or more public/private keys. Unsupported keys are
|
||||
// ignored as long as at least a single valid key is found.
|
||||
func ReadKeyRing(r io.Reader) (el EntityList, err error) {
|
||||
packets := packet.NewReader(r)
|
||||
var lastUnsupportedError error
|
||||
|
||||
for {
|
||||
var e *Entity
|
||||
e, err = ReadEntity(packets)
|
||||
if err != nil {
|
||||
// TODO: warn about skipped unsupported/unreadable keys
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
} else if _, ok := err.(errors.StructuralError); ok {
|
||||
// Skip unreadable, badly-formatted keys
|
||||
lastUnsupportedError = err
|
||||
err = readToNextPublicKey(packets)
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
el = nil
|
||||
break
|
||||
}
|
||||
} else {
|
||||
el = append(el, e)
|
||||
}
|
||||
}
|
||||
|
||||
if len(el) == 0 && err == nil {
|
||||
err = lastUnsupportedError
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readToNextPublicKey reads packets until the start of the entity and leaves
|
||||
// the first packet of the new entity in the Reader.
|
||||
func readToNextPublicKey(packets *packet.Reader) (err error) {
|
||||
var p packet.Packet
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey {
|
||||
packets.Unread(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEntity reads an entity (public key, identities, subkeys etc) from the
|
||||
// given Reader.
|
||||
func ReadEntity(packets *packet.Reader) (*Entity, error) {
|
||||
e := new(Entity)
|
||||
e.Identities = make(map[string]*Identity)
|
||||
|
||||
p, err := packets.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok {
|
||||
if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
|
||||
packets.Unread(p)
|
||||
return nil, errors.StructuralError("first packet was not a public/private key")
|
||||
}
|
||||
e.PrimaryKey = &e.PrivateKey.PublicKey
|
||||
}
|
||||
|
||||
if !e.PrimaryKey.PubKeyAlgo.CanSign() {
|
||||
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
||||
}
|
||||
|
||||
var current *Identity
|
||||
var revocations []*packet.Signature
|
||||
EachPacket:
|
||||
for {
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pkt := p.(type) {
|
||||
case *packet.UserId:
|
||||
current = new(Identity)
|
||||
current.Name = pkt.Id
|
||||
current.UserId = pkt
|
||||
e.Identities[pkt.Id] = current
|
||||
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig, ok := p.(*packet.Signature)
|
||||
if !ok {
|
||||
return nil, errors.StructuralError("user ID packet not followed by self-signature")
|
||||
}
|
||||
|
||||
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
|
||||
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
|
||||
return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error())
|
||||
}
|
||||
current.SelfSignature = sig
|
||||
break
|
||||
}
|
||||
current.Signatures = append(current.Signatures, sig)
|
||||
}
|
||||
case *packet.Signature:
|
||||
if pkt.SigType == packet.SigTypeKeyRevocation {
|
||||
revocations = append(revocations, pkt)
|
||||
} else if pkt.SigType == packet.SigTypeDirectSignature {
|
||||
// TODO: RFC4880 5.2.1 permits signatures
|
||||
// directly on keys (eg. to bind additional
|
||||
// revocation keys).
|
||||
} else if current == nil {
|
||||
return nil, errors.StructuralError("signature packet found before user id packet")
|
||||
} else {
|
||||
current.Signatures = append(current.Signatures, pkt)
|
||||
}
|
||||
case *packet.PrivateKey:
|
||||
if pkt.IsSubkey == false {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, &pkt.PublicKey, pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *packet.PublicKey:
|
||||
if pkt.IsSubkey == false {
|
||||
packets.Unread(p)
|
||||
break EachPacket
|
||||
}
|
||||
err = addSubkey(e, packets, pkt, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
// we ignore unknown packets
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Identities) == 0 {
|
||||
return nil, errors.StructuralError("entity without any identities")
|
||||
}
|
||||
|
||||
for _, revocation := range revocations {
|
||||
err = e.PrimaryKey.VerifyRevocationSignature(revocation)
|
||||
if err == nil {
|
||||
e.Revocations = append(e.Revocations, revocation)
|
||||
} else {
|
||||
// TODO: RFC 4880 5.2.3.15 defines revocation keys.
|
||||
return nil, errors.StructuralError("revocation signature signed by alternate key")
|
||||
}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
|
||||
var subKey Subkey
|
||||
subKey.PublicKey = pub
|
||||
subKey.PrivateKey = priv
|
||||
p, err := packets.Next()
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
var ok bool
|
||||
subKey.Sig, ok = p.(*packet.Signature)
|
||||
if !ok {
|
||||
return errors.StructuralError("subkey packet not followed by signature")
|
||||
}
|
||||
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||
return errors.StructuralError("subkey signature with wrong type")
|
||||
}
|
||||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
|
||||
if err != nil {
|
||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||
}
|
||||
e.Subkeys = append(e.Subkeys, subKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
const defaultRSAKeyBits = 2048
|
||||
|
||||
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
|
||||
// single identity composed of the given full name, comment and email, any of
|
||||
// which may be empty but must not contain any of "()<>\x00".
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
|
||||
currentTime := config.Now()
|
||||
|
||||
bits := defaultRSAKeyBits
|
||||
if config != nil && config.RSABits != 0 {
|
||||
bits = config.RSABits
|
||||
}
|
||||
|
||||
uid := packet.NewUserId(name, comment, email)
|
||||
if uid == nil {
|
||||
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
|
||||
}
|
||||
signingPriv, err := rsa.GenerateKey(config.Random(), bits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encryptingPriv, err := rsa.GenerateKey(config.Random(), bits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := &Entity{
|
||||
PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey),
|
||||
PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv),
|
||||
Identities: make(map[string]*Identity),
|
||||
}
|
||||
isPrimaryId := true
|
||||
e.Identities[uid.Id] = &Identity{
|
||||
Name: uid.Id,
|
||||
UserId: uid,
|
||||
SelfSignature: &packet.Signature{
|
||||
CreationTime: currentTime,
|
||||
SigType: packet.SigTypePositiveCert,
|
||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||
Hash: config.Hash(),
|
||||
IsPrimaryId: &isPrimaryId,
|
||||
FlagsValid: true,
|
||||
FlagSign: true,
|
||||
FlagCertify: true,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
},
|
||||
}
|
||||
err = e.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, e.PrimaryKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the user passes in a DefaultHash via packet.Config,
|
||||
// set the PreferredHash for the SelfSignature.
|
||||
if config != nil && config.DefaultHash != 0 {
|
||||
e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)}
|
||||
}
|
||||
|
||||
// Likewise for DefaultCipher.
|
||||
if config != nil && config.DefaultCipher != 0 {
|
||||
e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)}
|
||||
}
|
||||
|
||||
e.Subkeys = make([]Subkey, 1)
|
||||
e.Subkeys[0] = Subkey{
|
||||
PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
|
||||
PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv),
|
||||
Sig: &packet.Signature{
|
||||
CreationTime: currentTime,
|
||||
SigType: packet.SigTypeSubkeyBinding,
|
||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||
Hash: config.Hash(),
|
||||
FlagsValid: true,
|
||||
FlagEncryptStorage: true,
|
||||
FlagEncryptCommunications: true,
|
||||
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||
},
|
||||
}
|
||||
e.Subkeys[0].PublicKey.IsSubkey = true
|
||||
e.Subkeys[0].PrivateKey.IsSubkey = true
|
||||
err = e.Subkeys[0].Sig.SignKey(e.Subkeys[0].PublicKey, e.PrivateKey, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// SerializePrivate serializes an Entity, including private key material, to
|
||||
// the given Writer. For now, it must only be used on an Entity returned from
|
||||
// NewEntity.
|
||||
// config is ignored
|
||||
func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
|
||||
err = e.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ident.SelfSignature.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PrivateKey.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the public part of the given Entity to w. (No private
|
||||
// key material will be output).
|
||||
func (e *Entity) Serialize(w io.Writer) error {
|
||||
err := e.PrimaryKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ident := range e.Identities {
|
||||
err = ident.UserId.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ident.SelfSignature.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sig := range ident.Signatures {
|
||||
err = sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, subkey := range e.Subkeys {
|
||||
err = subkey.PublicKey.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = subkey.Sig.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignIdentity adds a signature to e, from signer, attesting that identity is
|
||||
// associated with e. The provided identity must already be an element of
|
||||
// e.Identities and the private key of signer must have been decrypted if
|
||||
// necessary.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error {
|
||||
if signer.PrivateKey == nil {
|
||||
return errors.InvalidArgumentError("signing Entity must have a private key")
|
||||
}
|
||||
if signer.PrivateKey.Encrypted {
|
||||
return errors.InvalidArgumentError("signing Entity's private key must be decrypted")
|
||||
}
|
||||
ident, ok := e.Identities[identity]
|
||||
if !ok {
|
||||
return errors.InvalidArgumentError("given identity string not found in Entity")
|
||||
}
|
||||
|
||||
sig := &packet.Signature{
|
||||
SigType: packet.SigTypeGenericCert,
|
||||
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
|
||||
Hash: config.Hash(),
|
||||
CreationTime: config.Now(),
|
||||
IssuerKeyId: &signer.PrivateKey.KeyId,
|
||||
}
|
||||
if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil {
|
||||
return err
|
||||
}
|
||||
ident.Signatures = append(ident.Signatures, sig)
|
||||
return nil
|
||||
}
|
||||
123
vendor/golang.org/x/crypto/openpgp/packet/compressed.go
generated
vendored
123
vendor/golang.org/x/crypto/openpgp/packet/compressed.go
generated
vendored
@@ -1,123 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Compressed represents a compressed OpenPGP packet. The decompressed contents
|
||||
// will contain more OpenPGP packets. See RFC 4880, section 5.6.
|
||||
type Compressed struct {
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
const (
|
||||
NoCompression = flate.NoCompression
|
||||
BestSpeed = flate.BestSpeed
|
||||
BestCompression = flate.BestCompression
|
||||
DefaultCompression = flate.DefaultCompression
|
||||
)
|
||||
|
||||
// CompressionConfig contains compressor configuration settings.
|
||||
type CompressionConfig struct {
|
||||
// Level is the compression level to use. It must be set to
|
||||
// between -1 and 9, with -1 causing the compressor to use the
|
||||
// default compression level, 0 causing the compressor to use
|
||||
// no compression and 1 to 9 representing increasing (better,
|
||||
// slower) compression levels. If Level is less than -1 or
|
||||
// more then 9, a non-nil error will be returned during
|
||||
// encryption. See the constants above for convenient common
|
||||
// settings for Level.
|
||||
Level int
|
||||
}
|
||||
|
||||
func (c *Compressed) parse(r io.Reader) error {
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch buf[0] {
|
||||
case 1:
|
||||
c.Body = flate.NewReader(r)
|
||||
case 2:
|
||||
c.Body, err = zlib.NewReader(r)
|
||||
case 3:
|
||||
c.Body = bzip2.NewReader(r)
|
||||
default:
|
||||
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// compressedWriterCloser represents the serialized compression stream
|
||||
// header and the compressor. Its Close() method ensures that both the
|
||||
// compressor and serialized stream header are closed. Its Write()
|
||||
// method writes to the compressor.
|
||||
type compressedWriteCloser struct {
|
||||
sh io.Closer // Stream Header
|
||||
c io.WriteCloser // Compressor
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Write(p []byte) (int, error) {
|
||||
return cwc.c.Write(p)
|
||||
}
|
||||
|
||||
func (cwc compressedWriteCloser) Close() (err error) {
|
||||
err = cwc.c.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cwc.sh.Close()
|
||||
}
|
||||
|
||||
// SerializeCompressed serializes a compressed data packet to w and
|
||||
// returns a WriteCloser to which the literal data packets themselves
|
||||
// can be written and which MUST be closed on completion. If cc is
|
||||
// nil, sensible defaults will be used to configure the compression
|
||||
// algorithm.
|
||||
func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) {
|
||||
compressed, err := serializeStreamHeader(w, packetTypeCompressed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = compressed.Write([]byte{uint8(algo)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
level := DefaultCompression
|
||||
if cc != nil {
|
||||
level = cc.Level
|
||||
}
|
||||
|
||||
var compressor io.WriteCloser
|
||||
switch algo {
|
||||
case CompressionZIP:
|
||||
compressor, err = flate.NewWriter(compressed, level)
|
||||
case CompressionZLIB:
|
||||
compressor, err = zlib.NewWriterLevel(compressed, level)
|
||||
default:
|
||||
s := strconv.Itoa(int(algo))
|
||||
err = errors.UnsupportedError("Unsupported compression algorithm: " + s)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
literaldata = compressedWriteCloser{compressed, compressor}
|
||||
|
||||
return
|
||||
}
|
||||
91
vendor/golang.org/x/crypto/openpgp/packet/config.go
generated
vendored
91
vendor/golang.org/x/crypto/openpgp/packet/config.go
generated
vendored
@@ -1,91 +0,0 @@
|
||||
// Copyright 2012 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 packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config collects a number of parameters along with sensible defaults.
|
||||
// A nil *Config is valid and results in all default values.
|
||||
type Config struct {
|
||||
// Rand provides the source of entropy.
|
||||
// If nil, the crypto/rand Reader is used.
|
||||
Rand io.Reader
|
||||
// DefaultHash is the default hash function to be used.
|
||||
// If zero, SHA-256 is used.
|
||||
DefaultHash crypto.Hash
|
||||
// DefaultCipher is the cipher to be used.
|
||||
// If zero, AES-128 is used.
|
||||
DefaultCipher CipherFunction
|
||||
// Time returns the current time as the number of seconds since the
|
||||
// epoch. If Time is nil, time.Now is used.
|
||||
Time func() time.Time
|
||||
// DefaultCompressionAlgo is the compression algorithm to be
|
||||
// applied to the plaintext before encryption. If zero, no
|
||||
// compression is done.
|
||||
DefaultCompressionAlgo CompressionAlgo
|
||||
// CompressionConfig configures the compression settings.
|
||||
CompressionConfig *CompressionConfig
|
||||
// S2KCount is only used for symmetric encryption. It
|
||||
// determines the strength of the passphrase stretching when
|
||||
// the said passphrase is hashed to produce a key. S2KCount
|
||||
// should be between 1024 and 65011712, inclusive. If Config
|
||||
// is nil or S2KCount is 0, the value 65536 used. Not all
|
||||
// values in the above range can be represented. S2KCount will
|
||||
// be rounded up to the next representable value if it cannot
|
||||
// be encoded exactly. When set, it is strongly encrouraged to
|
||||
// use a value that is at least 65536. See RFC 4880 Section
|
||||
// 3.7.1.3.
|
||||
S2KCount int
|
||||
// RSABits is the number of bits in new RSA keys made with NewEntity.
|
||||
// If zero, then 2048 bit keys are created.
|
||||
RSABits int
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
if c == nil || c.Rand == nil {
|
||||
return rand.Reader
|
||||
}
|
||||
return c.Rand
|
||||
}
|
||||
|
||||
func (c *Config) Hash() crypto.Hash {
|
||||
if c == nil || uint(c.DefaultHash) == 0 {
|
||||
return crypto.SHA256
|
||||
}
|
||||
return c.DefaultHash
|
||||
}
|
||||
|
||||
func (c *Config) Cipher() CipherFunction {
|
||||
if c == nil || uint8(c.DefaultCipher) == 0 {
|
||||
return CipherAES128
|
||||
}
|
||||
return c.DefaultCipher
|
||||
}
|
||||
|
||||
func (c *Config) Now() time.Time {
|
||||
if c == nil || c.Time == nil {
|
||||
return time.Now()
|
||||
}
|
||||
return c.Time()
|
||||
}
|
||||
|
||||
func (c *Config) Compression() CompressionAlgo {
|
||||
if c == nil {
|
||||
return CompressionNone
|
||||
}
|
||||
return c.DefaultCompressionAlgo
|
||||
}
|
||||
|
||||
func (c *Config) PasswordHashIterations() int {
|
||||
if c == nil || c.S2KCount == 0 {
|
||||
return 0
|
||||
}
|
||||
return c.S2KCount
|
||||
}
|
||||
206
vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go
generated
vendored
206
vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go
generated
vendored
@@ -1,206 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
const encryptedKeyVersion = 3
|
||||
|
||||
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
|
||||
// section 5.1.
|
||||
type EncryptedKey struct {
|
||||
KeyId uint64
|
||||
Algo PublicKeyAlgorithm
|
||||
CipherFunc CipherFunction // only valid after a successful Decrypt
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
|
||||
encryptedMPI1, encryptedMPI2 parsedMPI
|
||||
}
|
||||
|
||||
func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
var buf [10]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != encryptedKeyVersion {
|
||||
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
e.KeyId = binary.BigEndian.Uint64(buf[1:9])
|
||||
e.Algo = PublicKeyAlgorithm(buf[9])
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoElGamal:
|
||||
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = consumeAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 {
|
||||
var checksum uint16
|
||||
for _, v := range key {
|
||||
checksum += uint16(v)
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted session key with the given private key. The
|
||||
// private key must have been decrypted first.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
var err error
|
||||
var b []byte
|
||||
|
||||
// TODO(agl): use session key decryption routines here to avoid
|
||||
// padding oracle attacks.
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
k := priv.PrivateKey.(*rsa.PrivateKey)
|
||||
b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
|
||||
case PubKeyAlgoElGamal:
|
||||
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
|
||||
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)
|
||||
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.CipherFunc = CipherFunction(b[0])
|
||||
e.Key = b[1 : len(b)-2]
|
||||
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
|
||||
checksum := checksumKeyMaterial(e.Key)
|
||||
if checksum != expectedChecksum {
|
||||
return errors.StructuralError("EncryptedKey checksum incorrect")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the encrypted key packet, e, to w.
|
||||
func (e *EncryptedKey) Serialize(w io.Writer) error {
|
||||
var mpiLen int
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
mpiLen = 2 + len(e.encryptedMPI1.bytes)
|
||||
case PubKeyAlgoElGamal:
|
||||
mpiLen = 2 + len(e.encryptedMPI1.bytes) + 2 + len(e.encryptedMPI2.bytes)
|
||||
default:
|
||||
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
|
||||
}
|
||||
|
||||
serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen)
|
||||
|
||||
w.Write([]byte{encryptedKeyVersion})
|
||||
binary.Write(w, binary.BigEndian, e.KeyId)
|
||||
w.Write([]byte{byte(e.Algo)})
|
||||
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
writeMPIs(w, e.encryptedMPI1)
|
||||
case PubKeyAlgoElGamal:
|
||||
writeMPIs(w, e.encryptedMPI1, e.encryptedMPI2)
|
||||
default:
|
||||
panic("internal error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
|
||||
var buf [10]byte
|
||||
buf[0] = encryptedKeyVersion
|
||||
binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
|
||||
buf[9] = byte(pub.PubKeyAlgo)
|
||||
|
||||
keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */)
|
||||
keyBlock[0] = byte(cipherFunc)
|
||||
copy(keyBlock[1:], key)
|
||||
checksum := checksumKeyMaterial(key)
|
||||
keyBlock[1+len(key)] = byte(checksum >> 8)
|
||||
keyBlock[1+len(key)+1] = byte(checksum)
|
||||
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
|
||||
case PubKeyAlgoElGamal:
|
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
|
||||
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
|
||||
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error {
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText)
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeMPI(w, 8*uint16(len(cipherText)), cipherText)
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error {
|
||||
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := 10 /* header length */
|
||||
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
|
||||
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeBig(w, c1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeBig(w, c2)
|
||||
}
|
||||
89
vendor/golang.org/x/crypto/openpgp/packet/literal.go
generated
vendored
89
vendor/golang.org/x/crypto/openpgp/packet/literal.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// LiteralData represents an encrypted file. See RFC 4880, section 5.9.
|
||||
type LiteralData struct {
|
||||
IsBinary bool
|
||||
FileName string
|
||||
Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined.
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
// ForEyesOnly returns whether the contents of the LiteralData have been marked
|
||||
// as especially sensitive.
|
||||
func (l *LiteralData) ForEyesOnly() bool {
|
||||
return l.FileName == "_CONSOLE"
|
||||
}
|
||||
|
||||
func (l *LiteralData) parse(r io.Reader) (err error) {
|
||||
var buf [256]byte
|
||||
|
||||
_, err = readFull(r, buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.IsBinary = buf[0] == 'b'
|
||||
fileNameLen := int(buf[1])
|
||||
|
||||
_, err = readFull(r, buf[:fileNameLen])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.FileName = string(buf[:fileNameLen])
|
||||
|
||||
_, err = readFull(r, buf[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.Time = binary.BigEndian.Uint32(buf[:4])
|
||||
l.Body = r
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeLiteral serializes a literal data packet to w and returns a
|
||||
// WriteCloser to which the data itself can be written and which MUST be closed
|
||||
// on completion. The fileName is truncated to 255 bytes.
|
||||
func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) {
|
||||
var buf [4]byte
|
||||
buf[0] = 't'
|
||||
if isBinary {
|
||||
buf[0] = 'b'
|
||||
}
|
||||
if len(fileName) > 255 {
|
||||
fileName = fileName[:255]
|
||||
}
|
||||
buf[1] = byte(len(fileName))
|
||||
|
||||
inner, err := serializeStreamHeader(w, packetTypeLiteralData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = inner.Write(buf[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = inner.Write([]byte(fileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[:], time)
|
||||
_, err = inner.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
plaintext = inner
|
||||
return
|
||||
}
|
||||
143
vendor/golang.org/x/crypto/openpgp/packet/ocfb.go
generated
vendored
143
vendor/golang.org/x/crypto/openpgp/packet/ocfb.go
generated
vendored
@@ -1,143 +0,0 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
type ocfbEncrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// An OCFBResyncOption determines if the "resynchronization step" of OCFB is
|
||||
// performed.
|
||||
type OCFBResyncOption bool
|
||||
|
||||
const (
|
||||
OCFBResync OCFBResyncOption = true
|
||||
OCFBNoResync OCFBResyncOption = false
|
||||
)
|
||||
|
||||
// NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block, and an initial amount of
|
||||
// ciphertext. randData must be random bytes and be the same length as the
|
||||
// cipher.Block's block size. Resync determines if the "resynchronization step"
|
||||
// from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on
|
||||
// this point.
|
||||
func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) {
|
||||
blockSize := block.BlockSize()
|
||||
if len(randData) != blockSize {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
x := &ocfbEncrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefix := make([]byte, blockSize+2)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefix[i] = randData[i] ^ x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefix[blockSize] = x.fre[0] ^ randData[blockSize-2]
|
||||
prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1]
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
return x, prefix
|
||||
}
|
||||
|
||||
func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
x.fre[x.outUsed] ^= src[i]
|
||||
dst[i] = x.fre[x.outUsed]
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
||||
|
||||
type ocfbDecrypter struct {
|
||||
b cipher.Block
|
||||
fre []byte
|
||||
outUsed int
|
||||
}
|
||||
|
||||
// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's
|
||||
// cipher feedback mode using the given cipher.Block. Prefix must be the first
|
||||
// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's
|
||||
// block size. If an incorrect key is detected then nil is returned. On
|
||||
// successful exit, blockSize+2 bytes of decrypted data are written into
|
||||
// prefix. Resync determines if the "resynchronization step" from RFC 4880,
|
||||
// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point.
|
||||
func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream {
|
||||
blockSize := block.BlockSize()
|
||||
if len(prefix) != blockSize+2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
x := &ocfbDecrypter{
|
||||
b: block,
|
||||
fre: make([]byte, blockSize),
|
||||
outUsed: 0,
|
||||
}
|
||||
prefixCopy := make([]byte, len(prefix))
|
||||
copy(prefixCopy, prefix)
|
||||
|
||||
block.Encrypt(x.fre, x.fre)
|
||||
for i := 0; i < blockSize; i++ {
|
||||
prefixCopy[i] ^= x.fre[i]
|
||||
}
|
||||
|
||||
block.Encrypt(x.fre, prefix[:blockSize])
|
||||
prefixCopy[blockSize] ^= x.fre[0]
|
||||
prefixCopy[blockSize+1] ^= x.fre[1]
|
||||
|
||||
if prefixCopy[blockSize-2] != prefixCopy[blockSize] ||
|
||||
prefixCopy[blockSize-1] != prefixCopy[blockSize+1] {
|
||||
return nil
|
||||
}
|
||||
|
||||
if resync {
|
||||
block.Encrypt(x.fre, prefix[2:])
|
||||
} else {
|
||||
x.fre[0] = prefix[blockSize]
|
||||
x.fre[1] = prefix[blockSize+1]
|
||||
x.outUsed = 2
|
||||
}
|
||||
copy(prefix, prefixCopy)
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) {
|
||||
for i := 0; i < len(src); i++ {
|
||||
if x.outUsed == len(x.fre) {
|
||||
x.b.Encrypt(x.fre, x.fre)
|
||||
x.outUsed = 0
|
||||
}
|
||||
|
||||
c := src[i]
|
||||
dst[i] = x.fre[x.outUsed] ^ src[i]
|
||||
x.fre[x.outUsed] = c
|
||||
x.outUsed++
|
||||
}
|
||||
}
|
||||
73
vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go
generated
vendored
73
vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
||||
// section 5.4.
|
||||
type OnePassSignature struct {
|
||||
SigType SignatureType
|
||||
Hash crypto.Hash
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
KeyId uint64
|
||||
IsLast bool
|
||||
}
|
||||
|
||||
const onePassSignatureVersion = 3
|
||||
|
||||
func (ops *OnePassSignature) parse(r io.Reader) (err error) {
|
||||
var buf [13]byte
|
||||
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != onePassSignatureVersion {
|
||||
err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
var ok bool
|
||||
ops.Hash, ok = s2k.HashIdToHash(buf[2])
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2])))
|
||||
}
|
||||
|
||||
ops.SigType = SignatureType(buf[1])
|
||||
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3])
|
||||
ops.KeyId = binary.BigEndian.Uint64(buf[4:12])
|
||||
ops.IsLast = buf[12] != 0
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the given OnePassSignature to w.
|
||||
func (ops *OnePassSignature) Serialize(w io.Writer) error {
|
||||
var buf [13]byte
|
||||
buf[0] = onePassSignatureVersion
|
||||
buf[1] = uint8(ops.SigType)
|
||||
var ok bool
|
||||
buf[2], ok = s2k.HashToHashId(ops.Hash)
|
||||
if !ok {
|
||||
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
|
||||
}
|
||||
buf[3] = uint8(ops.PubKeyAlgo)
|
||||
binary.BigEndian.PutUint64(buf[4:12], ops.KeyId)
|
||||
if ops.IsLast {
|
||||
buf[12] = 1
|
||||
}
|
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(buf[:])
|
||||
return err
|
||||
}
|
||||
162
vendor/golang.org/x/crypto/openpgp/packet/opaque.go
generated
vendored
162
vendor/golang.org/x/crypto/openpgp/packet/opaque.go
generated
vendored
@@ -1,162 +0,0 @@
|
||||
// Copyright 2012 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 packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is
|
||||
// useful for splitting and storing the original packet contents separately,
|
||||
// handling unsupported packet types or accessing parts of the packet not yet
|
||||
// implemented by this package.
|
||||
type OpaquePacket struct {
|
||||
// Packet type
|
||||
Tag uint8
|
||||
// Reason why the packet was parsed opaquely
|
||||
Reason error
|
||||
// Binary contents of the packet data
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
func (op *OpaquePacket) parse(r io.Reader) (err error) {
|
||||
op.Contents, err = ioutil.ReadAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the packet to a writer in its original form, including
|
||||
// the packet header.
|
||||
func (op *OpaquePacket) Serialize(w io.Writer) (err error) {
|
||||
err = serializeHeader(w, packetType(op.Tag), len(op.Contents))
|
||||
if err == nil {
|
||||
_, err = w.Write(op.Contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Parse attempts to parse the opaque contents into a structure supported by
|
||||
// this package. If the packet is not known then the result will be another
|
||||
// OpaquePacket.
|
||||
func (op *OpaquePacket) Parse() (p Packet, err error) {
|
||||
hdr := bytes.NewBuffer(nil)
|
||||
err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
return op, err
|
||||
}
|
||||
p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents)))
|
||||
if err != nil {
|
||||
op.Reason = err
|
||||
p = op
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueReader reads OpaquePackets from an io.Reader.
|
||||
type OpaqueReader struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func NewOpaqueReader(r io.Reader) *OpaqueReader {
|
||||
return &OpaqueReader{r: r}
|
||||
}
|
||||
|
||||
// Read the next OpaquePacket.
|
||||
func (or *OpaqueReader) Next() (op *OpaquePacket, err error) {
|
||||
tag, _, contents, err := readHeader(or.r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
op = &OpaquePacket{Tag: uint8(tag), Reason: err}
|
||||
err = op.parse(contents)
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OpaqueSubpacket represents an unparsed OpenPGP subpacket,
|
||||
// as found in signature and user attribute packets.
|
||||
type OpaqueSubpacket struct {
|
||||
SubType uint8
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from
|
||||
// their byte representation.
|
||||
func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) {
|
||||
var (
|
||||
subHeaderLen int
|
||||
subPacket *OpaqueSubpacket
|
||||
)
|
||||
for len(contents) > 0 {
|
||||
subHeaderLen, subPacket, err = nextSubpacket(contents)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
result = append(result, subPacket)
|
||||
contents = contents[subHeaderLen+len(subPacket.Contents):]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) {
|
||||
// RFC 4880, section 5.2.3.1
|
||||
var subLen uint32
|
||||
if len(contents) < 1 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket = &OpaqueSubpacket{}
|
||||
switch {
|
||||
case contents[0] < 192:
|
||||
subHeaderLen = 2 // 1 length byte, 1 subtype byte
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[0])
|
||||
contents = contents[1:]
|
||||
case contents[0] < 255:
|
||||
subHeaderLen = 3 // 2 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192
|
||||
contents = contents[2:]
|
||||
default:
|
||||
subHeaderLen = 6 // 5 length bytes, 1 subtype
|
||||
if len(contents) < subHeaderLen {
|
||||
goto Truncated
|
||||
}
|
||||
subLen = uint32(contents[1])<<24 |
|
||||
uint32(contents[2])<<16 |
|
||||
uint32(contents[3])<<8 |
|
||||
uint32(contents[4])
|
||||
contents = contents[5:]
|
||||
}
|
||||
if subLen > uint32(len(contents)) || subLen == 0 {
|
||||
goto Truncated
|
||||
}
|
||||
subPacket.SubType = contents[0]
|
||||
subPacket.Contents = contents[1:subLen]
|
||||
return
|
||||
Truncated:
|
||||
err = errors.StructuralError("subpacket truncated")
|
||||
return
|
||||
}
|
||||
|
||||
func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) {
|
||||
buf := make([]byte, 6)
|
||||
n := serializeSubpacketLength(buf, len(osp.Contents)+1)
|
||||
buf[n] = osp.SubType
|
||||
if _, err = w.Write(buf[:n+1]); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(osp.Contents)
|
||||
return
|
||||
}
|
||||
549
vendor/golang.org/x/crypto/openpgp/packet/packet.go
generated
vendored
549
vendor/golang.org/x/crypto/openpgp/packet/packet.go
generated
vendored
@@ -1,549 +0,0 @@
|
||||
// Copyright 2011 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 packet implements parsing and serialization of OpenPGP packets, as
|
||||
// specified in RFC 4880.
|
||||
package packet // import "golang.org/x/crypto/openpgp/packet"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/cast5"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// readFull is the same as io.ReadFull except that reading zero bytes returns
|
||||
// ErrUnexpectedEOF rather than EOF.
|
||||
func readFull(r io.Reader, buf []byte) (n int, err error) {
|
||||
n, err = io.ReadFull(r, buf)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2.
|
||||
func readLength(r io.Reader) (length int64, isPartial bool, err error) {
|
||||
var buf [4]byte
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case buf[0] < 192:
|
||||
length = int64(buf[0])
|
||||
case buf[0] < 224:
|
||||
length = int64(buf[0]-192) << 8
|
||||
_, err = readFull(r, buf[0:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length += int64(buf[0]) + 192
|
||||
case buf[0] < 255:
|
||||
length = int64(1) << (buf[0] & 0x1f)
|
||||
isPartial = true
|
||||
default:
|
||||
_, err = readFull(r, buf[0:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length = int64(buf[0])<<24 |
|
||||
int64(buf[1])<<16 |
|
||||
int64(buf[2])<<8 |
|
||||
int64(buf[3])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths.
|
||||
// The continuation lengths are parsed and removed from the stream and EOF is
|
||||
// returned at the end of the packet. See RFC 4880, section 4.2.2.4.
|
||||
type partialLengthReader struct {
|
||||
r io.Reader
|
||||
remaining int64
|
||||
isPartial bool
|
||||
}
|
||||
|
||||
func (r *partialLengthReader) Read(p []byte) (n int, err error) {
|
||||
for r.remaining == 0 {
|
||||
if !r.isPartial {
|
||||
return 0, io.EOF
|
||||
}
|
||||
r.remaining, r.isPartial, err = readLength(r.r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
toRead := int64(len(p))
|
||||
if toRead > r.remaining {
|
||||
toRead = r.remaining
|
||||
}
|
||||
|
||||
n, err = r.r.Read(p[:int(toRead)])
|
||||
r.remaining -= int64(n)
|
||||
if n < int(toRead) && err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// partialLengthWriter writes a stream of data using OpenPGP partial lengths.
|
||||
// See RFC 4880, section 4.2.2.4.
|
||||
type partialLengthWriter struct {
|
||||
w io.WriteCloser
|
||||
lengthByte [1]byte
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Write(p []byte) (n int, err error) {
|
||||
for len(p) > 0 {
|
||||
for power := uint(14); power < 32; power-- {
|
||||
l := 1 << power
|
||||
if len(p) >= l {
|
||||
w.lengthByte[0] = 224 + uint8(power)
|
||||
_, err = w.w.Write(w.lengthByte[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var m int
|
||||
m, err = w.w.Write(p[:l])
|
||||
n += m
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p = p[l:]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *partialLengthWriter) Close() error {
|
||||
w.lengthByte[0] = 0
|
||||
_, err := w.w.Write(w.lengthByte[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return w.w.Close()
|
||||
}
|
||||
|
||||
// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the
|
||||
// underlying Reader returns EOF before the limit has been reached.
|
||||
type spanReader struct {
|
||||
r io.Reader
|
||||
n int64
|
||||
}
|
||||
|
||||
func (l *spanReader) Read(p []byte) (n int, err error) {
|
||||
if l.n <= 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if int64(len(p)) > l.n {
|
||||
p = p[0:l.n]
|
||||
}
|
||||
n, err = l.r.Read(p)
|
||||
l.n -= int64(n)
|
||||
if l.n > 0 && err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readHeader parses a packet header and returns an io.Reader which will return
|
||||
// the contents of the packet. See RFC 4880, section 4.2.
|
||||
func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) {
|
||||
var buf [4]byte
|
||||
_, err = io.ReadFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0]&0x80 == 0 {
|
||||
err = errors.StructuralError("tag byte does not have MSB set")
|
||||
return
|
||||
}
|
||||
if buf[0]&0x40 == 0 {
|
||||
// Old format packet
|
||||
tag = packetType((buf[0] & 0x3f) >> 2)
|
||||
lengthType := buf[0] & 3
|
||||
if lengthType == 3 {
|
||||
length = -1
|
||||
contents = r
|
||||
return
|
||||
}
|
||||
lengthBytes := 1 << lengthType
|
||||
_, err = readFull(r, buf[0:lengthBytes])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < lengthBytes; i++ {
|
||||
length <<= 8
|
||||
length |= int64(buf[i])
|
||||
}
|
||||
contents = &spanReader{r, length}
|
||||
return
|
||||
}
|
||||
|
||||
// New format packet
|
||||
tag = packetType(buf[0] & 0x3f)
|
||||
length, isPartial, err := readLength(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isPartial {
|
||||
contents = &partialLengthReader{
|
||||
remaining: length,
|
||||
isPartial: true,
|
||||
r: r,
|
||||
}
|
||||
length = -1
|
||||
} else {
|
||||
contents = &spanReader{r, length}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
|
||||
// 4.2.
|
||||
func serializeHeader(w io.Writer, ptype packetType, length int) (err error) {
|
||||
var buf [6]byte
|
||||
var n int
|
||||
|
||||
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||||
if length < 192 {
|
||||
buf[1] = byte(length)
|
||||
n = 2
|
||||
} else if length < 8384 {
|
||||
length -= 192
|
||||
buf[1] = 192 + byte(length>>8)
|
||||
buf[2] = byte(length)
|
||||
n = 3
|
||||
} else {
|
||||
buf[1] = 255
|
||||
buf[2] = byte(length >> 24)
|
||||
buf[3] = byte(length >> 16)
|
||||
buf[4] = byte(length >> 8)
|
||||
buf[5] = byte(length)
|
||||
n = 6
|
||||
}
|
||||
|
||||
_, err = w.Write(buf[:n])
|
||||
return
|
||||
}
|
||||
|
||||
// serializeStreamHeader writes an OpenPGP packet header to w where the
|
||||
// length of the packet is unknown. It returns a io.WriteCloser which can be
|
||||
// used to write the contents of the packet. See RFC 4880, section 4.2.
|
||||
func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) {
|
||||
var buf [1]byte
|
||||
buf[0] = 0x80 | 0x40 | byte(ptype)
|
||||
_, err = w.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = &partialLengthWriter{w: w}
|
||||
return
|
||||
}
|
||||
|
||||
// Packet represents an OpenPGP packet. Users are expected to try casting
|
||||
// instances of this interface to specific packet types.
|
||||
type Packet interface {
|
||||
parse(io.Reader) error
|
||||
}
|
||||
|
||||
// consumeAll reads from the given Reader until error, returning the number of
|
||||
// bytes read.
|
||||
func consumeAll(r io.Reader) (n int64, err error) {
|
||||
var m int
|
||||
var buf [1024]byte
|
||||
|
||||
for {
|
||||
m, err = r.Read(buf[:])
|
||||
n += int64(m)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// packetType represents the numeric ids of the different OpenPGP packet types. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2
|
||||
type packetType uint8
|
||||
|
||||
const (
|
||||
packetTypeEncryptedKey packetType = 1
|
||||
packetTypeSignature packetType = 2
|
||||
packetTypeSymmetricKeyEncrypted packetType = 3
|
||||
packetTypeOnePassSignature packetType = 4
|
||||
packetTypePrivateKey packetType = 5
|
||||
packetTypePublicKey packetType = 6
|
||||
packetTypePrivateSubkey packetType = 7
|
||||
packetTypeCompressed packetType = 8
|
||||
packetTypeSymmetricallyEncrypted packetType = 9
|
||||
packetTypeLiteralData packetType = 11
|
||||
packetTypeUserId packetType = 13
|
||||
packetTypePublicSubkey packetType = 14
|
||||
packetTypeUserAttribute packetType = 17
|
||||
packetTypeSymmetricallyEncryptedMDC packetType = 18
|
||||
)
|
||||
|
||||
// peekVersion detects the version of a public key packet about to
|
||||
// be read. A bufio.Reader at the original position of the io.Reader
|
||||
// is returned.
|
||||
func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) {
|
||||
bufr = bufio.NewReader(r)
|
||||
var verBuf []byte
|
||||
if verBuf, err = bufr.Peek(1); err != nil {
|
||||
return
|
||||
}
|
||||
ver = verBuf[0]
|
||||
return
|
||||
}
|
||||
|
||||
// Read reads a single OpenPGP packet from the given io.Reader. If there is an
|
||||
// error parsing a packet, the whole packet is consumed from the input.
|
||||
func Read(r io.Reader) (p Packet, err error) {
|
||||
tag, _, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case packetTypeEncryptedKey:
|
||||
p = new(EncryptedKey)
|
||||
case packetTypeSignature:
|
||||
var version byte
|
||||
// Detect signature version
|
||||
if contents, version, err = peekVersion(contents); err != nil {
|
||||
return
|
||||
}
|
||||
if version < 4 {
|
||||
p = new(SignatureV3)
|
||||
} else {
|
||||
p = new(Signature)
|
||||
}
|
||||
case packetTypeSymmetricKeyEncrypted:
|
||||
p = new(SymmetricKeyEncrypted)
|
||||
case packetTypeOnePassSignature:
|
||||
p = new(OnePassSignature)
|
||||
case packetTypePrivateKey, packetTypePrivateSubkey:
|
||||
pk := new(PrivateKey)
|
||||
if tag == packetTypePrivateSubkey {
|
||||
pk.IsSubkey = true
|
||||
}
|
||||
p = pk
|
||||
case packetTypePublicKey, packetTypePublicSubkey:
|
||||
var version byte
|
||||
if contents, version, err = peekVersion(contents); err != nil {
|
||||
return
|
||||
}
|
||||
isSubkey := tag == packetTypePublicSubkey
|
||||
if version < 4 {
|
||||
p = &PublicKeyV3{IsSubkey: isSubkey}
|
||||
} else {
|
||||
p = &PublicKey{IsSubkey: isSubkey}
|
||||
}
|
||||
case packetTypeCompressed:
|
||||
p = new(Compressed)
|
||||
case packetTypeSymmetricallyEncrypted:
|
||||
p = new(SymmetricallyEncrypted)
|
||||
case packetTypeLiteralData:
|
||||
p = new(LiteralData)
|
||||
case packetTypeUserId:
|
||||
p = new(UserId)
|
||||
case packetTypeUserAttribute:
|
||||
p = new(UserAttribute)
|
||||
case packetTypeSymmetricallyEncryptedMDC:
|
||||
se := new(SymmetricallyEncrypted)
|
||||
se.MDC = true
|
||||
p = se
|
||||
default:
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
}
|
||||
if p != nil {
|
||||
err = p.parse(contents)
|
||||
}
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SignatureType represents the different semantic meanings of an OpenPGP
|
||||
// signature. See RFC 4880, section 5.2.1.
|
||||
type SignatureType uint8
|
||||
|
||||
const (
|
||||
SigTypeBinary SignatureType = 0
|
||||
SigTypeText = 1
|
||||
SigTypeGenericCert = 0x10
|
||||
SigTypePersonaCert = 0x11
|
||||
SigTypeCasualCert = 0x12
|
||||
SigTypePositiveCert = 0x13
|
||||
SigTypeSubkeyBinding = 0x18
|
||||
SigTypePrimaryKeyBinding = 0x19
|
||||
SigTypeDirectSignature = 0x1F
|
||||
SigTypeKeyRevocation = 0x20
|
||||
SigTypeSubkeyRevocation = 0x28
|
||||
)
|
||||
|
||||
// PublicKeyAlgorithm represents the different public key system specified for
|
||||
// OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12
|
||||
type PublicKeyAlgorithm uint8
|
||||
|
||||
const (
|
||||
PubKeyAlgoRSA PublicKeyAlgorithm = 1
|
||||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
|
||||
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
|
||||
PubKeyAlgoElGamal PublicKeyAlgorithm = 16
|
||||
PubKeyAlgoDSA PublicKeyAlgorithm = 17
|
||||
// RFC 6637, Section 5.
|
||||
PubKeyAlgoECDH PublicKeyAlgorithm = 18
|
||||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
|
||||
)
|
||||
|
||||
// CanEncrypt returns true if it's possible to encrypt a message to a public
|
||||
// key of the given type.
|
||||
func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CanSign returns true if it's possible for a public key of the given type to
|
||||
// sign a message.
|
||||
func (pka PublicKeyAlgorithm) CanSign() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CipherFunction represents the different block ciphers specified for OpenPGP. See
|
||||
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
|
||||
type CipherFunction uint8
|
||||
|
||||
const (
|
||||
Cipher3DES CipherFunction = 2
|
||||
CipherCAST5 CipherFunction = 3
|
||||
CipherAES128 CipherFunction = 7
|
||||
CipherAES192 CipherFunction = 8
|
||||
CipherAES256 CipherFunction = 9
|
||||
)
|
||||
|
||||
// KeySize returns the key size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) KeySize() int {
|
||||
switch cipher {
|
||||
case Cipher3DES:
|
||||
return 24
|
||||
case CipherCAST5:
|
||||
return cast5.KeySize
|
||||
case CipherAES128:
|
||||
return 16
|
||||
case CipherAES192:
|
||||
return 24
|
||||
case CipherAES256:
|
||||
return 32
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// blockSize returns the block size, in bytes, of cipher.
|
||||
func (cipher CipherFunction) blockSize() int {
|
||||
switch cipher {
|
||||
case Cipher3DES:
|
||||
return des.BlockSize
|
||||
case CipherCAST5:
|
||||
return 8
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
return 16
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given cipher.
|
||||
func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
|
||||
switch cipher {
|
||||
case Cipher3DES:
|
||||
block, _ = des.NewTripleDESCipher(key)
|
||||
case CipherCAST5:
|
||||
block, _ = cast5.NewCipher(key)
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
block, _ = aes.NewCipher(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// readMPI reads a big integer from r. The bit length returned is the bit
|
||||
// length that was specified in r. This is preserved so that the integer can be
|
||||
// reserialized exactly.
|
||||
func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
|
||||
var buf [2]byte
|
||||
_, err = readFull(r, buf[0:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
bitLength = uint16(buf[0])<<8 | uint16(buf[1])
|
||||
numBytes := (int(bitLength) + 7) / 8
|
||||
mpi = make([]byte, numBytes)
|
||||
_, err = readFull(r, mpi)
|
||||
// According to RFC 4880 3.2. we should check that the MPI has no leading
|
||||
// zeroes (at least when not an encrypted MPI?), but this implementation
|
||||
// does generate leading zeroes, so we keep accepting them.
|
||||
return
|
||||
}
|
||||
|
||||
// writeMPI serializes a big integer to w.
|
||||
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
|
||||
// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
|
||||
// Implementations seem to be tolerant of them, and stripping them would
|
||||
// make it complex to guarantee matching re-serialization.
|
||||
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
|
||||
if err == nil {
|
||||
_, err = w.Write(mpiBytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// writeBig serializes a *big.Int to w.
|
||||
func writeBig(w io.Writer, i *big.Int) error {
|
||||
return writeMPI(w, uint16(i.BitLen()), i.Bytes())
|
||||
}
|
||||
|
||||
// padToKeySize left-pads a MPI with zeroes to match the length of the
|
||||
// specified RSA public.
|
||||
func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
if len(b) >= k {
|
||||
return b
|
||||
}
|
||||
bb := make([]byte, k)
|
||||
copy(bb[len(bb)-len(b):], b)
|
||||
return bb
|
||||
}
|
||||
|
||||
// CompressionAlgo Represents the different compression algorithms
|
||||
// supported by OpenPGP (except for BZIP2, which is not currently
|
||||
// supported). See Section 9.3 of RFC 4880.
|
||||
type CompressionAlgo uint8
|
||||
|
||||
const (
|
||||
CompressionNone CompressionAlgo = 0
|
||||
CompressionZIP CompressionAlgo = 1
|
||||
CompressionZLIB CompressionAlgo = 2
|
||||
)
|
||||
380
vendor/golang.org/x/crypto/openpgp/packet/private_key.go
generated
vendored
380
vendor/golang.org/x/crypto/openpgp/packet/private_key.go
generated
vendored
@@ -1,380 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
// section 5.5.3.
|
||||
type PrivateKey struct {
|
||||
PublicKey
|
||||
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
|
||||
encryptedData []byte
|
||||
cipher CipherFunction
|
||||
s2k func(out, in []byte)
|
||||
PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or a crypto.Signer.
|
||||
sha1Checksum bool
|
||||
iv []byte
|
||||
}
|
||||
|
||||
func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewDSAPrivateKey(currentTime time.Time, priv *dsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewDSAPublicKey(currentTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewElGamalPrivateKey(currentTime time.Time, priv *elgamal.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewElGamalPublicKey(currentTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewECDSAPublicKey(currentTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewSignerPrivateKey creates a sign-only PrivateKey from a crypto.Signer that
|
||||
// implements RSA or ECDSA.
|
||||
func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
switch pubkey := signer.Public().(type) {
|
||||
case rsa.PublicKey:
|
||||
pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
|
||||
pk.PubKeyAlgo = PubKeyAlgoRSASignOnly
|
||||
case ecdsa.PublicKey:
|
||||
pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
|
||||
default:
|
||||
panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey")
|
||||
}
|
||||
pk.PrivateKey = signer
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
err = (&pk.PublicKey).parse(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var buf [1]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s2kType := buf[0]
|
||||
|
||||
switch s2kType {
|
||||
case 0:
|
||||
pk.s2k = nil
|
||||
pk.Encrypted = false
|
||||
case 254, 255:
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.cipher = CipherFunction(buf[0])
|
||||
pk.Encrypted = true
|
||||
pk.s2k, err = s2k.Parse(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s2kType == 254 {
|
||||
pk.sha1Checksum = true
|
||||
}
|
||||
default:
|
||||
return errors.UnsupportedError("deprecated s2k function in private key")
|
||||
}
|
||||
|
||||
if pk.Encrypted {
|
||||
blockSize := pk.cipher.blockSize()
|
||||
if blockSize == 0 {
|
||||
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
|
||||
}
|
||||
pk.iv = make([]byte, blockSize)
|
||||
_, err = readFull(r, pk.iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pk.encryptedData, err = ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !pk.Encrypted {
|
||||
return pk.parsePrivateKey(pk.encryptedData)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mod64kHash(d []byte) uint16 {
|
||||
var h uint16
|
||||
for _, b := range d {
|
||||
h += uint16(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
||||
// TODO(agl): support encrypted private keys
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err = pk.PublicKey.serializeWithoutHeaders(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf.WriteByte(0 /* no encryption */)
|
||||
|
||||
privateKeyBuf := bytes.NewBuffer(nil)
|
||||
|
||||
switch priv := pk.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
err = serializeRSAPrivateKey(privateKeyBuf, priv)
|
||||
case *dsa.PrivateKey:
|
||||
err = serializeDSAPrivateKey(privateKeyBuf, priv)
|
||||
case *elgamal.PrivateKey:
|
||||
err = serializeElGamalPrivateKey(privateKeyBuf, priv)
|
||||
case *ecdsa.PrivateKey:
|
||||
err = serializeECDSAPrivateKey(privateKeyBuf, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ptype := packetTypePrivateKey
|
||||
contents := buf.Bytes()
|
||||
privateKeyBytes := privateKeyBuf.Bytes()
|
||||
if pk.IsSubkey {
|
||||
ptype = packetTypePrivateSubkey
|
||||
}
|
||||
err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(contents)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(privateKeyBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
checksum := mod64kHash(privateKeyBytes)
|
||||
var checksumBytes [2]byte
|
||||
checksumBytes[0] = byte(checksum >> 8)
|
||||
checksumBytes[1] = byte(checksum)
|
||||
_, err = w.Write(checksumBytes[:])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
|
||||
err := writeBig(w, priv.D)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeBig(w, priv.Primes[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeBig(w, priv.Primes[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeBig(w, priv.Precomputed.Qinv)
|
||||
}
|
||||
|
||||
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
|
||||
return writeBig(w, priv.X)
|
||||
}
|
||||
|
||||
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
|
||||
return writeBig(w, priv.X)
|
||||
}
|
||||
|
||||
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
|
||||
return writeBig(w, priv.D)
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted private key using a passphrase.
|
||||
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
||||
if !pk.Encrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := make([]byte, pk.cipher.KeySize())
|
||||
pk.s2k(key, passphrase)
|
||||
block := pk.cipher.new(key)
|
||||
cfb := cipher.NewCFBDecrypter(block, pk.iv)
|
||||
|
||||
data := make([]byte, len(pk.encryptedData))
|
||||
cfb.XORKeyStream(data, pk.encryptedData)
|
||||
|
||||
if pk.sha1Checksum {
|
||||
if len(data) < sha1.Size {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(data[:len(data)-sha1.Size])
|
||||
sum := h.Sum(nil)
|
||||
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-sha1.Size]
|
||||
} else {
|
||||
if len(data) < 2 {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(data)-2; i++ {
|
||||
sum += uint16(data[i])
|
||||
}
|
||||
if data[len(data)-2] != uint8(sum>>8) ||
|
||||
data[len(data)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-2]
|
||||
}
|
||||
|
||||
return pk.parsePrivateKey(data)
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
|
||||
switch pk.PublicKey.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
|
||||
return pk.parseRSAPrivateKey(data)
|
||||
case PubKeyAlgoDSA:
|
||||
return pk.parseDSAPrivateKey(data)
|
||||
case PubKeyAlgoElGamal:
|
||||
return pk.parseElGamalPrivateKey(data)
|
||||
case PubKeyAlgoECDSA:
|
||||
return pk.parseECDSAPrivateKey(data)
|
||||
}
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
|
||||
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
|
||||
rsaPriv := new(rsa.PrivateKey)
|
||||
rsaPriv.PublicKey = *rsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
q, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rsaPriv.D = new(big.Int).SetBytes(d)
|
||||
rsaPriv.Primes = make([]*big.Int, 2)
|
||||
rsaPriv.Primes[0] = new(big.Int).SetBytes(p)
|
||||
rsaPriv.Primes[1] = new(big.Int).SetBytes(q)
|
||||
if err := rsaPriv.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
rsaPriv.Precompute()
|
||||
pk.PrivateKey = rsaPriv
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) {
|
||||
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
|
||||
dsaPriv := new(dsa.PrivateKey)
|
||||
dsaPriv.PublicKey = *dsaPub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
x, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dsaPriv.X = new(big.Int).SetBytes(x)
|
||||
pk.PrivateKey = dsaPriv
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
|
||||
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey)
|
||||
priv := new(elgamal.PrivateKey)
|
||||
priv.PublicKey = *pub
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
x, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
priv.X = new(big.Int).SetBytes(x)
|
||||
pk.PrivateKey = priv
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
||||
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
d, _, err := readMPI(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pk.PrivateKey = &ecdsa.PrivateKey{
|
||||
PublicKey: *ecdsaPub,
|
||||
D: new(big.Int).SetBytes(d),
|
||||
}
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
753
vendor/golang.org/x/crypto/openpgp/packet/public_key.go
generated
vendored
753
vendor/golang.org/x/crypto/openpgp/packet/public_key.go
generated
vendored
@@ -1,753 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/elgamal"
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// NIST curve P-256
|
||||
oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}
|
||||
// NIST curve P-384
|
||||
oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22}
|
||||
// NIST curve P-521
|
||||
oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23}
|
||||
)
|
||||
|
||||
const maxOIDLength = 8
|
||||
|
||||
// ecdsaKey stores the algorithm-specific fields for ECDSA keys.
|
||||
// as defined in RFC 6637, Section 9.
|
||||
type ecdsaKey struct {
|
||||
// oid contains the OID byte sequence identifying the elliptic curve used
|
||||
oid []byte
|
||||
// p contains the elliptic curve point that represents the public key
|
||||
p parsedMPI
|
||||
}
|
||||
|
||||
// parseOID reads the OID for the curve as defined in RFC 6637, Section 9.
|
||||
func parseOID(r io.Reader) (oid []byte, err error) {
|
||||
buf := make([]byte, maxOIDLength)
|
||||
if _, err = readFull(r, buf[:1]); err != nil {
|
||||
return
|
||||
}
|
||||
oidLen := buf[0]
|
||||
if int(oidLen) > len(buf) {
|
||||
err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen)))
|
||||
return
|
||||
}
|
||||
oid = buf[:oidLen]
|
||||
_, err = readFull(r, oid)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ecdsaKey) parse(r io.Reader) (err error) {
|
||||
if f.oid, err = parseOID(r); err != nil {
|
||||
return err
|
||||
}
|
||||
f.p.bytes, f.p.bitLength, err = readMPI(r)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ecdsaKey) serialize(w io.Writer) (err error) {
|
||||
buf := make([]byte, maxOIDLength+1)
|
||||
buf[0] = byte(len(f.oid))
|
||||
copy(buf[1:], f.oid)
|
||||
if _, err = w.Write(buf[:len(f.oid)+1]); err != nil {
|
||||
return
|
||||
}
|
||||
return writeMPIs(w, f.p)
|
||||
}
|
||||
|
||||
func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) {
|
||||
var c elliptic.Curve
|
||||
if bytes.Equal(f.oid, oidCurveP256) {
|
||||
c = elliptic.P256()
|
||||
} else if bytes.Equal(f.oid, oidCurveP384) {
|
||||
c = elliptic.P384()
|
||||
} else if bytes.Equal(f.oid, oidCurveP521) {
|
||||
c = elliptic.P521()
|
||||
} else {
|
||||
return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid))
|
||||
}
|
||||
x, y := elliptic.Unmarshal(c, f.p.bytes)
|
||||
if x == nil {
|
||||
return nil, errors.UnsupportedError("failed to parse EC point")
|
||||
}
|
||||
return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil
|
||||
}
|
||||
|
||||
func (f *ecdsaKey) byteLen() int {
|
||||
return 1 + len(f.oid) + 2 + len(f.p.bytes)
|
||||
}
|
||||
|
||||
type kdfHashFunction byte
|
||||
type kdfAlgorithm byte
|
||||
|
||||
// ecdhKdf stores key derivation function parameters
|
||||
// used for ECDH encryption. See RFC 6637, Section 9.
|
||||
type ecdhKdf struct {
|
||||
KdfHash kdfHashFunction
|
||||
KdfAlgo kdfAlgorithm
|
||||
}
|
||||
|
||||
func (f *ecdhKdf) parse(r io.Reader) (err error) {
|
||||
buf := make([]byte, 1)
|
||||
if _, err = readFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
kdfLen := int(buf[0])
|
||||
if kdfLen < 3 {
|
||||
return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
|
||||
}
|
||||
buf = make([]byte, kdfLen)
|
||||
if _, err = readFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
reserved := int(buf[0])
|
||||
f.KdfHash = kdfHashFunction(buf[1])
|
||||
f.KdfAlgo = kdfAlgorithm(buf[2])
|
||||
if reserved != 0x01 {
|
||||
return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ecdhKdf) serialize(w io.Writer) (err error) {
|
||||
buf := make([]byte, 4)
|
||||
// See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys.
|
||||
buf[0] = byte(0x03) // Length of the following fields
|
||||
buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now
|
||||
buf[2] = byte(f.KdfHash)
|
||||
buf[3] = byte(f.KdfAlgo)
|
||||
_, err = w.Write(buf[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (f *ecdhKdf) byteLen() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
|
||||
type PublicKey struct {
|
||||
CreationTime time.Time
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey
|
||||
Fingerprint [20]byte
|
||||
KeyId uint64
|
||||
IsSubkey bool
|
||||
|
||||
n, e, p, q, g, y parsedMPI
|
||||
|
||||
// RFC 6637 fields
|
||||
ec *ecdsaKey
|
||||
ecdh *ecdhKdf
|
||||
}
|
||||
|
||||
// signingKey provides a convenient abstraction over signature verification
|
||||
// for v3 and v4 public keys.
|
||||
type signingKey interface {
|
||||
SerializeSignaturePrefix(io.Writer)
|
||||
serializeWithoutHeaders(io.Writer) error
|
||||
}
|
||||
|
||||
func fromBig(n *big.Int) parsedMPI {
|
||||
return parsedMPI{
|
||||
bytes: n.Bytes(),
|
||||
bitLength: uint16(n.BitLen()),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
|
||||
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoRSA,
|
||||
PublicKey: pub,
|
||||
n: fromBig(pub.N),
|
||||
e: fromBig(big.NewInt(int64(pub.E))),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey.
|
||||
func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoDSA,
|
||||
PublicKey: pub,
|
||||
p: fromBig(pub.P),
|
||||
q: fromBig(pub.Q),
|
||||
g: fromBig(pub.G),
|
||||
y: fromBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
|
||||
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoElGamal,
|
||||
PublicKey: pub,
|
||||
p: fromBig(pub.P),
|
||||
g: fromBig(pub.G),
|
||||
y: fromBig(pub.Y),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoECDSA,
|
||||
PublicKey: pub,
|
||||
ec: new(ecdsaKey),
|
||||
}
|
||||
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
pk.ec.oid = oidCurveP256
|
||||
case elliptic.P384():
|
||||
pk.ec.oid = oidCurveP384
|
||||
case elliptic.P521():
|
||||
pk.ec.oid = oidCurveP521
|
||||
default:
|
||||
panic("unknown elliptic curve")
|
||||
}
|
||||
|
||||
pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
|
||||
// The bit length is 3 (for the 0x04 specifying an uncompressed key)
|
||||
// plus two field elements (for x and y), which are rounded up to the
|
||||
// nearest byte. See https://tools.ietf.org/html/rfc6637#section-6
|
||||
fieldBytes := (pub.Curve.Params().BitSize + 7) & ^7
|
||||
pk.ec.p.bitLength = uint16(3 + fieldBytes + fieldBytes)
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 {
|
||||
return errors.UnsupportedError("public key version")
|
||||
}
|
||||
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
|
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
err = pk.parseRSA(r)
|
||||
case PubKeyAlgoDSA:
|
||||
err = pk.parseDSA(r)
|
||||
case PubKeyAlgoElGamal:
|
||||
err = pk.parseElGamal(r)
|
||||
case PubKeyAlgoECDSA:
|
||||
pk.ec = new(ecdsaKey)
|
||||
if err = pk.ec.parse(r); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PublicKey, err = pk.ec.newECDSA()
|
||||
case PubKeyAlgoECDH:
|
||||
pk.ec = new(ecdsaKey)
|
||||
if err = pk.ec.parse(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.ecdh = new(ecdhKdf)
|
||||
if err = pk.ecdh.parse(r); err != nil {
|
||||
return
|
||||
}
|
||||
// The ECDH key is stored in an ecdsa.PublicKey for convenience.
|
||||
pk.PublicKey, err = pk.ec.newECDSA()
|
||||
default:
|
||||
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) setFingerPrintAndKeyId() {
|
||||
// RFC 4880, section 12.2
|
||||
fingerPrint := sha1.New()
|
||||
pk.SerializeSignaturePrefix(fingerPrint)
|
||||
pk.serializeWithoutHeaders(fingerPrint)
|
||||
copy(pk.Fingerprint[:], fingerPrint.Sum(nil))
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
|
||||
}
|
||||
|
||||
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
||||
pk.n.bytes, pk.n.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.e.bytes, pk.e.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(pk.e.bytes) > 3 {
|
||||
err = errors.UnsupportedError("large public exponent")
|
||||
return
|
||||
}
|
||||
rsa := &rsa.PublicKey{
|
||||
N: new(big.Int).SetBytes(pk.n.bytes),
|
||||
E: 0,
|
||||
}
|
||||
for i := 0; i < len(pk.e.bytes); i++ {
|
||||
rsa.E <<= 8
|
||||
rsa.E |= int(pk.e.bytes[i])
|
||||
}
|
||||
pk.PublicKey = rsa
|
||||
return
|
||||
}
|
||||
|
||||
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseDSA(r io.Reader) (err error) {
|
||||
pk.p.bytes, pk.p.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.q.bytes, pk.q.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.g.bytes, pk.g.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.y.bytes, pk.y.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dsa := new(dsa.PublicKey)
|
||||
dsa.P = new(big.Int).SetBytes(pk.p.bytes)
|
||||
dsa.Q = new(big.Int).SetBytes(pk.q.bytes)
|
||||
dsa.G = new(big.Int).SetBytes(pk.g.bytes)
|
||||
dsa.Y = new(big.Int).SetBytes(pk.y.bytes)
|
||||
pk.PublicKey = dsa
|
||||
return
|
||||
}
|
||||
|
||||
// parseElGamal parses ElGamal public key material from the given Reader. See
|
||||
// RFC 4880, section 5.5.2.
|
||||
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) {
|
||||
pk.p.bytes, pk.p.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.g.bytes, pk.g.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.y.bytes, pk.y.bitLength, err = readMPI(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
elgamal := new(elgamal.PublicKey)
|
||||
elgamal.P = new(big.Int).SetBytes(pk.p.bytes)
|
||||
elgamal.G = new(big.Int).SetBytes(pk.g.bytes)
|
||||
elgamal.Y = new(big.Int).SetBytes(pk.y.bytes)
|
||||
pk.PublicKey = elgamal
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
||||
// The prefix is used when calculating a signature over this public key. See
|
||||
// RFC 4880, section 5.2.4.
|
||||
func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) {
|
||||
var pLength uint16
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
pLength += 2 + uint16(len(pk.n.bytes))
|
||||
pLength += 2 + uint16(len(pk.e.bytes))
|
||||
case PubKeyAlgoDSA:
|
||||
pLength += 2 + uint16(len(pk.p.bytes))
|
||||
pLength += 2 + uint16(len(pk.q.bytes))
|
||||
pLength += 2 + uint16(len(pk.g.bytes))
|
||||
pLength += 2 + uint16(len(pk.y.bytes))
|
||||
case PubKeyAlgoElGamal:
|
||||
pLength += 2 + uint16(len(pk.p.bytes))
|
||||
pLength += 2 + uint16(len(pk.g.bytes))
|
||||
pLength += 2 + uint16(len(pk.y.bytes))
|
||||
case PubKeyAlgoECDSA:
|
||||
pLength += uint16(pk.ec.byteLen())
|
||||
case PubKeyAlgoECDH:
|
||||
pLength += uint16(pk.ec.byteLen())
|
||||
pLength += uint16(pk.ecdh.byteLen())
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
pLength += 6
|
||||
h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
|
||||
length := 6 // 6 byte header
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
length += 2 + len(pk.n.bytes)
|
||||
length += 2 + len(pk.e.bytes)
|
||||
case PubKeyAlgoDSA:
|
||||
length += 2 + len(pk.p.bytes)
|
||||
length += 2 + len(pk.q.bytes)
|
||||
length += 2 + len(pk.g.bytes)
|
||||
length += 2 + len(pk.y.bytes)
|
||||
case PubKeyAlgoElGamal:
|
||||
length += 2 + len(pk.p.bytes)
|
||||
length += 2 + len(pk.g.bytes)
|
||||
length += 2 + len(pk.y.bytes)
|
||||
case PubKeyAlgoECDSA:
|
||||
length += pk.ec.byteLen()
|
||||
case PubKeyAlgoECDH:
|
||||
length += pk.ec.byteLen()
|
||||
length += pk.ecdh.byteLen()
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
|
||||
packetType := packetTypePublicKey
|
||||
if pk.IsSubkey {
|
||||
packetType = packetTypePublicSubkey
|
||||
}
|
||||
err = serializeHeader(w, packetType, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
|
||||
// OpenPGP public key packet, not including the packet header.
|
||||
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
||||
var buf [6]byte
|
||||
buf[0] = 4
|
||||
t := uint32(pk.CreationTime.Unix())
|
||||
buf[1] = byte(t >> 24)
|
||||
buf[2] = byte(t >> 16)
|
||||
buf[3] = byte(t >> 8)
|
||||
buf[4] = byte(t)
|
||||
buf[5] = byte(pk.PubKeyAlgo)
|
||||
|
||||
_, err = w.Write(buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
return writeMPIs(w, pk.n, pk.e)
|
||||
case PubKeyAlgoDSA:
|
||||
return writeMPIs(w, pk.p, pk.q, pk.g, pk.y)
|
||||
case PubKeyAlgoElGamal:
|
||||
return writeMPIs(w, pk.p, pk.g, pk.y)
|
||||
case PubKeyAlgoECDSA:
|
||||
return pk.ec.serialize(w)
|
||||
case PubKeyAlgoECDH:
|
||||
if err = pk.ec.serialize(w); err != nil {
|
||||
return
|
||||
}
|
||||
return pk.ecdh.serialize(w)
|
||||
}
|
||||
return errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
|
||||
// CanSign returns true iff this public key can generate signatures
|
||||
func (pk *PublicKey) CanSign() bool {
|
||||
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal
|
||||
}
|
||||
|
||||
// VerifySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) {
|
||||
if !pk.CanSign() {
|
||||
return errors.InvalidArgumentError("public key cannot generate signatures")
|
||||
}
|
||||
|
||||
signed.Write(sig.HashSuffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
|
||||
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("public key and signature use different algorithms")
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
|
||||
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes))
|
||||
if err != nil {
|
||||
return errors.SignatureError("RSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoDSA:
|
||||
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
|
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8
|
||||
if len(hashBytes) > subgroupSize {
|
||||
hashBytes = hashBytes[:subgroupSize]
|
||||
}
|
||||
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) {
|
||||
return errors.SignatureError("DSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoECDSA:
|
||||
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey)
|
||||
if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) {
|
||||
return errors.SignatureError("ECDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.SignatureError("Unsupported public key algorithm used in signature")
|
||||
}
|
||||
}
|
||||
|
||||
// VerifySignatureV3 returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) {
|
||||
if !pk.CanSign() {
|
||||
return errors.InvalidArgumentError("public key cannot generate signatures")
|
||||
}
|
||||
|
||||
suffix := make([]byte, 5)
|
||||
suffix[0] = byte(sig.SigType)
|
||||
binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix()))
|
||||
signed.Write(suffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
|
||||
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("public key and signature use different algorithms")
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
|
||||
if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil {
|
||||
return errors.SignatureError("RSA verification failure")
|
||||
}
|
||||
return
|
||||
case PubKeyAlgoDSA:
|
||||
dsaPublicKey := pk.PublicKey.(*dsa.PublicKey)
|
||||
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
|
||||
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8
|
||||
if len(hashBytes) > subgroupSize {
|
||||
hashBytes = hashBytes[:subgroupSize]
|
||||
}
|
||||
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) {
|
||||
return errors.SignatureError("DSA verification failure")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
panic("shouldn't happen")
|
||||
}
|
||||
}
|
||||
|
||||
// keySignatureHash returns a Hash of the message that needs to be signed for
|
||||
// pk to assert a subkey relationship to signed.
|
||||
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
signed.SerializeSignaturePrefix(h)
|
||||
signed.serializeWithoutHeaders(h)
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of signed.
|
||||
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error {
|
||||
h, err := keySignatureHash(pk, signed, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = pk.VerifySignature(h, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sig.FlagSign {
|
||||
// Signing subkeys must be cross-signed. See
|
||||
// https://www.gnupg.org/faq/subkey-cross-certify.html.
|
||||
if sig.EmbeddedSignature == nil {
|
||||
return errors.StructuralError("signing subkey is missing cross-signature")
|
||||
}
|
||||
// Verify the cross-signature. This is calculated over the same
|
||||
// data as the main signature, so we cannot just recursively
|
||||
// call signed.VerifyKeySignature(...)
|
||||
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil {
|
||||
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
|
||||
}
|
||||
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
|
||||
return errors.StructuralError("error while verifying cross-signature: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key.
|
||||
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
|
||||
h, err := keyRevocationHash(pk, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// userIdSignatureHash returns a Hash of the message that needs to be signed
|
||||
// to assert that pk is a valid key for id.
|
||||
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
|
||||
var buf [5]byte
|
||||
buf[0] = 0xb4
|
||||
buf[1] = byte(len(id) >> 24)
|
||||
buf[2] = byte(len(id) >> 16)
|
||||
buf[3] = byte(len(id) >> 8)
|
||||
buf[4] = byte(len(id))
|
||||
h.Write(buf[:])
|
||||
h.Write([]byte(id))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) {
|
||||
h, err := userIdSignatureHash(id, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKey) VerifyUserIdSignatureV3(id string, pub *PublicKey, sig *SignatureV3) (err error) {
|
||||
h, err := userIdSignatureV3Hash(id, pub, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignatureV3(h, sig)
|
||||
}
|
||||
|
||||
// KeyIdString returns the public key's fingerprint in capital hex
|
||||
// (e.g. "6C7EE1B8621CC013").
|
||||
func (pk *PublicKey) KeyIdString() string {
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
|
||||
}
|
||||
|
||||
// KeyIdShortString returns the short form of public key's fingerprint
|
||||
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
||||
func (pk *PublicKey) KeyIdShortString() string {
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
|
||||
}
|
||||
|
||||
// A parsedMPI is used to store the contents of a big integer, along with the
|
||||
// bit length that was specified in the original input. This allows the MPI to
|
||||
// be reserialized exactly.
|
||||
type parsedMPI struct {
|
||||
bytes []byte
|
||||
bitLength uint16
|
||||
}
|
||||
|
||||
// writeMPIs is a utility function for serializing several big integers to the
|
||||
// given Writer.
|
||||
func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) {
|
||||
for _, mpi := range mpis {
|
||||
err = writeMPI(w, mpi.bitLength, mpi.bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BitLength returns the bit length for the given public key.
|
||||
func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
bitLength = pk.n.bitLength
|
||||
case PubKeyAlgoDSA:
|
||||
bitLength = pk.p.bitLength
|
||||
case PubKeyAlgoElGamal:
|
||||
bitLength = pk.p.bitLength
|
||||
default:
|
||||
err = errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
return
|
||||
}
|
||||
279
vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go
generated
vendored
279
vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go
generated
vendored
@@ -1,279 +0,0 @@
|
||||
// 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 packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/md5"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and
|
||||
// should not be used for signing or encrypting. They are supported here only for
|
||||
// parsing version 3 key material and validating signatures.
|
||||
// See RFC 4880, section 5.5.2.
|
||||
type PublicKeyV3 struct {
|
||||
CreationTime time.Time
|
||||
DaysToExpire uint16
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
PublicKey *rsa.PublicKey
|
||||
Fingerprint [16]byte
|
||||
KeyId uint64
|
||||
IsSubkey bool
|
||||
|
||||
n, e parsedMPI
|
||||
}
|
||||
|
||||
// newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey.
|
||||
// Included here for testing purposes only. RFC 4880, section 5.5.2:
|
||||
// "an implementation MUST NOT generate a V3 key, but MAY accept it."
|
||||
func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3 {
|
||||
pk := &PublicKeyV3{
|
||||
CreationTime: creationTime,
|
||||
PublicKey: pub,
|
||||
n: fromBig(pub.N),
|
||||
e: fromBig(big.NewInt(int64(pub.E))),
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PublicKeyV3) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [8]byte
|
||||
if _, err = readFull(r, buf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] < 2 || buf[0] > 3 {
|
||||
return errors.UnsupportedError("public key version")
|
||||
}
|
||||
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
|
||||
pk.DaysToExpire = binary.BigEndian.Uint16(buf[5:7])
|
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[7])
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
err = pk.parseRSA(r)
|
||||
default:
|
||||
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pk.setFingerPrintAndKeyId()
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKeyV3) setFingerPrintAndKeyId() {
|
||||
// RFC 4880, section 12.2
|
||||
fingerPrint := md5.New()
|
||||
fingerPrint.Write(pk.n.bytes)
|
||||
fingerPrint.Write(pk.e.bytes)
|
||||
fingerPrint.Sum(pk.Fingerprint[:0])
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:])
|
||||
}
|
||||
|
||||
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) {
|
||||
if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil {
|
||||
return
|
||||
}
|
||||
if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// RFC 4880 Section 12.2 requires the low 8 bytes of the
|
||||
// modulus to form the key id.
|
||||
if len(pk.n.bytes) < 8 {
|
||||
return errors.StructuralError("v3 public key modulus is too short")
|
||||
}
|
||||
if len(pk.e.bytes) > 3 {
|
||||
err = errors.UnsupportedError("large public exponent")
|
||||
return
|
||||
}
|
||||
rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)}
|
||||
for i := 0; i < len(pk.e.bytes); i++ {
|
||||
rsa.E <<= 8
|
||||
rsa.E |= int(pk.e.bytes[i])
|
||||
}
|
||||
pk.PublicKey = rsa
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
||||
// The prefix is used when calculating a signature over this public key. See
|
||||
// RFC 4880, section 5.2.4.
|
||||
func (pk *PublicKeyV3) SerializeSignaturePrefix(w io.Writer) {
|
||||
var pLength uint16
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
pLength += 2 + uint16(len(pk.n.bytes))
|
||||
pLength += 2 + uint16(len(pk.e.bytes))
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
pLength += 6
|
||||
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKeyV3) Serialize(w io.Writer) (err error) {
|
||||
length := 8 // 8 byte header
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
length += 2 + len(pk.n.bytes)
|
||||
length += 2 + len(pk.e.bytes)
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
|
||||
packetType := packetTypePublicKey
|
||||
if pk.IsSubkey {
|
||||
packetType = packetTypePublicSubkey
|
||||
}
|
||||
if err = serializeHeader(w, packetType, length); err != nil {
|
||||
return
|
||||
}
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
|
||||
// OpenPGP public key packet, not including the packet header.
|
||||
func (pk *PublicKeyV3) serializeWithoutHeaders(w io.Writer) (err error) {
|
||||
var buf [8]byte
|
||||
// Version 3
|
||||
buf[0] = 3
|
||||
// Creation time
|
||||
t := uint32(pk.CreationTime.Unix())
|
||||
buf[1] = byte(t >> 24)
|
||||
buf[2] = byte(t >> 16)
|
||||
buf[3] = byte(t >> 8)
|
||||
buf[4] = byte(t)
|
||||
// Days to expire
|
||||
buf[5] = byte(pk.DaysToExpire >> 8)
|
||||
buf[6] = byte(pk.DaysToExpire)
|
||||
// Public key algorithm
|
||||
buf[7] = byte(pk.PubKeyAlgo)
|
||||
|
||||
if _, err = w.Write(buf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
return writeMPIs(w, pk.n, pk.e)
|
||||
}
|
||||
return errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
|
||||
// CanSign returns true iff this public key can generate signatures
|
||||
func (pk *PublicKeyV3) CanSign() bool {
|
||||
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly
|
||||
}
|
||||
|
||||
// VerifySignatureV3 returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKeyV3) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) {
|
||||
if !pk.CanSign() {
|
||||
return errors.InvalidArgumentError("public key cannot generate signatures")
|
||||
}
|
||||
|
||||
suffix := make([]byte, 5)
|
||||
suffix[0] = byte(sig.SigType)
|
||||
binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix()))
|
||||
signed.Write(suffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
|
||||
if pk.PubKeyAlgo != sig.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("public key and signature use different algorithms")
|
||||
}
|
||||
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
|
||||
if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil {
|
||||
return errors.SignatureError("RSA verification failure")
|
||||
}
|
||||
return
|
||||
default:
|
||||
// V3 public keys only support RSA.
|
||||
panic("shouldn't happen")
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKeyV3) VerifyUserIdSignatureV3(id string, pub *PublicKeyV3, sig *SignatureV3) (err error) {
|
||||
h, err := userIdSignatureV3Hash(id, pk, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignatureV3(h, sig)
|
||||
}
|
||||
|
||||
// VerifyKeySignatureV3 returns nil iff sig is a valid signature, made by this
|
||||
// public key, of signed.
|
||||
func (pk *PublicKeyV3) VerifyKeySignatureV3(signed *PublicKeyV3, sig *SignatureV3) (err error) {
|
||||
h, err := keySignatureHash(pk, signed, sig.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignatureV3(h, sig)
|
||||
}
|
||||
|
||||
// userIdSignatureV3Hash returns a Hash of the message that needs to be signed
|
||||
// to assert that pk is a valid key for id.
|
||||
func userIdSignatureV3Hash(id string, pk signingKey, hfn crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hfn.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hfn.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
|
||||
h.Write([]byte(id))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// KeyIdString returns the public key's fingerprint in capital hex
|
||||
// (e.g. "6C7EE1B8621CC013").
|
||||
func (pk *PublicKeyV3) KeyIdString() string {
|
||||
return fmt.Sprintf("%X", pk.KeyId)
|
||||
}
|
||||
|
||||
// KeyIdShortString returns the short form of public key's fingerprint
|
||||
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
||||
func (pk *PublicKeyV3) KeyIdShortString() string {
|
||||
return fmt.Sprintf("%X", pk.KeyId&0xFFFFFFFF)
|
||||
}
|
||||
|
||||
// BitLength returns the bit length for the given public key.
|
||||
func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) {
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
bitLength = pk.n.bitLength
|
||||
default:
|
||||
err = errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
return
|
||||
}
|
||||
76
vendor/golang.org/x/crypto/openpgp/packet/reader.go
generated
vendored
76
vendor/golang.org/x/crypto/openpgp/packet/reader.go
generated
vendored
@@ -1,76 +0,0 @@
|
||||
// Copyright 2011 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 packet
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/openpgp/errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
|
||||
// that they result from the next call to Next.
|
||||
type Reader struct {
|
||||
q []Packet
|
||||
readers []io.Reader
|
||||
}
|
||||
|
||||
// New io.Readers are pushed when a compressed or encrypted packet is processed
|
||||
// and recursively treated as a new source of packets. However, a carefully
|
||||
// crafted packet can trigger an infinite recursive sequence of packets. See
|
||||
// http://mumble.net/~campbell/misc/pgp-quine
|
||||
// https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4402
|
||||
// This constant limits the number of recursive packets that may be pushed.
|
||||
const maxReaders = 32
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown packet types are skipped.
|
||||
func (r *Reader) Next() (p Packet, err error) {
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
|
||||
for len(r.readers) > 0 {
|
||||
p, err = Read(r.readers[len(r.readers)-1])
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); !ok {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Push causes the Reader to start reading from a new io.Reader. When an EOF
|
||||
// error is seen from the new io.Reader, it is popped and the Reader continues
|
||||
// to read from the next most recent io.Reader. Push returns a StructuralError
|
||||
// if pushing the reader would exceed the maximum recursion level, otherwise it
|
||||
// returns nil.
|
||||
func (r *Reader) Push(reader io.Reader) (err error) {
|
||||
if len(r.readers) >= maxReaders {
|
||||
return errors.StructuralError("too many layers of packets")
|
||||
}
|
||||
r.readers = append(r.readers, reader)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unread causes the given Packet to be returned from the next call to Next.
|
||||
func (r *Reader) Unread(p Packet) {
|
||||
r.q = append(r.q, p)
|
||||
}
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
q: nil,
|
||||
readers: []io.Reader{r},
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user