1package imapserver
2
3import (
4 "fmt"
5 "io"
6 "strings"
7
8 "github.com/mjl-/mox/mlog"
9)
10
11type token interface {
12 pack(c *conn) string
13 xwriteTo(c *conn, xw io.Writer) // Writes to xw panic on error.
14}
15
16type bare string
17
18func (t bare) pack(c *conn) string {
19 return string(t)
20}
21
22func (t bare) xwriteTo(c *conn, xw io.Writer) {
23 xw.Write([]byte(t.pack(c)))
24}
25
26type niltoken struct{}
27
28var nilt niltoken
29
30func (t niltoken) pack(c *conn) string {
31 return "NIL"
32}
33
34func (t niltoken) xwriteTo(c *conn, xw io.Writer) {
35 xw.Write([]byte(t.pack(c)))
36}
37
38func nilOrString(s *string) token {
39 if s == nil {
40 return nilt
41 }
42 return string0(*s)
43}
44
45type string0 string
46
47// ../rfc/9051:7081
48// ../rfc/9051:6856 ../rfc/6855:153
49func (t string0) pack(c *conn) string {
50 var r strings.Builder
51 r.WriteString(`"`)
52 for _, ch := range t {
53 if ch == '\x00' || ch == '\r' || ch == '\n' || ch > 0x7f && !c.utf8strings() {
54 return syncliteral(t).pack(c)
55 }
56 if ch == '\\' || ch == '"' {
57 r.WriteString(`\`)
58 }
59 r.WriteString(string(ch))
60 }
61 r.WriteString(`"`)
62 return r.String()
63}
64
65func (t string0) xwriteTo(c *conn, xw io.Writer) {
66 xw.Write([]byte(t.pack(c)))
67}
68
69type dquote string
70
71func (t dquote) pack(c *conn) string {
72 var r strings.Builder
73 r.WriteString(`"`)
74 for _, c := range t {
75 if c == '\\' || c == '"' {
76 r.WriteString(`\`)
77 }
78 r.WriteString(string(c))
79 }
80 r.WriteString(`"`)
81 return r.String()
82}
83
84func (t dquote) xwriteTo(c *conn, xw io.Writer) {
85 xw.Write([]byte(t.pack(c)))
86}
87
88type syncliteral string
89
90func (t syncliteral) pack(c *conn) string {
91 return fmt.Sprintf("{%d}\r\n", len(t)) + string(t)
92}
93
94func (t syncliteral) xwriteTo(c *conn, xw io.Writer) {
95 fmt.Fprintf(xw, "{%d}\r\n", len(t))
96 xw.Write([]byte(t))
97}
98
99// data from reader with known size.
100type readerSizeSyncliteral struct {
101 r io.Reader
102 size int64
103 lit8 bool
104}
105
106func (t readerSizeSyncliteral) pack(c *conn) string {
107 buf, err := io.ReadAll(t.r)
108 if err != nil {
109 panic(err)
110 }
111 var lit string
112 if t.lit8 {
113 lit = "~"
114 }
115 return fmt.Sprintf("%s{%d}\r\n", lit, t.size) + string(buf)
116}
117
118func (t readerSizeSyncliteral) xwriteTo(c *conn, xw io.Writer) {
119 var lit string
120 if t.lit8 {
121 lit = "~"
122 }
123 fmt.Fprintf(xw, "%s{%d}\r\n", lit, t.size)
124 defer c.xtracewrite(mlog.LevelTracedata)()
125 if _, err := io.Copy(xw, io.LimitReader(t.r, t.size)); err != nil {
126 panic(err)
127 }
128}
129
130// data from reader without known size.
131type readerSyncliteral struct {
132 r io.Reader
133}
134
135func (t readerSyncliteral) pack(c *conn) string {
136 buf, err := io.ReadAll(t.r)
137 if err != nil {
138 panic(err)
139 }
140 return fmt.Sprintf("{%d}\r\n", len(buf)) + string(buf)
141}
142
143func (t readerSyncliteral) xwriteTo(c *conn, xw io.Writer) {
144 buf, err := io.ReadAll(t.r)
145 if err != nil {
146 panic(err)
147 }
148 fmt.Fprintf(xw, "{%d}\r\n", len(buf))
149 defer c.xtracewrite(mlog.LevelTracedata)()
150 xw.Write(buf)
151}
152
153// list with tokens space-separated
154type listspace []token
155
156func (t listspace) pack(c *conn) string {
157 var s strings.Builder
158 s.WriteString("(")
159 for i, e := range t {
160 if i > 0 {
161 s.WriteString(" ")
162 }
163 s.WriteString(e.pack(c))
164 }
165 s.WriteString(")")
166 return s.String()
167}
168
169func (t listspace) xwriteTo(c *conn, xw io.Writer) {
170 fmt.Fprint(xw, "(")
171 for i, e := range t {
172 if i > 0 {
173 fmt.Fprint(xw, " ")
174 }
175 e.xwriteTo(c, xw)
176 }
177 fmt.Fprint(xw, ")")
178}
179
180// concatenate tokens space-separated
181type concatspace []token
182
183func (t concatspace) pack(c *conn) string {
184 var s strings.Builder
185 for i, e := range t {
186 if i > 0 {
187 s.WriteString(" ")
188 }
189 s.WriteString(e.pack(c))
190 }
191 return s.String()
192}
193
194func (t concatspace) xwriteTo(c *conn, xw io.Writer) {
195 for i, e := range t {
196 if i > 0 {
197 fmt.Fprint(xw, " ")
198 }
199 e.xwriteTo(c, xw)
200 }
201}
202
203// Concatenated tokens, no spaces or list syntax.
204type concat []token
205
206func (t concat) pack(c *conn) string {
207 var s strings.Builder
208 for _, e := range t {
209 s.WriteString(e.pack(c))
210 }
211 return s.String()
212}
213
214func (t concat) xwriteTo(c *conn, xw io.Writer) {
215 for _, e := range t {
216 e.xwriteTo(c, xw)
217 }
218}
219
220type astring string
221
222func (t astring) pack(c *conn) string {
223 if len(t) == 0 {
224 return string0(t).pack(c)
225 }
226next:
227 for _, ch := range t {
228 for _, x := range atomChar {
229 if ch == x {
230 continue next
231 }
232 }
233 return string0(t).pack(c)
234 }
235 return string(t)
236}
237
238func (t astring) xwriteTo(c *conn, xw io.Writer) {
239 xw.Write([]byte(t.pack(c)))
240}
241
242// mailbox with utf7 encoding if connection requires it, or utf8 otherwise.
243type mailboxt string
244
245func (t mailboxt) pack(c *conn) string {
246 s := string(t)
247 if !c.utf8strings() {
248 s = utf7encode(s)
249 }
250 return astring(s).pack(c)
251}
252
253func (t mailboxt) xwriteTo(c *conn, xw io.Writer) {
254 xw.Write([]byte(t.pack(c)))
255}
256
257type number uint32
258
259func (t number) pack(c *conn) string {
260 return fmt.Sprintf("%d", t)
261}
262
263func (t number) xwriteTo(c *conn, xw io.Writer) {
264 xw.Write([]byte(t.pack(c)))
265}
266