Skip to content

Commit

Permalink
improved: imap idle (closes #15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Schulte-Tickmann committed Oct 2, 2020
1 parent d9e2500 commit 37ff3ef
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 12 deletions.
43 changes: 43 additions & 0 deletions imapidle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from threading import Thread
from time import sleep, time


class IMAPIdle(Thread):
def __init__(self, client, interval=1680):
self.client = client
self.interval = interval
self._idle = False
self._exit = False
super(IMAPIdle, self).__init__()

def run(self):
while self._exit is False:
if self._idle and (time()-self._idle) > self.interval:
self.restart_idle()
sleep(0.1)

def exit(self):
self._exit = True

def start_idle(self):
"""
start imap idle to hold the connection alive
"""
if self._idle is False:
#: must select a folder before invoking idle. we simply select the first folder to idle on
_, _, some_folder = self.client.list_folders()[0]
self.client.select_folder(some_folder, readonly=True)
self.client.idle()
self._idle = time()

def stop_idle(self):
"""
stop idle mode to allow normal commands
"""
if self._idle:
self.client.idle_done()
self._idle = False

def restart_idle(self):
self.stop_idle()
self.start_idle()
36 changes: 24 additions & 12 deletions pymap-copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from imapclient import IMAPClient, exceptions

from imapidle import IMAPIdle
from utils import decode_mime, beautysized, imaperror_decode


Expand Down Expand Up @@ -95,6 +96,8 @@ def login(client, user, password):
parser.add_argument('--denied-flags', help='mails with this flags will be skipped', type=str)
parser.add_argument('-r', '--redirect', help='redirect a folder (source:destination --denied-flags seen,recent -d)',
action='append')
parser.add_argument('--idle-interval', help='time in defines the interval (in seconds) in which the idle process is '
'restarted (default: 1680)', type=int, default=1680)
parser.add_argument('--ignore-quota', help='ignores insufficient quota', action='store_true')
parser.add_argument('--ignore-folder-flags', help='do not link default IMAP folders automatically (like Drafts, '
'Trash, etc.)', action='store_true')
Expand Down Expand Up @@ -212,7 +215,15 @@ def login(client, user, password):
print('\nAbort! Please fix the errors above.')
exit()

print()

#: starting idle threads
print('Starting idle threads : ', end='', flush=True)
source_idle = IMAPIdle(source, interval=args.idle_interval)
destination_idle = IMAPIdle(destination, interval=args.idle_interval)
source_idle.start()
destination_idle.start()
print('{} (restarts every {} seconds)'.format(colorize('OK', color='green'), args.idle_interval))
print()

#: get quota from source
Expand Down Expand Up @@ -257,10 +268,7 @@ def login(client, user, password):

print()




start_imap_idle(destination)
destination_idle.start_idle()

#: get source folders
print(colorize('Getting source folders : loading (this can take a while)', clear=True), flush=True, end='')
Expand Down Expand Up @@ -320,20 +328,15 @@ def login(client, user, password):
print(colorize('Getting source folders : Progressing ({} mails): {}'.
format(stats['source_mails'], name), clear=True), flush=True, end='')

# adding a check to refresh our imap idle session on the destination imap connection so we do not
# get logged out. This is possibly too frequently, but just taking a guess here.
if stats['source_mails'] % 10000 == 0:
restart_imap_idle(destination)

del mails[:args.buffer_size]

print(colorize('Getting source folders : {} mails in {} folders ({})'.
format(stats['source_mails'], len(db['source']['folders']),
beautysized(sum([f['size'] for f in db['source']['folders'].values()]))), clear=True))


end_imap_idle(destination)
start_imap_idle(source)
destination_idle.stop_idle()
source_idle.start_idle()


#: get destination folders
Expand Down Expand Up @@ -395,6 +398,10 @@ def login(client, user, password):
color='cyan')))
else:
print('\n{}'.format(colorize('Everything skipped! (list mode)', color='cyan')))

#: stop idle threads & exit
source_idle.exit()
destination_idle.exit()
exit()


Expand Down Expand Up @@ -427,7 +434,7 @@ def login(client, user, password):
print('\n{} Source folder not found: {}\n'.format(colorize('Error:', color='red', bold=True), ', '.join(not_found)))
exit()

end_imap_idle(source)
source_idle.stop_idle()

try:
for sf_name in sorted(db['source']['folders'], key=lambda x: x.lower()):
Expand Down Expand Up @@ -613,6 +620,11 @@ def login(client, user, password):
print()
print('Finish!\n')

#: stop idle threads
source_idle.exit()
destination_idle.exit()

#: logout source
try:
print('Logout source...', end='', flush=True)
source.logout()
Expand Down

0 comments on commit 37ff3ef

Please sign in to comment.