I'm using this script to bulk delete empty IMAP folders: https://gitlab.com/puzzlement/delete-empty-imap-dirs
#!/usr/bin/env python
import getpass, imaplib, sys, argparse
import parseNested
IGNORE = set(["INBOX", "Postponed", "Sent", "Sent Items", "Trash", "Drafts", "MQEmail.INBOX", "MQEmail.Outbox", "MQEmail.Postponed"])
def main():
parser = argparse.ArgumentParser(
description='This script deletes empty remote IMAP folders.')
parser.add_argument('--port', '-p', metavar='PORT', type = int,
help = 'Port number to connect to (143 or 993/SSL used by default)')
parser.add_argument("-quiet", "--q",
action="store_false", dest="verbose", default=True,
help="don't print status messages to standard error")
parser.add_argument("-s", "--ssl",
action="store_true", dest="ssl", default=False,
help="Use SSL encryption")
parser.add_argument('hostname', help="Domain name/host name of IMAP "
"server to delete folders on")
parser.add_argument('username', help="Username/login "
"on IMAP server")
args = parser.parse_args()
if args.ssl:
IMAPClass = imaplib.IMAP4_SSL
else:
IMAPClass = imaplib.IMAP4
if args.port:
M = IMAPClass(args.hostname, args.port)
else:
M = IMAPClass(args.hostname)
M.login(args.username, getpass.getpass('IMAP password for user %s at server %s: ' % (args.username, args.hostname)))
listresponse = M.list()
mailboxes = []
for chunk in listresponse[1]:
nested = parseNested.parseNestedParens(chunk)
if '\\HasNoChildren' in nested[0] and '\\NoSelect' in nested[0]:
if args.verbose:
sys.stderr.write("%s has no children and is not selectable, deleting\n" % nested[2])
M.delete(nested[2])
elif not '\\HasChildren' in nested[0]:
mboxname = nested[2]
ignoretest = set([])
ignoretest.add(mboxname)
ignoretest.add("INBOX." + mboxname)
ignoretest.add(mboxname.lstrip("INBOX."))
if not ignoretest.intersection(IGNORE):
mailboxes.append(mboxname)
for mailbox in mailboxes:
reply, data = M.select(mailbox)
if reply != 'OK':
print >> sys.stderr, "Cannot select mailbox '%s', reply was '%s', skipping" % (mailbox, str(data[0]))
continue
else:
nomessages = int(data[0])
M.close()
if nomessages == 0:
if args.verbose:
sys.stderr.write("%s is empty of messages, deleting\n" % mailbox)
M.delete(mailbox)
M.logout()
if __name__ == '__main__':
main()
It's using this line of code to delete the folders:
M.delete(mailbox)
However this doesn't seem to completely delete the folders:
I know that IMAP has 2 steps for deleting actual email messages (delete, then purge/expunge)... so I'm guessing this is something similar, but I can't find much info on how this works with folders on IMAP anywhere at all.
How can I:
In IMAP4Rev1, there are a couple reasons why a folder may continue to exist in some form after you DELETE it:
In the latter case, the folder does not exist, but the server may continue to return it as result to the LSUB
command, which some clients use to present their heirarchy.
You could add M.unsubscribe()
to your folder deletion code to remove it from the subscription list as well. You may also need to use M.lsub()
to find these folders.