aboutsummaryrefslogtreecommitdiffstats
path: root/archive_folder.py
diff options
context:
space:
mode:
Diffstat (limited to 'archive_folder.py')
-rwxr-xr-xarchive_folder.py140
1 files changed, 113 insertions, 27 deletions
diff --git a/archive_folder.py b/archive_folder.py
index d4aca54..2b3ba14 100755
--- a/archive_folder.py
+++ b/archive_folder.py
@@ -7,15 +7,16 @@
# for unittest.mock tutorials
# for testing
import argparse
+import collections
import configparser
import email
import email.header
import email.utils
+import functools
import locale
import logging
import imaplib
import os
-import pprint
import sys
from datetime import date, timedelta
@@ -34,6 +35,8 @@ class FolderError(IOError):
class MessageError(IOError):
pass
+Capas = collections.namedtuple('Capas', ['MOVE', 'UIDPLUS'])
+
def get_config():
config = configparser.ConfigParser()
@@ -72,17 +75,67 @@ class Message(object):
class Folder(object):
- def __init__(self, box, folder_sep, fld_name):
+ def __init__(self, box, fld_name, create=False):
self.box = box
+ log.debug('fld_name = %s', fld_name)
self.fld_name = fld_name
- self.folder_separator = folder_sep
self.selected = False
+ self.__create_missing = create
+ self.__folder_sep = self.__get_separator()
+
+ log.debug('create_missing = %s', self.__create_missing)
+ log.debug('list_folder = %s', self.__list_folder)
+ if self.__create_missing and self.__list_folder() is None:
+ self.__create_folder()
def select(self):
- ret = self.box.select(mailbox=self.fld_name)
+ ok, _ = self.box.select(mailbox=self.fld_name)
+ if ok != 'OK':
+ raise FolderError('Cannot select folder %s' % self.fld_name)
self.selected = True
return self
+ def __create_folder(self):
+ sep = self.__folder_sep
+ split_name = self.fld_name.split(sep)
+ log.debug('split_name = %s', split_name)
+ target = ''
+ for part in split_name:
+ log.debug('part = %s', part)
+ target += '{}{}'.format(part, self.__folder_sep)
+ if self.__list_folder(target) is None:
+ self.box.create(target)
+ self.box.subscribe(target)
+ self.__list_folder.cache_clear()
+
+ @functools.lru_cache()
+ def __list_folder(self, name=None, wildcards='*'):
+ if name is None:
+ name = self.fld_name
+ ok, data = self.box.list(name, wildcards)
+ if ok != 'OK':
+ raise ServerError('Cannot list folder %s' % self.fld_name)
+ return data[0]
+
+ def __get_separator(self):
+ # type: () -> None
+ """
+ Find current folder hierarchy separator, either from the current
+ folder, or if it doesn't exist from INBOX.
+ """
+ data = self.__list_folder()
+
+ log.debug('data = %s (%s)', data, type(data))
+
+ if data is None:
+ data = self.__list_folder('""', '""')
+
+ data = data.decode().split(' ')
+ if len(data) == 3:
+ return data[1].strip(' "')
+ else:
+ raise ServerError('Cannot find folder separator from %s' % data)
+
def __emails_search(self, search_type, date_str):
ok, res = self.box.uid('SEARCH', '%s %s' % (search_type.upper(), date_str))
if ok != 'OK':
@@ -98,8 +151,45 @@ class Folder(object):
return self.__emails_search('BEFORE', before_str)
def get_archive_folder(self, msg, aroot):
- return self.folder_separator.join((aroot, msg.date.strftime("%Y"), self.fld_name))
-
+ return self.__folder_sep.join((aroot, msg.date.strftime("%Y"), self.fld_name))
+
+ def move_messages(self, messages):
+ assert self.selected == False, 'Target folder should not be selected.'
+ if self.box.features_present.MOVE:
+ ok, data = self.box.uid('MOVE',
+ '%s %s' % (messages, self.fld_name))
+ log.debug('MOVE ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot move messages to folder %s' %
+ self.fld_name)
+ elif self.box.features_present.UIDPLUS:
+ ok, data = self.box.uid('COPY',
+ '%s %s' % (messages, self.fld_name))
+ log.debug('COPY ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot copy messages to folder %s' %
+ self.fld_name)
+ ok, data = self.box.uid('STORE',
+ r'+FLAGS.SILENT (\DELETED) %s' % messages)
+ log.debug('STORE ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot delete messages-')
+ ok, data = self.box.uid('EXPUNGE', messages)
+ log.debug('EXPUNGE ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot expunge messages.')
+ else:
+ ok, data = self.box.uid('COPY',
+ '%s %s' % (messages, self.fld_name))
+ log.debug('COPY ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot copy messages to folder %s' %
+ self.fld_name)
+ ok, data = self.box.uid('STORE',
+ r'+FLAGS.SILENT (\DELETED) %s' % messages)
+ log.debug('STORE ok = %s, data = %s', ok, data)
+ if ok != 'OK':
+ raise FolderError('Cannot delete messages-')
class EmailServer(object):
@@ -109,30 +199,21 @@ class EmailServer(object):
else config['general']['account']
self.cfg = dict(config.items(acc_name))
self.archive_root = archive_root
-
self.__box = self.__login(**self.cfg)
- self.__folder_sep = self.__get_separator()
def __login(self, host='localhost', username=None, password=None, ssl=None):
box = imaplib.IMAP4_SSL(host=host)
ok, data = box.login(username, password)
- if ok == 'OK':
- return box
- else:
+ if ok != 'OK':
raise ServerError('Cannot login with credentials %s' %
str((host, username, password,)))
- def __get_separator(self):
- # type: () -> None
- ok, data = self.__box.list('""', '""')
- if ok != 'OK':
- raise ServerError('Cannot list known folders')
-
- data = data[0].decode().split(' ')
- if len(data) == 3:
- return data[1].strip(' "')
- else:
- raise ServerError('Cannot find folder separator from %s' % data)
+ ok, data = box.capability()
+ log.debug('data = %s', data)
+ capas = data[0].decode()
+ log.debug('capas = %s', capas)
+ box.features_present = Capas._make(['MOVE' in capas, 'UIDPLUS' in capas])
+ log.debug('features_present = %s', box.features_present)
def archive_folder(self, folder_name, before_date):
# type: (str, datetime.date) -> None
@@ -143,7 +224,7 @@ class EmailServer(object):
:param: before_date
"""
copy_cache = {}
- fld = Folder(self.__box, self.__folder_sep, folder_name).select()
+ fld = Folder(self.__box, folder_name).select()
before_str = before_date.strftime('%d-%b-%Y')
for msg in fld.emails_before(before_str):
arch_folder = fld.get_archive_folder(msg, self.archive_root)
@@ -153,11 +234,17 @@ class EmailServer(object):
copy_cache[arch_folder] = [msg]
for key in copy_cache:
- log.info('\n\n%s:', key)
+ log.info('***** %s:', key)
for msg in copy_cache[key]:
log.info('\tmsg: %s', str(msg))
- # TODO: Projdi keš a proveď operaci
+ # Go through the cache and make moves.
+ for arch_dir in copy_cache:
+ msg_ids = ','.join([x.uid for x in copy_cache[arch_dir]])
+ log.debug('arch_dir = %s, msgs = %s', arch_dir,
+ msg_ids)
+ dir = Folder(self.__box, arch_dir, create=True)
+ dir.move_messages(msg_ids)
def __enter__(self):
return self
@@ -165,8 +252,7 @@ class EmailServer(object):
def __exit__(self, *args):
if args != (None, None, None):
log.warning('args = %s', args)
- else:
- self.__box.close()
+ self.__box.close()
if __name__ == '__main__':