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