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