[Из песочницы] Zimbra: удаление случайных или ненужных писем

Тема, возможно, избита и не очень актуальна, если настроено автоматическое удаление писем по истечению определенного срока.

Но если случается, что один сотрудник вместо того, чтобы отправить очень приватно-пикантное сообщение другому, отправляет его (сообщение) всему холдингу… Ждать день, когда стартанет автоматическая очистка — не вариант, а удалить надо сейчас и у всех.

Под катом описание того, как это реализовано у меня.
Покопавшись в сети, наткнулся на сайт, где описан небольшой скрипт для удаления старой почты, он не плох, но не подходил для удаления писем в подпапках.

Проблемы решал по-мере приближения к основной задаче.

Для начала нужно найти все папки, в которые могло попасть письмо с подробностями вечера, для этого нам поможет zmmailbox (мощный и всеобъемлющий помошник). Вводим:

/opt/zimbra/bin/zmmailbox -z -m user@corp.org gaf # gaf - getAllFolders


В ответ получаем:

       Id  View      Unread   Msg Count  Path
----------  ----  ----------  ----------  ----------
         1  unkn           0           0  /
        16  docu           0          15  /Briefcase
        10  appo           0          52  /Calendar
        14  mess           0        1564  /Chats
         7  cont           0           7  /Contacts
         6  mess           0          56  /Drafts
        13  cont           0         179  /Emailed Contacts
         2  mess           0        5352  /Inbox
     65450  mess           0       12433  /Inbox/до 1.04.2015
     31907  mess           0        3125  /Inbox/Маркетинг
      5993  mess           0         881  /Inbox/Юрики
         4  mess           0           0  /Junk
         5  mess           0        7527  /Sent
        15  task           0           8  /Tasks
         3  unkn           0           0  /Trash
      1770  mess           7           7  /Trash/Тех. Поддержка (support@corp.org:703)
      3741  docu           0           1  /Инструкция
     87195  docu           0           1  /Коменты
      1015  docu           0          12  /Личный
       663  cont           0           1  /ОБЩАЯ АДРЕСНАЯ КНИГА
       290  task           0           2  /Общедоступные
      1100  mess        4404       22301  /Тех. Поддержка - Входящие (support@corp.org:2)


Как видим, у нас полным-полно всего. Столбец View нам предподоткрывает глаза и мы видим, что там типы папок. Нам нужны папки с сообщений — это mess. Зная что нам нужно, можем отфильтровать запрос:

/opt/zimbra/bin/zmmailbox -z -m user@corp.org gaf | grep mess


Вывод запроса:

       Id  View      Unread   Msg Count  Path
----------  ----  ----------  ----------  ----------
        14  mess           0        1564  /Chats
         6  mess           0          56  /Drafts
         2  mess           0        5352  /Inbox
     65450  mess           0       12433  /Inbox/до 1.04.2015
     31907  mess           0        3125  /Inbox/Маркетинг
      5993  mess           0         881  /Inbox/Юрики
         4  mess           0           0  /Junk
         5  mess           0        7527  /Sent
      1770  mess           7           7  /Trash/Тех. Поддержка (support@corp.org:703)
      1100  mess        4404       22301  /Тех. Поддержка - Входящие (support@corp.org:2)


Как видим, получаем уже более нужный список, но 1100 mess 4404 22301 /Тех. Поддержка — Входящие (support@corp.org:2) — это расшаренная папка с другого ящика, а, следовательно, она будет проверена отдельно. Добавим еще одно исключение и сразу уберем все лишнее: типы, порядковые номера, уберем все до слеша (названия папок).

/opt/zimbra/bin/zmmailbox -z -m user@corp.org gaf | grep mess | cut -d"/" -f 2- | grep -v "@corp.org"


Получаем нужный список:

Chats
Drafts
Inbox
Inbox/до 1.04.2015
Inbox/Маркетинг
Inbox/Юрики
Junk
Sent


Вывод команды перенаправляю в файл, для небольшого и редкого удобства, и уже из файла скрипт будет брать названия папок. Итак, самое сложное — это не столько заглядывать в папки, сколько заглядывать в папки, имена которых содержат пробелы или спецсимволы.

Если ввести в консоли:

/opt/zimbra/bin/zmmailbox -z -m user@corp.org s -l 300 in:Inbox/Папка | grep `date -d '-5 day' +%m/%d/%y` | grep "Вечеринка" | sed -e "s/^\s\s*//" | sed -e "s/\s\s*/ /g"


Выводит темы писем 5-ти дневной давности. Вместо date -d '-5 day' +%m/%d/%y можно сразу писать даты в фомате 01/27/16 или меняя -5 на другое значение, в зависимости от того, на сколько дней назад надо откатиться.

Но если ввести:

/opt/zimbra/bin/zmmailbox -z -m user@corp.org s -l 300 in:Inbox/Папка Секретов | grep `date -d '-5 day' +%m/%d/%y` | grep "Вечеринка" | sed -e "s/^\s\s*//" | sed -e "s/\s\s*/ /g" 


То вывалится ошибка:

ERROR: mail.NO_SUCH_FOLDER (no such folder path: /Inbox/Папка\)


И хоть обвставляйся кавычками, апострофами и экранами — не поможет.

Тут мне указали на вики, в которой нашел ответ на вопрос.
Для решения проблемы нужно поместить в определенные блоки имя папки с пробелами. »\» — вот блоко-тег.

/opt/zimbra/bin/zmmailbox -z -m user@corp.org s -l 300 in:"\"Inbox/Папка Секретов"\"


Выполнив это в консили, получил требуемый результат, но моя задача состояла все скриптозовать. И в моем скрипте переменная, содержащая название папки, брала данные из файла.

/opt/zimbra/bin/zmmailbox -z -m user@corp.org gaf | grep mess | cut -d"/" -f 2- | grep -v "@corp.org" > /tmp/messfolder.list


И при вставлении переменной в мой код:

/opt/zimbra/bin/zmmailbox -z -m $i s -l 300 in:"\"$p"\" | grep `date -d '-5 day' +%m/%d/%y` | grep "Вечеринка" | sed -e "s/^\s\s*//" | sed -e "s/\s\s*/ /g" |


У меня вываливалась таже ошибка:

ERROR: mail.NO_SUCH_FOLDER (no such folder path: /Inbox/Папка\)


Хотя переменная и помещена в нужные блоко-теги. Поискав еще немного, вспомнил наткнулся на переменную разделителя поля IFS. Задал ему значение IFS=$'\n' и запустил скрипт — все пошло как и планировалось.

Вот привожу весь скрипт с описаниями
#!/bin/bash

DOMAIN_NAME="corp.org"  # Тут домен
EMAIL=/tmp/email.list           # Это список всем ящиков
MESID=/tmp/mesid.list          # это ID писем для удаления 
DELTEXT='вечеринка'         # Слово в теме письма или сама тема письма

IFS=$'\n' # Переменная отвечающая за разделитель поля

before="$(date +%s)"  # Время запуска скрипта
t=0  # Переменная, для подсчета удаленных писем

/opt/zimbra/bin/zmprov -l gaa $DOMAIN_NAME | sort > $EMAIL  #   Собираем список ящиков

# Ну и циклом их перебираем

for i in $(cat $EMAIL);
    do
        echo $i
        # Тут мы получаем список всех папок, отсеим папки которые расшаренные, так как они и так будут пройдены и исключаем папки, которые не почтовые - это Портфель, и Контакты и прочие, не mess

        /opt/zimbra/bin/zmmailbox -z -m $i gaf | grep mess | cut -d"/" -f 2- | grep -v "@corp.org" > /tmp/messfolder.list
        
        # Теперь циклом проходим по списку и получаем название папки, затем всталяем в запрос и получаем id сообщения для удаления. И так проделываем с каждой папкой.

        for p in $(cat /tmp/messfolder.list);
            do
                echo $p
                
                # Тут все просто: до первого grep мы выводим сообщения из ящика, grep фильтрует по дате, второй grep по теме, затем sed'ы убирают лишние пробелы и cut вырезает остаток и получаем чистый, фильтрованный номер сообщения.
                /opt/zimbra/bin/zmmailbox -z -m $i s -l 300 in:"\"$p"\" | grep `date -d '-5 day' +%m/%d/%y` | grep "$DELTEXT" | sed -e "s/^\s\s*//" | sed -e "s/\s\s*/ /g" | cut -d" " -f2 > $MESID

            # Выводим номер сообщения и считаем кол-во сообщений.
             cat $MESID
             count=`grep '' $MESID -c`
             let t=$t+$count

             for a in $(cat $MESID | grep ^- | sed s/-//g )
                 do
                     /opt/zimbra/bin/zmmailbox -z -m $i deleteMessage $a
                 done

         for a in $(cat $MESID | sed /-/d)
             do
                 /opt/zimbra/bin/zmmailbox -z -m $i deleteConversation $a
             done
         echo -n > $MESID
         RES=$?
         if [ "$RES" == "0" ]; then echo "[Ok]"; else echo "[Err]"; fi

            done

        echo "Найдено писем: "$t

done

after="$(date +%s)"
elapsed="$(expr $after - $before)"
hours=$(($elapsed / 3600))
elapsed=$(($elapsed - $hours * 3600))
minutes=$(($elapsed / 60))
seconds=$(($elapsed - $minutes * 60))
echo "Скрипт выполнялся: $hours часов $minutes минут $seconds секунд"
echo "Всего удалено: "$t" писем"



Конечно, скрипт можно еще доработать: убрать ненужные папки, вводить дату и тему заранее, но это уже не так принципиально. Этот скрипт повесил на самодельную веб-админку и все даты и темы для удаления ввожу из нее, все ж удобнее.

Кто-то посчитает, что это лишнее, и такой скрипт излишен, но как показала практика — он спас как минимум 10 человек и пару начальников. Им хорошо, и админу после того, как им стало хорошо, тоже стало хорошо.

У этого скрипта есть один минус — это время. Данный скрипт пробегает по всем папкам 700 пользователей за 4 часа. Пока что не выявлял где можно выиграть время, но если подскажите или укажите на ошибки — буду благодарен.

Спасибо за прочтение и коментарии.

© Habrahabr.ru