annotate managesieve.go @ 0:6369453d47a3

Initial revision
author Guido Berhoerster <guido+managesieve@berhoerster.name>
date Thu, 15 Oct 2020 09:11:05 +0200
parents
children 8413916df2be
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
1 // Copyright (C) 2020 Guido Berhoerster <guido+managesieve@berhoerster.name>
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
2 //
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
3 // Permission is hereby granted, free of charge, to any person obtaining
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
4 // a copy of this software and associated documentation files (the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
5 // "Software"), to deal in the Software without restriction, including
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
6 // without limitation the rights to use, copy, modify, merge, publish,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
7 // distribute, sublicense, and/or sell copies of the Software, and to
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
8 // permit persons to whom the Software is furnished to do so, subject to
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
9 // the following conditions:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
10 //
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
11 // The above copyright notice and this permission notice shall be included
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
12 // in all copies or substantial portions of the Software.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
13 //
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
17 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
18 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
19 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
20 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
21
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
22 // Package managesieve implements the MANAGESIEVE protocol as specified in
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
23 // RFC 5804. It covers all mandatory parts of the protocol with the exception
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
24 // of the SCRAM-SHA-1 SASL mechanism. Additional SASL authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
25 // mechanisms can be provided by consumers.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
26 package managesieve
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
27
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
28 import (
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
29 "crypto/tls"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
30 "encoding/base64"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
31 "fmt"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
32 "math"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
33 "net"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
34 "strconv"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
35 "strings"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
36 )
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
37
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
38 // ParserError represents a syntax error encountered while parsing a response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
39 // from the server.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
40 type ParserError string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
41
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
42 func (e ParserError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
43 return "parse error: " + string(e)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
44 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
45
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
46 // ProtocolError represents a MANAGESIEVE protocol violation.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
47 type ProtocolError string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
48
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
49 func (e ProtocolError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
50 return "protocol error: " + string(e)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
51 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
52
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
53 // NotSupportedError is returned if an operation requires an extension that is
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
54 // not available.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
55 type NotSupportedError string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
56
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
57 func (e NotSupportedError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
58 return "not supported: " + string(e)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
59 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
60
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
61 // AuthenticationError is returned if an authentication attempt has failed.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
62 type AuthenticationError string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
63
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
64 func (e AuthenticationError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
65 return "authentication failed: " + string(e)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
66 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
67
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
68 // A ServerError is represents an error returned by the server in the form of a
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
69 // NO response.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
70 type ServerError struct {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
71 Code string // optional response code of the error
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
72 Msg string // optional human readable error message
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
73 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
74
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
75 func (e *ServerError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
76 if e.Msg != "" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
77 return e.Msg
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
78 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
79 return "unspecified server error"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
80 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
81
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
82 // The ConnClosedError is returned if the server has closed the connection.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
83 type ConnClosedError struct {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
84 Code string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
85 Msg string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
86 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
87
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
88 func (e *ConnClosedError) Error() string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
89 msg := "the server has closed to connection"
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
90 if e.Msg != "" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
91 return msg + ": " + e.Msg
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
92 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
93 return msg
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
94 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
95
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
96 // Tries to look up the MANAGESIEVE SRV record for the domain and returns an
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
97 // slice of strings containing hostnames and ports. If no SRV record was found
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
98 // it falls back to the given domain name and port 4190.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
99 func LookupService(domain string) ([]string, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
100 _, addrs, err := net.LookupSRV("sieve", "tcp", domain)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
101 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
102 if dnserr, ok := err.(*net.DNSError); ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
103 if dnserr.IsNotFound {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
104 // no SRV record found, fall back to port 4190
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
105 services := [1]string{domain + ":4190"}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
106 return services[:], nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
107 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
108 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
109 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
110 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
111 services := make([]string, len(addrs))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
112 // addrs is already ordered by priority
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
113 for _, addr := range addrs {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
114 services = append(services,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
115 fmt.Sprintf("%s:%d", addr.Target, addr.Port))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
116 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
117 return services, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
118 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
119
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
120 // Checks whtether the given string conforms to the "Unicode Format for Network
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
121 // Interchange" specified in RFC 5198.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
122 func IsNetUnicode(s string) bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
123 for _, c := range s {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
124 if c <= 0x1f || (c >= 0x7f && c <= 0x9f) ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
125 c == 0x2028 || c == 0x2029 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
126 return false
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
127 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
128 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
129 return true
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
130 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
131
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
132 func quoteString(s string) string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
133 return fmt.Sprintf("{%d+}\r\n%s", len(s), s)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
134 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
135
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
136 // Client represents a client connection to a MANAGESIEVE server.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
137 type Client struct {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
138 conn net.Conn
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
139 p *parser
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
140 isAuth bool
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
141 capa map[string]string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
142 serverName string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
143 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
144
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
145 // Dial creates a new connection to a MANAGESIEVE server. The given addr must
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
146 // contain both a hostname or IP address and post.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
147 func Dial(addr string) (*Client, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
148 host, _, err := net.SplitHostPort(addr)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
149 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
150 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
151 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
152 conn, err := net.Dial("tcp", addr)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
153 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
154 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
155 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
156 return NewClient(conn, host)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
157 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
158
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
159 // NewClient create a new client based on an existing connection to a
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
160 // MANAGESIEVE server where host specifies the hostname of the remote end of
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
161 // the connection.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
162 func NewClient(conn net.Conn, host string) (*Client, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
163 s := newScanner(conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
164 p := &parser{s}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
165 c := &Client{
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
166 conn: conn,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
167 p: p,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
168 serverName: host,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
169 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
170
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
171 r, err := c.p.readReply()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
172 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
173 c.Close()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
174 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
175 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
176 switch r.resp {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
177 case responseOk:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
178 c.capa, err = parseCapabilities(r)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
179 case responseNo:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
180 return c, &ServerError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
181 case responseBye:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
182 return c, &ConnClosedError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
183 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
184 return c, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
185 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
186
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
187 // SupportsRFC5804 returns true if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
188 func (c *Client) SupportsRFC5804() bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
189 _, ok := c.capa["VERSION"]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
190 return ok
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
191 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
192
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
193 // SupportsTLS returns true if the server supports TLS connections via the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
194 // STARTTLS command.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
195 func (c *Client) SupportsTLS() bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
196 _, ok := c.capa["STARTTLS"]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
197 return ok
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
198 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
199
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
200 // Extensions returns the Sieve script extensions supported by the Sieve engine.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
201 func (c *Client) Extensions() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
202 return strings.Fields(c.capa["SIEVE"])
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
203 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
204
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
205 // MaxRedirects returns the limit on the number of Sieve "redirect" during a
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
206 // single evaluation.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
207 func (c *Client) MaxRedirects() int {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
208 n, err := strconv.ParseUint(c.capa["MAXREDIRECTS"], 10, 32)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
209 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
210 return 0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
211 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
212 return int(n)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
213 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
214
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
215 // NotifyMethods returns the URI schema parts for supported notification
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
216 // methods.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
217 func (c *Client) NotifyMethods() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
218 return strings.Fields(c.capa["NOTIFY"])
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
219 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
220
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
221 // SASLMechanisms returns the SASL authentication mechanism supported by the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
222 // server. This may change depending on whether a TLS connection is used.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
223 func (c *Client) SASLMechanisms() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
224 splitFunc := func(r rune) bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
225 return r == ' '
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
226 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
227 return strings.FieldsFunc(c.capa["SASL"], splitFunc)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
228 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
229
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
230 func (c *Client) cmd(args ...interface{}) (*reply, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
231 // write each arg separated by a space and terminated by CR+LF
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
232 for i, arg := range args {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
233 if i > 0 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
234 if _, err := c.conn.Write([]byte{' '}); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
235 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
236 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
237 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
238 if _, err := fmt.Fprint(c.conn, arg); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
239 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
240 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
241 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
242 if _, err := c.conn.Write([]byte("\r\n")); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
243 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
244 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
245
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
246 r, err := c.p.readReply()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
247 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
248 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
249 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
250 if r.resp == responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
251 return r, &ServerError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
252 } else if r.resp == responseBye {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
253 return r, &ConnClosedError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
254 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
255 return r, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
256 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
257
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
258 // StartTLS upgrades the connection to use TLS encryption based on the given
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
259 // configuration.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
260 func (c *Client) StartTLS(config *tls.Config) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
261 if _, ok := c.conn.(*tls.Conn); ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
262 return ProtocolError("already using a TLS connection")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
263 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
264 if c.isAuth {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
265 return ProtocolError("cannot STARTTLS in authenticated state")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
266 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
267 if _, ok := c.capa["STARTTLS"]; !ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
268 return NotSupportedError("STARTTLS")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
269 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
270 if _, err := c.cmd("STARTTLS"); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
271 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
272 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
273 c.conn = tls.Client(c.conn, config)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
274 s := newScanner(c.conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
275 c.p = &parser{s}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
276
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
277 r, err := c.p.readReply()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
278 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
279 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
280 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
281 // capabilities are no longer valid if STARTTLS succeeded
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
282 c.capa, err = parseCapabilities(r)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
283 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
284 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
285
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
286 // TLSConnectionState returns the ConnectionState of the current TLS
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
287 // connection.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
288 func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
289 tc, ok := c.conn.(*tls.Conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
290 if !ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
291 return
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
292 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
293 return tc.ConnectionState(), ok
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
294 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
295
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
296 // Authenticate authenticates a client using the given authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
297 // mechanism. In case of an AuthenticationError the client remains in a defined
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
298 // state and can continue to be used.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
299 func (c *Client) Authenticate(a Auth) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
300 encoding := base64.StdEncoding
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
301 _, isTLS := c.conn.(*tls.Conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
302 info := &ServerInfo{c.serverName, isTLS, c.SASLMechanisms()}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
303 mech, resp, err := a.Start(info)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
304 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
305 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
306 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
307 if _, err = fmt.Fprintf(c.conn, "AUTHENTICATE \"%s\" \"%s\"\r\n",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
308 mech, encoding.EncodeToString(resp)); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
309 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
310 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
311
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
312 var line []*token
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
313 // handle SASL challenge-response messages exchanged as base64-encoded
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
314 // strings until the server sends a MANAGESIEVE response which may
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
315 // contain some final SASL data
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
316 for {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
317 line, err = c.p.readLine()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
318 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
319 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
320 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
321
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
322 if c.p.isResponseLine(line) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
323 break
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
324 } else if len(line) != 1 ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
325 (line[0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
326 line[0].typ != tokenLiteralString) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
327 return ParserError("failed to parse SASL data: expected string")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
328 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
329 msg, err := encoding.DecodeString(line[0].literal)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
330 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
331 return ParserError("failed to decode SASL data: " +
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
332 err.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
333 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
334
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
335 // perform next step in authentication process
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
336 resp, authErr := a.Next(msg, true)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
337 if authErr != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
338 // this error should be recoverable, abort
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
339 // authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
340 if _, err := fmt.Fprintf(c.conn, "\"*\"\r\n"); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
341 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
342 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
343
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
344 line, err = c.p.readLine()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
345 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
346 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
347 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
348 if r, err := c.p.parseResponseLine(line); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
349 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
350 } else if r.resp != responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
351 return ProtocolError("invalid response to aborted authentication: expected NO")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
352 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
353
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
354 return AuthenticationError(authErr.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
355 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
356
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
357 // send SASL response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
358 if _, err := fmt.Fprintf(c.conn, "\"%s\"\r\n",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
359 encoding.EncodeToString(resp)); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
360 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
361 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
362 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
363
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
364 // handle MANAGESIEVE response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
365 r, err := c.p.parseResponseLine(line)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
366 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
367 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
368 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
369 if r.resp == responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
370 return AuthenticationError(r.msg)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
371 } else if r.resp == responseBye {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
372 return &ConnClosedError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
373 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
374
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
375 // check for SASL response code with final SASL data as the response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
376 // code argument
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
377 if r.code == "SASL" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
378 if len(r.codeArgs) != 1 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
379 return ParserError("failed to parse SASL code argument: expected a single argument")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
380 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
381 msg64 := r.codeArgs[0]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
382 msg, err := encoding.DecodeString(msg64)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
383 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
384 return ParserError("failed to decode SASL code argument: " + err.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
385 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
386
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
387 if _, err = a.Next(msg, false); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
388 return AuthenticationError(err.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
389 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
390 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
391
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
392 // capabilities are no longer valid after succesful authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
393 r, err = c.cmd("CAPABILITY")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
394 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
395 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
396 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
397 c.capa, err = parseCapabilities(r)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
398 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
399 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
400
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
401 // HaveSpace queries the server if there is sufficient space to store a script
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
402 // with the given name and size. An already existing script with the same name
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
403 // will be treated as if it were replaced with a script of the given size.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
404 func (c *Client) HaveSpace(name string, size int64) (bool, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
405 if size < 0 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
406 return false,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
407 ProtocolError(fmt.Sprintf("invalid script size: %d",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
408 size))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
409 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
410 if size > math.MaxInt32 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
411 return false, ProtocolError("script exceeds maximum size")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
412 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
413 r, err := c.cmd("HAVESPACE", quoteString(name), size)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
414 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
415 if r.code == "QUOTA" || r.code == "QUOTA/MAXSIZE" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
416 err = nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
417 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
418 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
419 return r.resp == responseOk, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
420 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
421
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
422 // PutScript stores the script content with the given name on the server. An
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
423 // already existing script with the same name will be replaced.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
424 func (c *Client) PutScript(name, content string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
425 if !IsNetUnicode(name) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
426 return ProtocolError("script name must comply with Net-Unicode")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
427 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
428 _, err := c.cmd("PUTSCRIPT", quoteString(name), quoteString(content))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
429 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
430 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
431
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
432 // ListScripts returns the names of all scripts on the server and the name of
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
433 // the currently active script. If there is no active script it returns the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
434 // empty string.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
435 func (c *Client) ListScripts() ([]string, string, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
436 r, err := c.cmd("LISTSCRIPTS")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
437 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
438 return nil, "", err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
439 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
440
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
441 var scripts []string = make([]string, 0)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
442 var active string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
443 for _, tokens := range r.lines {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
444 if tokens[0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
445 tokens[0].typ != tokenLiteralString {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
446 return nil, "", ParserError("failed to parse script list: expected string")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
447 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
448 switch len(tokens) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
449 case 2:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
450 if tokens[1].typ != tokenAtom ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
451 tokens[1].literal != "ACTIVE" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
452 return nil, "", ParserError("failed to parse script list: expected atom ACTIVE")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
453 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
454 active = tokens[0].literal
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
455 fallthrough
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
456 case 1:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
457 scripts = append(scripts, tokens[0].literal)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
458 default:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
459 return nil, "", ParserError("failed to parse script list: trailing data")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
460 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
461 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
462 return scripts, active, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
463 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
464
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
465 // ActivateScript activates a script. Only one script can be active at the same
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
466 // time, activating a script will deactivate the previously active script. If
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
467 // the name is the empty string the currently active script will be
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
468 // deactivated.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
469 func (c *Client) ActivateScript(name string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
470 _, err := c.cmd("SETACTIVE", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
471 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
472 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
473
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
474 // GetScript returns the content of the script with the given name.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
475 func (c *Client) GetScript(name string) (string, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
476 r, err := c.cmd("GETSCRIPT", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
477 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
478 return "", err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
479 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
480 if len(r.lines) != 1 ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
481 (r.lines[0][0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
482 r.lines[0][0].typ != tokenLiteralString) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
483 return "", ParserError("failed to parse script: expected string")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
484 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
485 return r.lines[0][0].literal, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
486 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
487
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
488 // DeleteScript deletes the script with the given name from the server.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
489 func (c *Client) DeleteScript(name string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
490 _, err := c.cmd("DELETESCRIPT", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
491 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
492 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
493
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
494 // RenameScript renames a script on the server. This operation is only
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
495 // available if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
496 func (c *Client) RenameScript(oldName, newName string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
497 if !c.SupportsRFC5804() {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
498 return NotSupportedError("RENAMESCRIPT")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
499 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
500 if !IsNetUnicode(newName) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
501 return ProtocolError("script name must comply with Net-Unicode")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
502 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
503 _, err := c.cmd("RENAMESCRIPT", quoteString(oldName),
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
504 quoteString(newName))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
505 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
506 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
507
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
508 // CheckScript checks if the given script contains any errors. This operation
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
509 // is only available if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
510 func (c *Client) CheckScript(content string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
511 if !c.SupportsRFC5804() {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
512 return NotSupportedError("CHECKSCRIPT")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
513 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
514 _, err := c.cmd("CHECKSCRIPT", quoteString(content))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
515 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
516 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
517
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
518 // Noop does nothing but contact the server and can be used to prevent timeouts
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
519 // and to check whether the connection is still alive. This operation is only
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
520 // available if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
521 func (c *Client) Noop() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
522 if !c.SupportsRFC5804() {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
523 return NotSupportedError("NOOP")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
524 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
525 _, err := c.cmd("NOOP")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
526 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
527 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
528
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
529 // Close closes the connection to the server immediately without informing the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
530 // remote end that the client has finished. Under normal circumstances Logout
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
531 // should be used instead.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
532 func (c *Client) Close() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
533 return c.conn.Close()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
534 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
535
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
536 // Logout first indicates to the server that the client is finished and
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
537 // subsequently closes the connection. No further commands can be sent after
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
538 // this.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
539 func (c *Client) Logout() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
540 _, err := c.cmd("LOGOUT")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
541 cerr := c.Close()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
542 if err == nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
543 err = cerr
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
544 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
545 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
546 }