aboutsummaryrefslogtreecommitdiffstats
path: root/trio/doc/footer.html
diff options
context:
space:
mode:
Diffstat (limited to 'trio/doc/footer.html')
0 files changed, 0 insertions, 0 deletions
7'>97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
import os
import subprocess
import confparser
import imapclient
import contextlib
import backports.ssl
import socket

def conf_boolean_postprocess(src):
    src = src.lower().strip()
    if src == 'true' or src == 'on' or src == '1':
        return True
    return False

def conf_postprocess(conf):
    d = {
            'imap.tls':                     conf_boolean_postprocess,
            'imap.start_tls':               conf_boolean_postprocess,
            'imap.tls_nocheck_hostname':    conf_boolean_postprocess,
            'imap.tls_nocheck_ca':          conf_boolean_postprocess,
    }
    return {key: d[key](value) if key in d else value
            for key, value in conf.items()}

@contextlib.contextmanager
def connect_to_imap(conf, password):

    cafile = conf.get('imap.tls_ca', None)

    if cafile:
        cafile = os.path.expanduser(cafile)

    ssl_context = imapclient.create_default_context(cafile=cafile)

    if conf.get('imap.tls_nocheck_hostname', False):
        # don't check if certificate hostname doesn't match target hostname
        ssl_context.check_hostname = False

    if conf.get('imap.tls_nocheck_ca', False):
        # don't check if the certificate is trusted by a certificate authority
        ssl_context.verify_mode = backports.ssl.CERT_NONE

    connection = imapclient.IMAPClient(host=conf.get('imap.server'),
            ssl=conf.get('imap.tls', True),
            ssl_context=ssl_context)
    if conf.get('imap.start_tls', False):
        connection.start_tls(ssl_context=ssl_context)
    print('connection succeed')
    connection.login(username=conf.get('imap.username'), password=password)
    print('successfuly logged')

    # TODO: replace shutdown by logouts when it will not timeout through ssl
    try:
        yield connection
        #connection.logout()
    except (connection.AbortError, socket.error, socket.timeout,
            backports.ssl.SSLError, backports.ssl.CertificateError):
        raise
    else:
        pass
        #connection.logout()
    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 i[1] == b'RECENT']


def main(callback=None):
    assert callback
    confpath = os.path.expanduser('~/.config/climl/climl.cfg')
    conf = conf_postprocess(confparser.read_conf(confpath))
    print('Read conf:', conf)
    password_command = conf.get('imap.password_command', None)
    if password_command:
        password = subprocess.check_output(password_command, shell=True)
        password = password.rstrip().decode('utf8')
        print('got pasword:', password)
    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'))
                # at start, select all mails
                idlist = connection.search(['SEEN'])
                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')