1package imapserver
2
3import (
4 "testing"
5
6 "github.com/mjl-/mox/imapclient"
7)
8
9func TestRename(t *testing.T) {
10 testRename(t, false)
11}
12
13func TestRenameUIDOnly(t *testing.T) {
14 testRename(t, true)
15}
16
17// todo: check that UIDValidity is indeed updated properly.
18func testRename(t *testing.T, uidonly bool) {
19 tc := start(t, uidonly)
20 defer tc.close()
21
22 tc2 := startNoSwitchboard(t, uidonly)
23 defer tc2.closeNoWait()
24
25 tc.login("mjl@mox.example", password0)
26 tc2.login("mjl@mox.example", password0)
27
28 tc.transactf("bad", "rename") // Missing parameters.
29 tc.transactf("bad", "rename x") // Missing destination.
30 tc.transactf("bad", "rename x y ") // Leftover data.
31
32 tc.transactf("no", "rename doesnotexist newbox") // Does not exist.
33 tc.xcodeWord("NONEXISTENT") // ../rfc/9051:5140
34 tc.transactf("no", "rename expungebox newbox") // No longer exists.
35 tc.xcodeWord("NONEXISTENT")
36 tc.transactf("no", `rename "Sent" "Trash"`) // Already exists.
37 tc.xcodeWord("ALREADYEXISTS")
38
39 tc.client.Create("x", nil)
40 tc.client.Subscribe("sub")
41 tc.client.Create("a/b/c", nil)
42 tc.client.Subscribe("x/y/c") // For later rename, but not affected by rename of x.
43 tc2.transactf("ok", "noop") // Drain.
44
45 tc.transactf("ok", "rename x z")
46 tc2.transactf("ok", "noop")
47 tc2.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "z"})
48
49 // OldName is only set for IMAP4rev2 or NOTIFY.
50 tc2.client.Enable(imapclient.CapIMAP4rev2)
51 tc.transactf("ok", "rename z y")
52 tc2.transactf("ok", "noop")
53 tc2.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "y", OldName: "z"})
54
55 // Rename to a mailbox that only exists in database as subscribed.
56 tc.transactf("ok", "rename y sub")
57 tc2.transactf("ok", "noop")
58 tc2.xuntagged(imapclient.UntaggedList{Flags: []string{`\Subscribed`}, Separator: '/', Mailbox: "sub", OldName: "y"})
59
60 // Cannot rename a child to a parent. It already exists.
61 tc.transactf("no", "rename a/b/c a/b")
62 tc.xcodeWord("ALREADYEXISTS")
63 tc.transactf("no", "rename a/b a")
64 tc.xcodeWord("ALREADYEXISTS")
65
66 tc2.transactf("ok", "noop") // Drain.
67 tc.transactf("ok", "rename a/b x/y") // This will cause new parent "x" to be created, and a/b and a/b/c to be renamed.
68 tc2.transactf("ok", "noop")
69 tc2.xuntagged(imapclient.UntaggedList{Flags: []string{`\Subscribed`}, Separator: '/', Mailbox: "x"}, imapclient.UntaggedList{Separator: '/', Mailbox: "x/y", OldName: "a/b"}, imapclient.UntaggedList{Flags: []string{`\Subscribed`}, Separator: '/', Mailbox: "x/y/c", OldName: "a/b/c"})
70
71 tc.client.Create("k/l", nil)
72 tc.transactf("ok", "rename k/l k/l/m") // With "l" renamed, a new "k" will be created.
73 tc.transactf("ok", `list "" "k*" return (subscribed)`)
74 tc.xuntagged(imapclient.UntaggedList{Flags: []string{`\Subscribed`}, Separator: '/', Mailbox: "k"}, imapclient.UntaggedList{Flags: []string{`\Subscribed`}, Separator: '/', Mailbox: "k/l"}, imapclient.UntaggedList{Separator: '/', Mailbox: "k/l/m"})
75
76 // Similar, but with missing parent not subscribed.
77 tc.transactf("ok", "rename k/l/m k/ll")
78 tc.transactf("ok", "delete k/l")
79 tc.transactf("ok", "rename k/ll k/l") // Restored to previous mailboxes now.
80 tc.client.Unsubscribe("k")
81 tc.transactf("ok", "rename k/l k/l/m") // With "l" renamed, a new "k" will be created.
82 tc.transactf("ok", `list "" "k*" return (subscribed)`)
83 tc.xuntagged(
84 imapclient.UntaggedList{Separator: '/', Mailbox: "k"},
85 imapclient.UntaggedList{Flags: []string{"\\Subscribed"}, Separator: '/', Mailbox: "k/l"},
86 imapclient.UntaggedList{Separator: '/', Mailbox: "k/l/m"},
87 )
88
89 tc.transactf("ok", "rename k/l/m k/l/x/y/m") // k/l/x and k/l/x/y will be created.
90 tc.transactf("ok", `list "" "k/l/x*" return (subscribed)`)
91 tc.xuntagged(
92 imapclient.UntaggedList{Separator: '/', Mailbox: "k/l/x"},
93 imapclient.UntaggedList{Separator: '/', Mailbox: "k/l/x/y"},
94 imapclient.UntaggedList{Separator: '/', Mailbox: "k/l/x/y/m"},
95 )
96
97 // Renaming inbox keeps inbox in existence, moves messages, and does not rename children.
98 tc.transactf("ok", "create inbox/a")
99 // To check if UIDs are renumbered properly, we add UIDs 1 and 2. Expunge 1,
100 // keeping only 2. Then rename the inbox, which should renumber UID 2 in the old
101 // inbox to UID 1 in the newly created mailbox.
102 tc.transactf("ok", "append inbox (\\deleted) {1+}\r\nx")
103 tc.transactf("ok", "append inbox (label1) {1+}\r\nx")
104 tc.transactf("ok", `select inbox`)
105 tc.transactf("ok", "expunge")
106 tc.transactf("ok", "rename inbox x/minbox")
107 tc.transactf("ok", `list "" (inbox inbox/a x/minbox)`)
108 tc.xuntagged(
109 imapclient.UntaggedList{Separator: '/', Mailbox: "Inbox"},
110 imapclient.UntaggedList{Separator: '/', Mailbox: "Inbox/a"},
111 imapclient.UntaggedList{Separator: '/', Mailbox: "x/minbox"},
112 )
113 tc.transactf("ok", `select x/minbox`)
114 tc.transactf("ok", `uid fetch 1:* flags`)
115 tc.xuntagged(tc.untaggedFetch(1, 1, imapclient.FetchFlags{"label1"}))
116
117 // Renaming to new hiearchy that does not have any subscribes.
118 tc.transactf("ok", "rename x/minbox w/w")
119 tc.transactf("ok", `list "" "w*"`)
120 tc.xuntagged(imapclient.UntaggedList{Separator: '/', Mailbox: "w"}, imapclient.UntaggedList{Separator: '/', Mailbox: "w/w"})
121
122 tc.transactf("ok", "rename inbox misc/old/inbox")
123 tc.transactf("ok", `list "" (misc misc/old/inbox)`)
124 tc.xuntagged(
125 imapclient.UntaggedList{Separator: '/', Mailbox: "misc"},
126 imapclient.UntaggedList{Separator: '/', Mailbox: "misc/old/inbox"},
127 )
128
129 // todo: test create+delete+rename of/to a name results in a higher uidvalidity.
130}
131