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 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, w io.Writer) {
116 var lit string
117 if t.lit8 {
118 lit = "~"
119 }
120 fmt.Fprintf(w, "%s{%d}\r\n", lit, t.size)
121 defer c.xtrace(mlog.LevelTracedata)()
122 if _, err := io.Copy(w, 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, w io.Writer) {
141 buf, err := io.ReadAll(t.r)
142 if err != nil {
143 panic(err)
144 }
145 fmt.Fprintf(w, "{%d}\r\n", len(buf))
146 defer c.xtrace(mlog.LevelTracedata)()
147 _, err = w.Write(buf)
148 if err != nil {
149 panic(err)
150 }
151}
152
153// list with tokens space-separated
154type listspace []token
155
156func (t listspace) pack(c *conn) string {
157 s := "("
158 for i, e := range t {
159 if i > 0 {
160 s += " "
161 }
162 s += e.pack(c)
163 }
164 s += ")"
165 return s
166}
167
168func (t listspace) writeTo(c *conn, w io.Writer) {
169 fmt.Fprint(w, "(")
170 for i, e := range t {
171 if i > 0 {
172 fmt.Fprint(w, " ")
173 }
174 e.writeTo(c, w)
175 }
176 fmt.Fprint(w, ")")
177}
178
179// Concatenated tokens, no spaces or list syntax.
180type concat []token
181
182func (t concat) pack(c *conn) string {
183 var s string
184 for _, e := range t {
185 s += e.pack(c)
186 }
187 return s
188}
189
190func (t concat) writeTo(c *conn, w io.Writer) {
191 for _, e := range t {
192 e.writeTo(c, w)
193 }
194}
195
196type astring string
197
198func (t astring) pack(c *conn) string {
199 if len(t) == 0 {
200 return string0(t).pack(c)
201 }
202next:
203 for _, ch := range t {
204 for _, x := range atomChar {
205 if ch == x {
206 continue next
207 }
208 }
209 return string0(t).pack(c)
210 }
211 return string(t)
212}
213
214func (t astring) writeTo(c *conn, w io.Writer) {
215 w.Write([]byte(t.pack(c)))
216}
217
218type number uint32
219
220func (t number) pack(c *conn) string {
221 return fmt.Sprintf("%d", t)
222}
223
224func (t number) writeTo(c *conn, w io.Writer) {
225 w.Write([]byte(t.pack(c)))
226}
227