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, w io.Writer)
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, w io.Writer) {
22 w.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, w io.Writer) {
34 w.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, w io.Writer) {
64 w.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, w io.Writer) {
82 w.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, w io.Writer) {
92 fmt.Fprintf(w, "{%d}\r\n", len(t))
93 w.Write([]byte(t))
94}
95
96// data from reader with known size.
97type readerSizeSyncliteral struct {
98 r io.Reader
99 size int64
100}
101
102func (t readerSizeSyncliteral) pack(c *conn) string {
103 buf, err := io.ReadAll(t.r)
104 if err != nil {
105 panic(err)
106 }
107 return fmt.Sprintf("{%d}\r\n", t.size) + string(buf)
108}
109
110func (t readerSizeSyncliteral) writeTo(c *conn, w io.Writer) {
111 fmt.Fprintf(w, "{%d}\r\n", t.size)
112 defer c.xtrace(mlog.LevelTracedata)()
113 if _, err := io.Copy(w, io.LimitReader(t.r, t.size)); err != nil {
114 panic(err)
115 }
116}
117
118// data from reader without known size.
119type readerSyncliteral struct {
120 r io.Reader
121}
122
123func (t readerSyncliteral) pack(c *conn) string {
124 buf, err := io.ReadAll(t.r)
125 if err != nil {
126 panic(err)
127 }
128 return fmt.Sprintf("{%d}\r\n", len(buf)) + string(buf)
129}
130
131func (t readerSyncliteral) writeTo(c *conn, w io.Writer) {
132 buf, err := io.ReadAll(t.r)
133 if err != nil {
134 panic(err)
135 }
136 fmt.Fprintf(w, "{%d}\r\n", len(buf))
137 defer c.xtrace(mlog.LevelTracedata)()
138 _, err = w.Write(buf)
139 if err != nil {
140 panic(err)
141 }
142}
143
144// list with tokens space-separated
145type listspace []token
146
147func (t listspace) pack(c *conn) string {
148 s := "("
149 for i, e := range t {
150 if i > 0 {
151 s += " "
152 }
153 s += e.pack(c)
154 }
155 s += ")"
156 return s
157}
158
159func (t listspace) writeTo(c *conn, w io.Writer) {
160 fmt.Fprint(w, "(")
161 for i, e := range t {
162 if i > 0 {
163 fmt.Fprint(w, " ")
164 }
165 e.writeTo(c, w)
166 }
167 fmt.Fprint(w, ")")
168}
169
170// Concatenated tokens, no spaces or list syntax.
171type concat []token
172
173func (t concat) pack(c *conn) string {
174 var s string
175 for _, e := range t {
176 s += e.pack(c)
177 }
178 return s
179}
180
181func (t concat) writeTo(c *conn, w io.Writer) {
182 for _, e := range t {
183 e.writeTo(c, w)
184 }
185}
186
187type astring string
188
189func (t astring) pack(c *conn) string {
190 if len(t) == 0 {
191 return string0(t).pack(c)
192 }
193next:
194 for _, ch := range t {
195 for _, x := range atomChar {
196 if ch == x {
197 continue next
198 }
199 }
200 return string0(t).pack(c)
201 }
202 return string(t)
203}
204
205func (t astring) writeTo(c *conn, w io.Writer) {
206 w.Write([]byte(t.pack(c)))
207}
208
209type number uint32
210
211func (t number) pack(c *conn) string {
212 return fmt.Sprintf("%d", t)
213}
214
215func (t number) writeTo(c *conn, w io.Writer) {
216 w.Write([]byte(t.pack(c)))
217}
218