From 2512ade6b4d0560972e09cf667885816f5ebab9d Mon Sep 17 00:00:00 2001 From: VG Date: Mon, 9 May 2016 15:40:01 +0200 Subject: Auto-commit on 6d1dbe8495b5fafbc5f50d80268d0ca5b7b097be --- climl/imap.py | 82 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/climl/imap.py b/climl/imap.py index bd5b3ff..783b228 100644 --- a/climl/imap.py +++ b/climl/imap.py @@ -62,6 +62,50 @@ def connect_to_imap(conf, password): finally: connection.shutdown() + +def imap_waiter(connection): + ''' + from dovecot wiki: + > If IDLE command is started, Dovecot never + > disconnects. Only if the connection is lost there will be + > a disconnection. A dead connection is detected by Dovecot + > periodically sending "I'm still here" notifications to + > client (imap_idle_notify_interval setting - default every + > 2 minutes). + > + > IMAP clients are supposed to send something before 30 + > minutes are up, but several clients don't do this. Some + > Outlook versions even stop receiving new mails entirely + > until manual intervention if IMAP server disconnects the + > client. + + We are a compliant client. To be compatible with a lot of servers + (general assumption is a 29minutes timeout), we try to redo idle-mode + every 24 minutes. Each call to imap_waiter is a single idle iteration. + Thus this function may return empty list. + ''' + print('entering idle mode...') + connection.idle() + print('waiting for an event') + try: + while True: + events = connection.idle_check(timeout=24*60) + if len(events) == 1 and \ + str(bytes(events[0][0]), encoding='ascii') == 'OK': + # in case of dovecot 'Ok still here' keepalives, timeout did + # not expired (and never will appart from a disconnection) + continue + else: + break + except KeyboardInterrupt: + print('quitting idle mode on interrupt.') + connection.idle_done() + raise + print('quitting idle mode...') + connection.idle_done() + return [i for i in events if str(bytes(i[0]), encoding='ascii') != 'OK'] + + def main(callback=None): assert callback confpath = os.path.expanduser('~/.config/climl/climl.cfg') @@ -72,24 +116,20 @@ def main(callback=None): password = subprocess.check_output(password_command, shell=True) password = password.rstrip().decode('utf8') print('got pasword:', password) - - #connection = connect_to_imap(conf, password) - with connect_to_imap(conf, password) as connection: - print('selecting folder') - connection.select_folder(conf.get('imap.mailbox')) - print('entering idle mode') - connection.idle() - print('waiting for an event') - while True: - try: - result = connection.idle_check(timeout=10) - except KeyboardInterrupt: - break - print('result:', result) - print('calling callback') - if result: - callback('mail') - print('end of connection') - - -#main() # dev mode for now + while True: + print('connection...') + try: + with connect_to_imap(conf, password) as connection: + print('selecting folder', conf.get('imap.mailbox')) + connection.select_folder(conf.get('imap.mailbox')) + while True: + events = imap_waiter(connection) + if events: + print('events:', events) + print('calling callback') + callback('mail') + except (socket.error, socket.timeout): + pass + except KeyboardInterrupt: + break + print('end of connection') -- cgit v1.2.3