mox/imapserver/pack.go

214 lines
3.5 KiB
Go
Raw Normal View History

2023-01-30 16:27:06 +03:00
package imapserver
import (
"fmt"
"io"
)
type token interface {
pack(c *conn) string
writeTo(c *conn, w io.Writer)
}
type bare string
func (t bare) pack(c *conn) string {
return string(t)
}
func (t bare) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}
type niltoken struct{}
var nilt niltoken
func (t niltoken) pack(c *conn) string {
return "NIL"
}
func (t niltoken) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}
func nilOrString(s string) token {
if s == "" {
return nilt
}
return string0(s)
}
type string0 string
// ../rfc/9051:7081
// ../rfc/9051:6856 ../rfc/6855:153
func (t string0) pack(c *conn) string {
r := `"`
for _, ch := range t {
if ch == '\x00' || ch == '\r' || ch == '\n' || ch > 0x7f && !c.utf8strings() {
return syncliteral(t).pack(c)
}
if ch == '\\' || ch == '"' {
r += `\`
}
r += string(ch)
}
r += `"`
return r
}
func (t string0) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}
type dquote string
func (t dquote) pack(c *conn) string {
r := `"`
for _, c := range t {
if c == '\\' || c == '"' {
r += `\`
}
r += string(c)
}
r += `"`
return r
}
func (t dquote) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}
type syncliteral string
func (t syncliteral) pack(c *conn) string {
return fmt.Sprintf("{%d}\r\n", len(t)) + string(t)
}
func (t syncliteral) writeTo(c *conn, w io.Writer) {
fmt.Fprintf(w, "{%d}\r\n", len(t))
w.Write([]byte(t))
}
// data from reader with known size.
type readerSizeSyncliteral struct {
r io.Reader
size int64
}
func (t readerSizeSyncliteral) pack(c *conn) string {
buf, err := io.ReadAll(t.r)
if err != nil {
panic(err)
}
return fmt.Sprintf("{%d}\r\n", t.size) + string(buf)
}
func (t readerSizeSyncliteral) writeTo(c *conn, w io.Writer) {
fmt.Fprintf(w, "{%d}\r\n", t.size)
if _, err := io.Copy(w, io.LimitReader(t.r, t.size)); err != nil {
panic(err)
}
}
// data from reader without known size.
type readerSyncliteral struct {
r io.Reader
}
func (t readerSyncliteral) pack(c *conn) string {
buf, err := io.ReadAll(t.r)
if err != nil {
panic(err)
}
return fmt.Sprintf("{%d}\r\n", len(buf)) + string(buf)
}
func (t readerSyncliteral) writeTo(c *conn, w io.Writer) {
buf, err := io.ReadAll(t.r)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "{%d}\r\n", len(buf))
_, err = w.Write(buf)
if err != nil {
panic(err)
}
}
// list with tokens space-separated
type listspace []token
func (t listspace) pack(c *conn) string {
s := "("
for i, e := range t {
if i > 0 {
s += " "
}
s += e.pack(c)
}
s += ")"
return s
}
func (t listspace) writeTo(c *conn, w io.Writer) {
fmt.Fprint(w, "(")
for i, e := range t {
if i > 0 {
fmt.Fprint(w, " ")
}
e.writeTo(c, w)
}
fmt.Fprint(w, ")")
}
// Concatenated tokens, no spaces or list syntax.
type concat []token
func (t concat) pack(c *conn) string {
var s string
for _, e := range t {
s += e.pack(c)
}
return s
}
func (t concat) writeTo(c *conn, w io.Writer) {
for _, e := range t {
e.writeTo(c, w)
}
}
type astring string
func (t astring) pack(c *conn) string {
if len(t) == 0 {
return string0(t).pack(c)
}
next:
for _, ch := range t {
for _, x := range atomChar {
if ch == x {
continue next
}
}
return string0(t).pack(c)
}
return string(t)
}
func (t astring) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}
type number uint32
func (t number) pack(c *conn) string {
return fmt.Sprintf("%d", t)
}
func (t number) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
}