David Härdeman
2021-Aug-24 15:45 UTC
Problem deleting a parent folder with child folder(s)
Hi, I'm seeing some odd behavior wrt deleting a parent folder which has child/inferior folders: root at vmtest2:~# telnet localhost 143 Trying ::1... Connected to localhost. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS AUTH=PLAIN AUTH=LOGIN] Dovecot (Debian) ready. 1 LOGIN "test at example.com" "foobar" 1 OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY STATUS=SIZE SAVEDATE LITERAL+ NOTIFY SPECIAL-USE QUOTA] Logged in 2 LIST "" "*" * LIST (\HasNoChildren \UnMarked \Trash) "." INBOX.Trash * LIST (\HasNoChildren \UnMarked \Sent) "." INBOX.Sent * LIST (\HasNoChildren \UnMarked \Junk) "." INBOX.Spam * LIST (\HasNoChildren \UnMarked \Archive) "." INBOX.Read * LIST (\HasNoChildren \UnMarked \Drafts) "." INBOX.Drafts * LIST (\HasChildren) "." INBOX 2 OK List completed (0.002 + 0.000 + 0.001 secs). 3 CREATE ParentA.ChildA 3 OK Create completed (0.017 + 0.000 + 0.017 secs). 4 LIST "" "*" * LIST (\Noselect \HasChildren) "." ParentA * LIST (\HasNoChildren) "." ParentA.ChildA * LIST (\HasNoChildren \UnMarked \Trash) "." INBOX.Trash * LIST (\HasNoChildren \UnMarked \Sent) "." INBOX.Sent * LIST (\HasNoChildren \UnMarked \Junk) "." INBOX.Spam * LIST (\HasNoChildren \UnMarked \Archive) "." INBOX.Read * LIST (\HasNoChildren \UnMarked \Drafts) "." INBOX.Drafts * LIST (\HasChildren) "." INBOX 4 OK List completed (0.001 + 0.000 secs). 5 EXAMINE ParentA 5 NO Mailbox doesn't exist: ParentA (0.001 + 0.000 secs). 6 DELETE ParentA.ChildA 6 OK Delete completed (0.022 + 0.000 + 0.021 secs). 7 CREATE ParentB 7 OK Create completed (0.019 + 0.000 + 0.018 secs). 8 CREATE ParentB.ChildB 8 OK Create completed (0.009 + 0.000 + 0.008 secs). 9 LIST "" "*" * LIST (\HasChildren) "." ParentB * LIST (\HasNoChildren) "." ParentB.ChildB * LIST (\HasNoChildren \UnMarked \Trash) "." INBOX.Trash * LIST (\HasNoChildren \UnMarked \Sent) "." INBOX.Sent * LIST (\HasNoChildren \UnMarked \Junk) "." INBOX.Spam * LIST (\HasNoChildren \UnMarked \Archive) "." INBOX.Read * LIST (\HasNoChildren \UnMarked \Drafts) "." INBOX.Drafts * LIST (\HasChildren) "." INBOX 9 OK List completed (0.001 + 0.000 secs). 10 DELETE ParentB 10 OK Delete completed (0.015 + 0.000 + 0.014 secs). 11 LIST "" "*" * LIST (\HasChildren) "." ParentB * LIST (\HasNoChildren) "." ParentB.ChildB * LIST (\HasNoChildren \UnMarked \Trash) "." INBOX.Trash * LIST (\HasNoChildren \UnMarked \Sent) "." INBOX.Sent * LIST (\HasNoChildren \UnMarked \Junk) "." INBOX.Spam * LIST (\HasNoChildren \UnMarked \Archive) "." INBOX.Read * LIST (\HasNoChildren \UnMarked \Drafts) "." INBOX.Drafts * LIST (\HasChildren) "." INBOX 11 OK List completed (0.001 + 0.000 secs). 12 EXAMINE ParentB 12 NO Mailbox doesn't exist: ParentB (0.001 + 0.000 secs). I would have expected the output from the "11 LIST..." command to be identical to that of the "4 LIST..." command, i.e. that "ParentB" would be listed as "\Noselect" in the response to "11 LIST...". I've tracked the contents of the local /var/vmail/.../Maildir folder while executing the above commands, and it appears to correspond to the expected situation (i.e. ".ParentA.ChildA" is created, then deleted, then ".ParentB" is created, then ".ParentB.ChildB" is created, and finally ".ParentB" is deleted - in other words, neither the ".ParentA" nor the ".ParentB" folders exist at the time when I issue the "4 LIST..." and "11 LIST..." commands). That "ParentB" should have a "\Noselect" flag in the last "LIST" also appears to be what's mandated by RFC3501, section 6.3.4: ... The DELETE command MUST NOT remove inferior hierarchical names. For example, if a mailbox "foo" has an inferior "foo.bar" (assuming "." is the hierarchy delimiter character), removing "foo" MUST NOT remove "foo.bar". It is an error to attempt to delete a name that has inferior hierarchical names and also has the \Noselect mailbox name attribute (see the description of the LIST response for more details). It is permitted to delete a name that has inferior hierarchical names and does not have the \Noselect mailbox name attribute. In this case, all messages in that mailbox are removed, and the name will acquire the \Noselect mailbox name attribute. ... Tested using the default dovecot package in Debian Bullseye, which is version 1:2.3.13+dfsg1-2. The file system is ext4, and Dovecot is running in a virtual (systemd-nspawn) machine for testing purposes. I've tried grepping through the git changelogs and sources, but I couldn't find anything which would appear to be relevant post-2.3.13. Any ideas? (Please CC me on replies, not subscribed) doveconf -n output =================# 2.3.13 (89f716dc2): /etc/dovecot/dovecot.conf # Pigeonhole version 0.5.13 (cdd19fe3) # OS: Linux 5.10.0-8-amd64 x86_64 Debian 11.0 # Hostname: localhost auth_default_realm = example.com auth_mechanisms = plain login doveadm_password = # hidden, use -P to show it doveadm_port = 12345 mail_gid = vmail mail_home = /var/vmail/%d/%n mail_location = maildir:~/Maildir mail_privileged_group = vmail mail_uid = vmail managesieve_notify_capability = mailto managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext imapsieve vnd.dovecot.imapsieve namespace inbox { inbox = yes location = mailbox INBOX.Drafts { auto = subscribe special_use = \Drafts } mailbox INBOX.Read { auto = subscribe special_use = \Archive } mailbox INBOX.Sent { auto = subscribe special_use = \Sent } mailbox INBOX.Spam { auto = subscribe special_use = \Junk } mailbox INBOX.Trash { auto = subscribe special_use = \Trash } prefix = } passdb { args = /etc/dovecot/dovecot.deny deny = yes driver = passwd-file } passdb { args = /etc/dovecot/dovecot-ldap-passdb.conf driver = ldap } plugin { imapsieve_mailbox1_before = file:/var/vmail/sieve/global/learn-spam.sieve imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_name = INBOX.Spam imapsieve_mailbox2_before = file:/var/vmail/sieve/global/learn-ham.sieve imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_from = INBOX.Spam imapsieve_mailbox2_name = * quota = maildir:User quota sieve = file:~/sieve;active=~/.dovecot.sieve sieve_before = /var/vmail/sieve/global/spam-global.sieve sieve_global_extensions = +vnd.dovecot.pipe sieve_pipe_bin_dir = /usr/bin sieve_plugins = sieve_imapsieve sieve_extprograms } protocols = imap lmtp sieve service aggregator { fifo_listener replication-notify-fifo { user = vmail } unix_listener replication-notify { user = vmail } } service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0660 user = postfix } unix_listener auth-userdb { group = vmail mode = 0660 user = vmail } } service doveadm { inet_listener doveadm { port = 12345 ssl = yes } } service imap-login { inet_listener imap { address = 127.0.0.1, [::1] port = 143 } inet_listener imaps { port = 993 } } service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { group = postfix mode = 0660 user = postfix } user = vmail } service managesieve-login { inet_listener sieve { port = 4190 } } service replicator { process_min_avail = 1 unix_listener replicator-doveadm { mode = 0600 user = vmail } } ssl = required ssl_cert = </etc/pki/realms/domain/default.crt ssl_client_ca_dir = /etc/ssl/certs ssl_dh = # hidden, use -P to show it ssl_key = # hidden, use -P to show it ssl_min_protocol = TLSv1.2 ssl_prefer_server_ciphers = yes userdb { args = /etc/dovecot/dovecot-ldap-userdb.conf driver = ldap } protocol imap { mail_plugins = " notify replication quota imap_sieve imap_quota" } protocol lmtp { mail_plugins = " notify replication quota sieve" postmaster_address = postmaster at example.com }