Mercurial > projects > managesieve
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 |
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 } |