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