annotate managesieve.go @ 12:66b46b3d73be default tip

Handle capabilities sent by the server after negotiating a SASL security layer
author Guido Berhoerster <guido+managesieve@berhoerster.name>
date Tue, 09 Feb 2021 23:01:02 +0100
parents b790df0733d4
children
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 {
11
b790df0733d4 Fix typos
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 8
diff changeset
89 msg := "the server has closed the connection"
0
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 }
8
75a4ee940f36 Fix SRV record lookup
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 4
diff changeset
111 services := make([]string, 0, len(addrs))
0
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
3
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
187 // Implementation returns the name and version of the implementation as
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
188 // reported by the server.
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
189 func (c *Client) Implementation() string {
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
190 return c.capa["IMPLEMENTATION"]
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
191 }
8413916df2be Add method to query the implementation string
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 0
diff changeset
192
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
193 // SupportsRFC5804 returns true if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
194 func (c *Client) SupportsRFC5804() bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
195 _, ok := c.capa["VERSION"]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
196 return ok
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
197 }
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 // SupportsTLS returns true if the server supports TLS connections via the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
200 // STARTTLS command.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
201 func (c *Client) SupportsTLS() bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
202 _, ok := c.capa["STARTTLS"]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
203 return ok
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
206 // Extensions returns the Sieve script extensions supported by the Sieve engine.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
207 func (c *Client) Extensions() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
208 return strings.Fields(c.capa["SIEVE"])
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
209 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
210
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
211 // MaxRedirects returns the limit on the number of Sieve "redirect" during a
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
212 // single evaluation.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
213 func (c *Client) MaxRedirects() int {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
214 n, err := strconv.ParseUint(c.capa["MAXREDIRECTS"], 10, 32)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
215 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
216 return 0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
217 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
218 return int(n)
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 // NotifyMethods returns the URI schema parts for supported notification
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
222 // methods.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
223 func (c *Client) NotifyMethods() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
224 return strings.Fields(c.capa["NOTIFY"])
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
225 }
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 // SASLMechanisms returns the SASL authentication mechanism supported by the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
228 // server. This may change depending on whether a TLS connection is used.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
229 func (c *Client) SASLMechanisms() []string {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
230 splitFunc := func(r rune) bool {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
231 return r == ' '
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
232 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
233 return strings.FieldsFunc(c.capa["SASL"], splitFunc)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
234 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
235
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
236 func (c *Client) cmd(args ...interface{}) (*reply, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
237 // write each arg separated by a space and terminated by CR+LF
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
238 for i, arg := range args {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
239 if i > 0 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
240 if _, err := c.conn.Write([]byte{' '}); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
241 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
242 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
243 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
244 if _, err := fmt.Fprint(c.conn, arg); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
245 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
246 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
247 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
248 if _, err := c.conn.Write([]byte("\r\n")); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
249 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
250 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
251
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
252 r, err := c.p.readReply()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
253 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
254 return nil, err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
255 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
256 if r.resp == responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
257 return r, &ServerError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
258 } else if r.resp == responseBye {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
259 return r, &ConnClosedError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
260 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
261 return r, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
262 }
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 // StartTLS upgrades the connection to use TLS encryption based on the given
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
265 // configuration.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
266 func (c *Client) StartTLS(config *tls.Config) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
267 if _, ok := c.conn.(*tls.Conn); ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
268 return ProtocolError("already using a TLS connection")
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 c.isAuth {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
271 return ProtocolError("cannot STARTTLS in authenticated state")
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 if _, ok := c.capa["STARTTLS"]; !ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
274 return NotSupportedError("STARTTLS")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
275 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
276 if _, err := c.cmd("STARTTLS"); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
277 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
278 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
279 c.conn = tls.Client(c.conn, config)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
280 s := newScanner(c.conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
281 c.p = &parser{s}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
282
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
283 r, err := c.p.readReply()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
284 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
285 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
286 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
287 // capabilities are no longer valid if STARTTLS succeeded
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
288 c.capa, err = parseCapabilities(r)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
289 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
290 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
291
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
292 // TLSConnectionState returns the ConnectionState of the current TLS
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
293 // connection.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
294 func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
295 tc, ok := c.conn.(*tls.Conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
296 if !ok {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
297 return
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
298 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
299 return tc.ConnectionState(), ok
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
300 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
301
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
302 // Authenticate authenticates a client using the given authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
303 // mechanism. In case of an AuthenticationError the client remains in a defined
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
304 // state and can continue to be used.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
305 func (c *Client) Authenticate(a Auth) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
306 encoding := base64.StdEncoding
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
307 _, isTLS := c.conn.(*tls.Conn)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
308 info := &ServerInfo{c.serverName, isTLS, c.SASLMechanisms()}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
309 mech, resp, err := a.Start(info)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
310 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
311 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
312 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
313 if _, err = fmt.Fprintf(c.conn, "AUTHENTICATE \"%s\" \"%s\"\r\n",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
314 mech, encoding.EncodeToString(resp)); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
315 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
316 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
317
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
318 var line []*token
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
319 // handle SASL challenge-response messages exchanged as base64-encoded
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
320 // strings until the server sends a MANAGESIEVE response which may
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
321 // contain some final SASL data
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
322 for {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
323 line, err = c.p.readLine()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
324 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
325 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
326 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
327
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
328 if c.p.isResponseLine(line) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
329 break
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
330 } else if len(line) != 1 ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
331 (line[0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
332 line[0].typ != tokenLiteralString) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
333 return ParserError("failed to parse SASL data: expected string")
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 msg, err := encoding.DecodeString(line[0].literal)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
336 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
337 return ParserError("failed to decode SASL data: " +
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
338 err.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
339 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
340
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
341 // perform next step in authentication process
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
342 resp, authErr := a.Next(msg, true)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
343 if authErr != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
344 // this error should be recoverable, abort
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
345 // authentication
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
346 if _, err := fmt.Fprintf(c.conn, "\"*\"\r\n"); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
347 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
348 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
349
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
350 line, err = c.p.readLine()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
351 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
352 return err
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 if r, err := c.p.parseResponseLine(line); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
355 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
356 } else if r.resp != responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
357 return ProtocolError("invalid response to aborted authentication: expected NO")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
358 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
359
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
360 return AuthenticationError(authErr.Error())
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 // send SASL response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
364 if _, err := fmt.Fprintf(c.conn, "\"%s\"\r\n",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
365 encoding.EncodeToString(resp)); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
366 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
367 }
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
370 // handle MANAGESIEVE response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
371 r, err := c.p.parseResponseLine(line)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
372 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
373 return err
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 if r.resp == responseNo {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
376 return AuthenticationError(r.msg)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
377 } else if r.resp == responseBye {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
378 return &ConnClosedError{r.code, r.msg}
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
379 }
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 // check for SASL response code with final SASL data as the response
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
382 // code argument
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
383 if r.code == "SASL" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
384 if len(r.codeArgs) != 1 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
385 return ParserError("failed to parse SASL code argument: expected a single argument")
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 msg64 := r.codeArgs[0]
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
388 msg, err := encoding.DecodeString(msg64)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
389 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
390 return ParserError("failed to decode SASL code argument: " + err.Error())
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
393 if _, err = a.Next(msg, false); err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
394 return AuthenticationError(err.Error())
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
395 }
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
12
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
398 if a.SASLSecurityLayer() {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
399 // server sends capabilities response
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
400 r, err := c.p.readReply()
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
401 if err != nil {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
402 return err
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
403 }
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
404 if r.resp == responseNo {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
405 return &ServerError{r.code, r.msg}
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
406 } else if r.resp == responseBye {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
407 return &ConnClosedError{r.code, r.msg}
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
408 }
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
409 } else {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
410 // capabilities are no longer valid after succesful
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
411 // authentication
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
412 r, err = c.cmd("CAPABILITY")
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
413 if err != nil {
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
414 return err
66b46b3d73be Handle capabilities sent by the server after negotiating a SASL security layer
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 11
diff changeset
415 }
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
416 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
417 c.capa, err = parseCapabilities(r)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
418 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
419 }
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 // 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
422 // 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
423 // 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
424 func (c *Client) HaveSpace(name string, size int64) (bool, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
425 if size < 0 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
426 return false,
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
427 ProtocolError(fmt.Sprintf("invalid script size: %d",
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
428 size))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
429 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
430 if size > math.MaxInt32 {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
431 return false, ProtocolError("script exceeds maximum size")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
432 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
433 r, err := c.cmd("HAVESPACE", quoteString(name), size)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
434 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
435 if r.code == "QUOTA" || r.code == "QUOTA/MAXSIZE" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
436 err = nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
437 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
438 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
439 return r.resp == responseOk, err
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
442 // 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
443 // already existing script with the same name will be replaced.
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
444 func (c *Client) PutScript(name, content string) (warnings string, err error) {
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
445 if !IsNetUnicode(name) {
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
446 err = ProtocolError("script name must comply with Net-Unicode")
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
447 return
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
448 }
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
449 r, err := c.cmd("PUTSCRIPT", quoteString(name), quoteString(content))
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
450 if err != nil {
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
451 return
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
452 }
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
453 if r.code == "WARNINGS" {
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
454 warnings = r.msg
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
455 }
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
456 return
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
457 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
458
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
459 // 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
460 // 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
461 // empty string.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
462 func (c *Client) ListScripts() ([]string, string, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
463 r, err := c.cmd("LISTSCRIPTS")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
464 if err != nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
465 return nil, "", err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
466 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
467
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
468 var scripts []string = make([]string, 0)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
469 var active string
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
470 for _, tokens := range r.lines {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
471 if tokens[0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
472 tokens[0].typ != tokenLiteralString {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
473 return nil, "", ParserError("failed to parse script list: expected string")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
474 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
475 switch len(tokens) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
476 case 2:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
477 if tokens[1].typ != tokenAtom ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
478 tokens[1].literal != "ACTIVE" {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
479 return nil, "", ParserError("failed to parse script list: expected atom ACTIVE")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
480 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
481 active = tokens[0].literal
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
482 fallthrough
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
483 case 1:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
484 scripts = append(scripts, tokens[0].literal)
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
485 default:
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
486 return nil, "", ParserError("failed to parse script list: trailing data")
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 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
489 return scripts, active, nil
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
490 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
491
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
492 // 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
493 // time, activating a script will deactivate the previously active script. If
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
494 // the name is the empty string the currently active script will be
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
495 // deactivated.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
496 func (c *Client) ActivateScript(name string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
497 _, err := c.cmd("SETACTIVE", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
498 return err
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
501 // GetScript returns the content of the script with the given name.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
502 func (c *Client) GetScript(name string) (string, error) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
503 r, err := c.cmd("GETSCRIPT", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
504 if err != nil {
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 if len(r.lines) != 1 ||
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
508 (r.lines[0][0].typ != tokenQuotedString &&
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
509 r.lines[0][0].typ != tokenLiteralString) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
510 return "", ParserError("failed to parse script: expected string")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
511 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
512 return r.lines[0][0].literal, nil
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
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
515 // DeleteScript deletes the script with the given name from the server.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
516 func (c *Client) DeleteScript(name string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
517 _, err := c.cmd("DELETESCRIPT", quoteString(name))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
518 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
519 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
520
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
521 // RenameScript renames a script on the server. This operation is only
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
522 // available if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
523 func (c *Client) RenameScript(oldName, newName string) error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
524 if !c.SupportsRFC5804() {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
525 return NotSupportedError("RENAMESCRIPT")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
526 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
527 if !IsNetUnicode(newName) {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
528 return ProtocolError("script name must comply with Net-Unicode")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
529 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
530 _, err := c.cmd("RENAMESCRIPT", quoteString(oldName),
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
531 quoteString(newName))
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
532 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
533 }
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 // CheckScript checks if the given script contains any errors. This operation
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
536 // is only available if the server conforms to RFC 5804.
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
537 func (c *Client) CheckScript(content string) (warnings string, err error) {
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
538 if !c.SupportsRFC5804() {
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
539 err = NotSupportedError("CHECKSCRIPT")
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
540 return
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
541 }
4
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
542 r, err := c.cmd("CHECKSCRIPT", quoteString(content))
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
543 if err != nil {
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
544 return
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
545 }
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
546 if r.code == "WARNINGS" {
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
547 warnings = r.msg
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
548 }
f9bb517e9447 Return warning messages from the CHECKSCRIPT and PUTSCRIPT commands
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents: 3
diff changeset
549 return
0
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
550 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
551
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
552 // 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
553 // 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
554 // available if the server conforms to RFC 5804.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
555 func (c *Client) Noop() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
556 if !c.SupportsRFC5804() {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
557 return NotSupportedError("NOOP")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
558 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
559 _, err := c.cmd("NOOP")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
560 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
561 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
562
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
563 // Close closes the connection to the server immediately without informing the
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
564 // remote end that the client has finished. Under normal circumstances Logout
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
565 // should be used instead.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
566 func (c *Client) Close() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
567 return c.conn.Close()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
568 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
569
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
570 // Logout first indicates to the server that the client is finished and
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
571 // subsequently closes the connection. No further commands can be sent after
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
572 // this.
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
573 func (c *Client) Logout() error {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
574 _, err := c.cmd("LOGOUT")
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
575 cerr := c.Close()
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
576 if err == nil {
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
577 err = cerr
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
578 }
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
579 return err
6369453d47a3 Initial revision
Guido Berhoerster <guido+managesieve@berhoerster.name>
parents:
diff changeset
580 }