1package imapserver
2
3import (
4 "testing"
5
6 "github.com/mjl-/mox/imapclient"
7)
8
9func TestAppend(t *testing.T) {
10 defer mockUIDValidity()()
11
12 tc := start(t) // note: with switchboard because this connection stays alive unlike tc2.
13 defer tc.close()
14
15 tc2 := startNoSwitchboard(t) // note: without switchboard because this connection will break during tests.
16 defer tc2.closeNoWait()
17
18 tc3 := startNoSwitchboard(t)
19 defer tc3.closeNoWait()
20
21 tc2.client.Login("mjl@mox.example", password0)
22 tc2.client.Select("inbox")
23 tc.client.Login("mjl@mox.example", password0)
24 tc.client.Select("inbox")
25 tc3.client.Login("mjl@mox.example", password0)
26
27 tc2.transactf("bad", "append") // Missing params.
28 tc2.transactf("bad", `append inbox`) // Missing message.
29 tc2.transactf("bad", `append inbox "test"`) // Message must be literal.
30
31 // Syntax error for line ending in literal causes connection abort.
32 tc2.transactf("bad", "append inbox (\\Badflag) {1+}\r\nx") // Unknown flag.
33 tc2 = startNoSwitchboard(t)
34 defer tc2.closeNoWait()
35 tc2.client.Login("mjl@mox.example", password0)
36 tc2.client.Select("inbox")
37
38 tc2.transactf("bad", "append inbox () \"bad time\" {1+}\r\nx") // Bad time.
39 tc2 = startNoSwitchboard(t)
40 defer tc2.closeNoWait()
41 tc2.client.Login("mjl@mox.example", password0)
42 tc2.client.Select("inbox")
43
44 tc2.transactf("no", "append nobox (\\Seen) \" 1-Jan-2022 10:10:00 +0100\" {1}")
45 tc2.xcode("TRYCREATE")
46
47 tc2.transactf("no", "append expungebox (\\Seen) {1}")
48 tc2.xcode("TRYCREATE")
49
50 tc2.transactf("ok", "append inbox (\\Seen Label1 $label2) \" 1-Jan-2022 10:10:00 +0100\" {1+}\r\nx")
51 tc2.xuntagged(imapclient.UntaggedExists(1))
52 tc2.xcodeArg(imapclient.CodeAppendUID{UIDValidity: 1, UIDs: xparseUIDRange("1")})
53
54 tc.transactf("ok", "noop")
55 uid1 := imapclient.FetchUID(1)
56 flags := imapclient.FetchFlags{`\Seen`, "$label2", "label1"}
57 tc.xuntagged(imapclient.UntaggedExists(1), imapclient.UntaggedFetch{Seq: 1, Attrs: []imapclient.FetchAttr{uid1, flags}})
58 tc3.transactf("ok", "noop")
59 tc3.xuntagged() // Inbox is not selected, nothing to report.
60
61 tc2.transactf("ok", "append inbox (\\Seen) \" 1-Jan-2022 10:10:00 +0100\" UTF8 (~{47+}\r\ncontent-type: just completely invalid;;\r\n\r\ntest)")
62 tc2.xuntagged(imapclient.UntaggedExists(2))
63 tc2.xcodeArg(imapclient.CodeAppendUID{UIDValidity: 1, UIDs: xparseUIDRange("2")})
64
65 tc2.transactf("ok", "append inbox (\\Seen) \" 1-Jan-2022 10:10:00 +0100\" UTF8 (~{31+}\r\ncontent-type: text/plain;\n\ntest)")
66 tc2.xuntagged(imapclient.UntaggedExists(3))
67 tc2.xcodeArg(imapclient.CodeAppendUID{UIDValidity: 1, UIDs: xparseUIDRange("3")})
68
69 // Messages that we cannot parse are marked as application/octet-stream. Perhaps
70 // the imap client knows how to deal with them.
71 tc2.transactf("ok", "uid fetch 2 body")
72 uid2 := imapclient.FetchUID(2)
73 xbs := imapclient.FetchBodystructure{
74 RespAttr: "BODY",
75 Body: imapclient.BodyTypeBasic{
76 MediaType: "APPLICATION",
77 MediaSubtype: "OCTET-STREAM",
78 BodyFields: imapclient.BodyFields{
79 Octets: 4,
80 },
81 },
82 }
83 tc2.xuntagged(imapclient.UntaggedFetch{Seq: 2, Attrs: []imapclient.FetchAttr{uid2, xbs}})
84
85 // Multiappend with two messages.
86 tc.transactf("ok", "noop") // Flush pending untagged responses.
87 tc.transactf("ok", "append inbox {6+}\r\ntest\r\n ~{6+}\r\ntost\r\n")
88 tc.xuntagged(imapclient.UntaggedExists(5))
89 tc.xcodeArg(imapclient.CodeAppendUID{UIDValidity: 1, UIDs: xparseUIDRange("4:5")})
90
91 // Cancelled with zero-length message.
92 tc.transactf("no", "append inbox {6+}\r\ntest\r\n {0+}\r\n")
93
94 tclimit := startArgs(t, false, false, true, true, "limit")
95 defer tclimit.close()
96 tclimit.client.Login("limit@mox.example", password0)
97 tclimit.client.Select("inbox")
98 // First message of 1 byte is within limits.
99 tclimit.transactf("ok", "append inbox (\\Seen Label1 $label2) \" 1-Jan-2022 10:10:00 +0100\" {1+}\r\nx")
100 tclimit.xuntagged(imapclient.UntaggedExists(1))
101 // Second message would take account past limit.
102 tclimit.transactf("no", "append inbox (\\Seen Label1 $label2) \" 1-Jan-2022 10:10:00 +0100\" {1+}\r\nx")
103 tclimit.xcode("OVERQUOTA")
104
105 // Empty mailbox.
106 tclimit.transactf("ok", `store 1 flags (\deleted)`)
107 tclimit.transactf("ok", "expunge")
108
109 // Multiappend with first message within quota, and second message with sync
110 // literal causing quota error. Request should get error response immediately.
111 tclimit.transactf("no", "append inbox {1+}\r\nx {100000}")
112 tclimit.xcode("OVERQUOTA")
113
114 // Again, but second message now with non-sync literal, which is fully consumed by server.
115 tclimit.client.Commandf("", "append inbox {1+}\r\nx {4000+}")
116 buf := make([]byte, 4000, 4002)
117 for i := range buf {
118 buf[i] = 'x'
119 }
120 buf = append(buf, "\r\n"...)
121 _, err := tclimit.client.Write(buf)
122 tclimit.check(err, "write append message")
123 tclimit.response("no")
124 tclimit.xcode("OVERQUOTA")
125}
126