1package imapserver
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "io"
7 mathrand "math/rand/v2"
8 "strings"
9 "testing"
10 "time"
11)
12
13func TestCompress(t *testing.T) {
14 tc := start(t, false)
15 defer tc.close()
16
17 tc.login("mjl@mox.example", password0)
18
19 tc.transactf("bad", "compress")
20 tc.transactf("bad", "compress bogus ")
21 tc.transactf("no", "compress bogus")
22
23 tc.client.CompressDeflate()
24 tc.transactf("no", "compress deflate") // Cannot have multiple.
25 tc.xcodeWord("COMPRESSIONACTIVE")
26
27 tc.client.Select("inbox")
28 tc.transactf("ok", "append inbox (\\seen) {%d+}\r\n%s", len(exampleMsg), exampleMsg)
29 tc.transactf("ok", "noop")
30 tc.transactf("ok", "fetch 1 body.peek[1]")
31}
32
33func TestCompressStartTLS(t *testing.T) {
34 tc := start(t, false)
35 defer tc.close()
36
37 tc.client.StartTLS(&tls.Config{InsecureSkipVerify: true})
38 tc.login("mjl@mox.example", password0)
39 tc.client.CompressDeflate()
40 tc.client.Select("inbox")
41 tc.transactf("ok", "append inbox (\\seen) {%d+}\r\n%s", len(exampleMsg), exampleMsg)
42 tc.transactf("ok", "noop")
43 tc.transactf("ok", "fetch 1 body.peek[1]")
44}
45
46func TestCompressBreak(t *testing.T) {
47 // Close the client connection when the server is writing. That causes writes in
48 // the server to fail (panic), jumping out of the flate writer and leaving its
49 // state inconsistent. We must not call into the flate writer again because due to
50 // its broken internal state it may cause array out of bounds accesses.
51
52 tc := start(t, false)
53 defer tc.close()
54
55 var msg strings.Builder
56 msg.WriteString(exampleMsg)
57 // Add random data (so it is not compressible). Don't know why, but only
58 // reproducible with large writes. As if setting socket buffers had no effect.
59 buf := make([]byte, 64*1024)
60 _, err := io.ReadFull(mathrand.NewChaCha8([32]byte{}), buf)
61 tcheck(t, err, "read random")
62 text := base64.StdEncoding.EncodeToString(buf)
63 for len(text) > 0 {
64 n := min(76, len(text))
65 msg.WriteString(text[:n] + "\r\n")
66 text = text[n:]
67 }
68
69 tc.login("mjl@mox.example", password0)
70 tc.client.CompressDeflate()
71 tc.client.Select("inbox")
72 tc.transactf("ok", "append inbox (\\seen) {%d+}\r\n%s", len(msg.String()), msg.String())
73 tc.transactf("ok", "noop")
74
75 // Write request. Close connection instead of reading data. Write will panic,
76 // coming through flate writer leaving its state inconsistent. Server must not try
77 // to Flush/Write again on flate writer or it may panic.
78 tc.client.Writelinef("x fetch 1 body.peek[1]")
79
80 // Close client connection and prevent cleanup from closing the client again.
81 time.Sleep(time.Second / 10)
82 tc.client = nil
83 tc.conn.Close() // Simulate client disappearing.
84}
85