Полностью удалить + очистить/удалить папки IMAP с помощью Python imaplib

Я использую этот скрипт для массового удаления пустых папок 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)

Однако это, похоже, не полностью удаляет папки:

  • В Outlook папки остаются как ни в чем не бывало
  • В Thunderbird имена папок меняются с черного на серый.
  • В вебмайле на хосте (Network Solutions) пропали папки
  • Если я снова запущу этот скрипт Python, он не увидит папки, которые он удалил при предыдущем запуске.

Я знаю, что IMAP имеет 2 шага для удаления фактических сообщений электронной почты (удалить, затем очистить/удалить)... так что я предполагаю, что это что-то похожее, но я нигде не могу найти много информации о том, как это работает с папками на IMAP. вообще.

Как я могу:

  1. Пусть этот скрипт Python снова увидит «частично удаленные» папки при последующих запусках.
  2. Полностью удалите все следы папок, т.е. выполните любую очистку/удаление, необходимую

Также возможно, что сервер (тихо?) отказывается удалять папки, которые ему нужны (я предполагаю, что Microsoft), и/или что клиент будет воссоздавать папки, которые он считает «стандартными», если вы попытаетесь переместить в них сообщения, а не терпят неудачу, пока вы не создадите их вручную.

tripleee 19.11.2022 13:06

Реальная история: на сервере Exchange (на работе, не по моему выбору, не спрашивайте) я установил свои предпочтения на Гренландию для краткого эксперимента (вокруг часовых поясов IIRC), и теперь у меня есть несколько папок с именами на датском языке, которые абсолютно не могут быть удален.

tripleee 19.11.2022 13:09

@tripleee - я вижу одно и то же поведение независимо от имени папки, включая нестандартные. Не уверен, что это за демон, хост сервера IMAP: mail0.hostingplatform.com

LaVache 19.11.2022 13:26

DELETE — единственная команда для удаления папки, хотя от нее можно отказаться. Обязательно проверьте свои коды результатов!

Max 19.11.2022 14:41

@Макс, ты на 100% уверен в этом? Если бы команда удаления не удалась, я бы все равно увидел «фактически не удаленные папки» при повторном запуске скрипта Python. Но они исчезли с 1-го запуска. Также ушел из почтового клиента Claws, но все еще существует для Outlook + Thunderbird (серый) — даже когда я создаю совершенно новые профили учетных записей и синхронизирую их с нуля.

LaVache 20.11.2022 01:05

Да, я. Справочным документом для IMAP является RFC 3501, и я написал/сотрудничал с несколькими почтовыми клиентами.

Max 20.11.2022 01:26

Thunderbird, возможно, показывает вам кэшированную папку.

Max 20.11.2022 01:27

@Макс Хорошо, спасибо. Да, я задавался вопросом о проблемах с кэшированием/локальными данными как в Outlook, так и в Thunderbird. Но когда я создаю совершенно новые профили и настраиваю учетную запись с нуля и впервые синхронизирую из ничего... все эти папки все еще там. Тем не менее, они не указаны ни в сценарии Python, ни в почтовом клиенте Claws, ни в веб-почте Network Solutions. В приведенном выше скрипте Python есть что-нибудь, что, по вашему мнению, я могу добавить, что может привести к тому, что он будет отображать папки, которых в данный момент нет? Может быть, что-то связанное с флагами «NoChild» или что-то в этом роде?

LaVache 20.11.2022 01:36

Если у вас есть дочерние папки, родитель должен «существовать», чтобы содержать дочерние папки, даже если это не настоящая папка. Существует концепция папки \NoSelect, которая существует, но не может хранить электронную почту.

Max 20.11.2022 02:57

@Макс Хорошо, спасибо. В этом случае все папки находятся на самом верхнем уровне. Нет подпапок, с которыми нужно иметь дело. Просто интересно, могут ли быть какие-либо другие флаги, которые могут повлиять на видимость в зависимости от клиента.

LaVache 20.11.2022 04:00

Некоторые клиенты будут использовать LIST (все папки), а некоторые будут использовать LSUB (подписные папки, этот список можно изменить с помощью команд SUBSCRIBE и UNSUBSCRIBE). Возможно, на вашем сервере есть ошибка, из-за которой он показывает несуществующие папки с подпиской? Вы можете запустить lsub() с imaplib и посмотреть, получаете ли вы значительно другой список или посмотреть, возвращаются ли устаревшие данные.

Max 20.11.2022 04:03

Обратите внимание, я вижу, вы пытаетесь удалить папки \NoSelect. Обычно они уже «удалены» и существуют по какой-то другой причине (наличие дочерних папок или корней пространства имен - единственные причины, о которых я могу думать, но, возможно, есть и другие).

Max 20.11.2022 04:12

Большое спасибо @Max! Вы в принципе решили мою проблему. Да, это были подписки на несуществующие папки. При дальнейшем изучении я заметил этот комментарий github.com/roundcube/roundcubemail/issues/… относительно спецификации IMAP, не требующей, чтобы серверы удаляли подписки на несуществующие папки. Вы упомянули, что это может быть ошибка сервера, так что в данном случае, что касается этой части RFC, должна ли она технически считаться ошибкой, которую нужно исправить? Или просто вариант реализации, соответствующий RFC? Просто интересно, должен ли я сказать хосту исправить что-нибудь или нет.

LaVache 21.11.2022 04:07

Судя по спецификации, это не ошибка, поэтому я бы просто добавил .unsubscribe в ваш скрипт. Я думаю, что для LSUB странно возвращать папки, которых в настоящее время не существует. RFC указывает, что подписка должна быть сохранена, но неясно, должен ли LSUB возвращать их. Лично я думаю, что поведение здесь более странное, чем ситуация, которую он пытается предотвратить.

Max 21.11.2022 13:53

Я напишу ответ, так как это помогло вам!

Max 21.11.2022 14:00
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
Python PyPDF2 - запись метаданных PDF
Python PyPDF2 - запись метаданных PDF
Python скрипт, который будет записывать метаданные в PDF файл, для этого мы будем использовать PDF ридер из библиотеки PyPDF2 . PyPDF2 - это...
Переменные, типы данных и операторы в Python
Переменные, типы данных и операторы в Python
В Python переменные используются как место для хранения значений. Пример переменной формы:
Почему Python - идеальный выбор для проекта AI и ML
Почему Python - идеальный выбор для проекта AI и ML
Блог, которым поделился Harikrishna Kundariya в нашем сообществе Developer Nation Community.
Как автоматически добавлять котировки в заголовки запросов с помощью PyCharm
Как автоматически добавлять котировки в заголовки запросов с помощью PyCharm
Как автоматически добавлять котировки в заголовки запросов с помощью PyCharm
Анализ продукта магазина на Tokopedia
Анализ продукта магазина на Tokopedia
Tokopedia - это место, где продавцы могут продавать свои товары. Товар должен быть размещен на витрине, чтобы покупателям было легче найти товар...
0
15
97
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В IMAP4Rev1 есть несколько причин, по которым папка может продолжать существовать в той или иной форме после того, как вы ее УДАЛИТЕ:

  • У него есть дочерние папки (тогда он будет отображаться как папка \NoSelect).
  • Это обязательная системная папка
  • Или он все еще подписан

В последнем случае папка не существует, но сервер может продолжать возвращать ее как результат команде LSUB, которую некоторые клиенты используют для представления своей иерархии.

Вы можете добавить M.unsubscribe() к коду удаления папки, чтобы удалить ее из списка подписки. Вам также может понадобиться использовать M.lsub(), чтобы найти эти папки.

Другие вопросы по теме