diff options
| -rw-r--r-- | climl/imap.py | 82 | 
1 files 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') | 
