1package imapserver
2
3import (
4 "testing"
5
6 "github.com/mjl-/bstore"
7
8 "github.com/mjl-/mox/imapclient"
9 "github.com/mjl-/mox/mox-"
10 "github.com/mjl-/mox/store"
11)
12
13func TestCopy(t *testing.T) {
14 testCopy(t, false)
15}
16
17func TestCopyUIDOnly(t *testing.T) {
18 testCopy(t, true)
19}
20
21func TestCopyFromIntrobox(t *testing.T) {
22 defer mockUIDValidity()()
23 tc := start(t, false)
24 defer tc.close()
25
26 conf := mox.Conf.Dynamic.Accounts["mjl"]
27 origConf := conf
28 conf.Introbox = "Introbox"
29 mox.Conf.Dynamic.Accounts["mjl"] = conf
30 defer func() {
31 mox.Conf.Dynamic.Accounts["mjl"] = origConf
32 }()
33
34 tc.login("mjl@mox.example", password0)
35 tc.client.Create("Introbox", nil)
36 tc.client.Create("Intended", nil)
37 tc.client.Append("Introbox", makeAppend(exampleMsg))
38 tc.client.Select("Introbox")
39
40 err := tc.account.DB.Write(ctxbg, func(tx *bstore.Tx) error {
41 introbox, err := tc.account.MailboxFind(tx, "Introbox")
42 if err != nil {
43 return err
44 }
45 intended, err := tc.account.MailboxFind(tx, "Intended")
46 if err != nil {
47 return err
48 }
49 m, err := bstore.QueryTx[store.Message](tx).FilterNonzero(store.Message{MailboxID: introbox.ID}).Get()
50 if err != nil {
51 return err
52 }
53 m.MailboxDestinedID = intended.ID
54 return tx.Update(&m)
55 })
56 tcheck(t, err, "set intended mailbox")
57
58 tc.transactf("ok", "uid copy 1 Intended")
59
60 err = tc.account.DB.Read(ctxbg, func(tx *bstore.Tx) error {
61 intended, err := tc.account.MailboxFind(tx, "Intended")
62 if err != nil {
63 return err
64 }
65 m, err := bstore.QueryTx[store.Message](tx).FilterNonzero(store.Message{MailboxID: intended.ID}).Get()
66 if err != nil {
67 return err
68 }
69 if m.MailboxOrigID != intended.ID || m.MailboxDestinedID != 0 || m.Junk || !m.Notjunk {
70 t.Fatalf("copied introbox message not promoted: %#v", m)
71 }
72 return nil
73 })
74 tcheck(t, err, "check copied message")
75}
76
77func testCopy(t *testing.T, uidonly bool) {
78 defer mockUIDValidity()()
79 tc := start(t, uidonly)
80 defer tc.close()
81
82 tc2 := startNoSwitchboard(t, uidonly)
83 defer tc2.closeNoWait()
84
85 tc.login("mjl@mox.example", password0)
86 tc.client.Select("inbox")
87
88 tc2.login("mjl@mox.example", password0)
89 tc2.client.Select("Trash")
90
91 tc.transactf("bad", "copy") // Missing params.
92 tc.transactf("bad", "copy 1") // Missing params.
93 tc.transactf("bad", "copy 1 inbox ") // Leftover.
94
95 // Seqs 1,2 and UIDs 3,4.
96 tc.client.Append("inbox", makeAppend(exampleMsg))
97 tc.client.Append("inbox", makeAppend(exampleMsg))
98 tc.transactf("ok", `Uid Store 1:2 +Flags.Silent (\Deleted)`)
99 tc.client.Expunge()
100 tc.client.Append("inbox", makeAppend(exampleMsg))
101 tc.client.Append("inbox", makeAppend(exampleMsg))
102
103 if uidonly {
104 tc.transactf("ok", "uid copy 3:* Trash")
105 } else {
106 tc.transactf("no", "copy 1 nonexistent")
107 tc.xcodeWord("TRYCREATE")
108 tc.transactf("no", "copy 1 expungebox")
109 tc.xcodeWord("TRYCREATE")
110
111 tc.transactf("no", "copy 1 inbox") // Cannot copy to same mailbox.
112
113 tc2.transactf("ok", "noop") // Drain.
114
115 tc.transactf("ok", "copy 1:* Trash")
116 tc.xcode(mustParseCode("COPYUID 1 3:4 1:2"))
117 }
118 tc2.transactf("ok", "noop")
119 tc2.xuntagged(
120 imapclient.UntaggedExists(2),
121 tc2.untaggedFetch(1, 1, imapclient.FetchFlags(nil)),
122 tc2.untaggedFetch(2, 2, imapclient.FetchFlags(nil)),
123 )
124
125 tc.transactf("no", "uid copy 1,2 Trash") // No match.
126 tc.transactf("ok", "uid copy 4,3 Trash")
127 tc.xcode(mustParseCode("COPYUID 1 3:4 3:4"))
128 tc2.transactf("ok", "noop")
129 tc2.xuntagged(
130 imapclient.UntaggedExists(4),
131 tc2.untaggedFetch(3, 3, imapclient.FetchFlags(nil)),
132 tc2.untaggedFetch(4, 4, imapclient.FetchFlags(nil)),
133 )
134
135 tclimit := startArgs(t, uidonly, false, false, true, true, "limit")
136 defer tclimit.close()
137 tclimit.login("limit@mox.example", password0)
138 tclimit.client.Select("inbox")
139 // First message of 1 byte is within limits.
140 tclimit.transactf("ok", "append inbox (\\Seen Label1 $label2) \" 1-Jan-2022 10:10:00 +0100\" {1+}\r\nx")
141 tclimit.xuntagged(imapclient.UntaggedExists(1))
142 // Second message would take account past limit.
143 tclimit.transactf("no", "uid copy 1:* Trash")
144 tclimit.xcodeWord("OVERQUOTA")
145}
146