Я использую этот скрипт для массового удаления пустых папок IMAP: 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()
Он использует эту строку кода для удаления папок:
M.delete(mailbox)
Однако это, похоже, не полностью удаляет папки:
Я знаю, что IMAP имеет 2 шага для удаления фактических сообщений электронной почты (удалить, затем очистить/удалить)... так что я предполагаю, что это что-то похожее, но я нигде не могу найти много информации о том, как это работает с папками на IMAP. вообще.
Как я могу:
Реальная история: на сервере Exchange (на работе, не по моему выбору, не спрашивайте) я установил свои предпочтения на Гренландию для краткого эксперимента (вокруг часовых поясов IIRC), и теперь у меня есть несколько папок с именами на датском языке, которые абсолютно не могут быть удален.
@tripleee - я вижу одно и то же поведение независимо от имени папки, включая нестандартные. Не уверен, что это за демон, хост сервера IMAP: mail0.hostingplatform.com
DELETE — единственная команда для удаления папки, хотя от нее можно отказаться. Обязательно проверьте свои коды результатов!
@Макс, ты на 100% уверен в этом? Если бы команда удаления не удалась, я бы все равно увидел «фактически не удаленные папки» при повторном запуске скрипта Python. Но они исчезли с 1-го запуска. Также ушел из почтового клиента Claws, но все еще существует для Outlook + Thunderbird (серый) — даже когда я создаю совершенно новые профили учетных записей и синхронизирую их с нуля.
Да, я. Справочным документом для IMAP является RFC 3501, и я написал/сотрудничал с несколькими почтовыми клиентами.
Thunderbird, возможно, показывает вам кэшированную папку.
@Макс Хорошо, спасибо. Да, я задавался вопросом о проблемах с кэшированием/локальными данными как в Outlook, так и в Thunderbird. Но когда я создаю совершенно новые профили и настраиваю учетную запись с нуля и впервые синхронизирую из ничего... все эти папки все еще там. Тем не менее, они не указаны ни в сценарии Python, ни в почтовом клиенте Claws, ни в веб-почте Network Solutions. В приведенном выше скрипте Python есть что-нибудь, что, по вашему мнению, я могу добавить, что может привести к тому, что он будет отображать папки, которых в данный момент нет? Может быть, что-то связанное с флагами «NoChild» или что-то в этом роде?
Если у вас есть дочерние папки, родитель должен «существовать», чтобы содержать дочерние папки, даже если это не настоящая папка. Существует концепция папки \NoSelect, которая существует, но не может хранить электронную почту.
@Макс Хорошо, спасибо. В этом случае все папки находятся на самом верхнем уровне. Нет подпапок, с которыми нужно иметь дело. Просто интересно, могут ли быть какие-либо другие флаги, которые могут повлиять на видимость в зависимости от клиента.
Некоторые клиенты будут использовать LIST (все папки), а некоторые будут использовать LSUB (подписные папки, этот список можно изменить с помощью команд SUBSCRIBE и UNSUBSCRIBE). Возможно, на вашем сервере есть ошибка, из-за которой он показывает несуществующие папки с подпиской? Вы можете запустить lsub() с imaplib и посмотреть, получаете ли вы значительно другой список или посмотреть, возвращаются ли устаревшие данные.
Обратите внимание, я вижу, вы пытаетесь удалить папки \NoSelect. Обычно они уже «удалены» и существуют по какой-то другой причине (наличие дочерних папок или корней пространства имен - единственные причины, о которых я могу думать, но, возможно, есть и другие).
Большое спасибо @Max! Вы в принципе решили мою проблему. Да, это были подписки на несуществующие папки. При дальнейшем изучении я заметил этот комментарий github.com/roundcube/roundcubemail/issues/… относительно спецификации IMAP, не требующей, чтобы серверы удаляли подписки на несуществующие папки. Вы упомянули, что это может быть ошибка сервера, так что в данном случае, что касается этой части RFC, должна ли она технически считаться ошибкой, которую нужно исправить? Или просто вариант реализации, соответствующий RFC? Просто интересно, должен ли я сказать хосту исправить что-нибудь или нет.
Судя по спецификации, это не ошибка, поэтому я бы просто добавил .unsubscribe в ваш скрипт. Я думаю, что для LSUB странно возвращать папки, которых в настоящее время не существует. RFC указывает, что подписка должна быть сохранена, но неясно, должен ли LSUB возвращать их. Лично я думаю, что поведение здесь более странное, чем ситуация, которую он пытается предотвратить.
Я напишу ответ, так как это помогло вам!
В IMAP4Rev1 есть несколько причин, по которым папка может продолжать существовать в той или иной форме после того, как вы ее УДАЛИТЕ:
В последнем случае папка не существует, но сервер может продолжать возвращать ее как результат команде LSUB
, которую некоторые клиенты используют для представления своей иерархии.
Вы можете добавить M.unsubscribe()
к коду удаления папки, чтобы удалить ее из списка подписки. Вам также может понадобиться использовать M.lsub()
, чтобы найти эти папки.
Также возможно, что сервер (тихо?) отказывается удалять папки, которые ему нужны (я предполагаю, что Microsoft), и/или что клиент будет воссоздавать папки, которые он считает «стандартными», если вы попытаетесь переместить в них сообщения, а не терпят неудачу, пока вы не создадите их вручную.