Ticket #5697: 5697-imap4client-20120612.patch

File 5697-imap4client-20120612.patch, 7.0 KB (added by argonemyth, 4 years ago)
  • doc/mail/examples/imap4client.py

     
    2020from twisted.python import util
    2121from twisted.python import log
    2222
     23
     24
    2325class TrivialPrompter(basic.LineReceiver):
    2426    from os import linesep as delimiter
    2527
     
    4042        d, self.promptDeferred = self.promptDeferred, None
    4143        d.callback(line)
    4244
     45
     46
    4347class SimpleIMAP4Client(imap4.IMAP4Client):
     48    """
     49    Add callbacks when the client receives greeting messages from
     50    an IMAP server.
     51    """
    4452    greetDeferred = None
    4553   
    4654    def serverGreeting(self, caps):
     
    4957            d, self.greetDeferred = self.greetDeferred, None
    5058            d.callback(self)
    5159
     60
     61
    5262class SimpleIMAP4ClientFactory(protocol.ClientFactory):
    5363    usedUp = False
    5464
    5565    protocol = SimpleIMAP4Client
    5666
     67
    5768    def __init__(self, username, onConn):
    5869        self.ctx = ssl.ClientContextFactory()
    5970       
    6071        self.username = username
    6172        self.onConn = onConn
    6273
     74
    6375    def buildProtocol(self, addr):
     76        """
     77        Initiate the protocol instance. Since we are building a simple
     78        IMAP client, we don't bother checking what capabilities the server
     79        has. We just add all the authenticators twisted.mail has.
     80        Note: Gmail no longer uses any of the method below, it's been
     81        using XOAUTH since 2010.
     82        """
    6483        assert not self.usedUp
    6584        self.usedUp = True
    6685       
     
    6887        p.factory = self
    6988        p.greetDeferred = self.onConn
    7089
    71         auth = imap4.CramMD5ClientAuthenticator(self.username)
    72         p.registerAuthenticator(auth)
    73        
     90        p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
     91        p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
     92        p.registerAuthenticator(
     93                imap4.CramMD5ClientAuthenticator(self.username))
     94
    7495        return p
    7596   
     97
    7698    def clientConnectionFailed(self, connector, reason):
    7799        d, self.onConn = self.onConn, None
    78100        d.errback(reason)
    79101
    80 # Initial callback - invoked after the server sends us its greet message
     102
     103
    81104def cbServerGreeting(proto, username, password):
     105    """
     106    Initial callback - invoked after the server sends us its greet message.
     107    """
    82108    # Hook up stdio
    83109    tp = TrivialPrompter()
    84110    stdio.StandardIO(tp)
     
    93119        ).addErrback(ebAuthentication, proto, username, password
    94120        )
    95121
    96 # Fallback error-handler.  If anything goes wrong, log it and quit.
     122
    97123def ebConnection(reason):
     124    """
     125    Fallback error-handler. If anything goes wrong, log it and quit.
     126    """
    98127    log.startLogging(sys.stdout)
    99128    log.err(reason)
    100     from twisted.internet import reactor
    101     reactor.stop()
     129    return reason
    102130
    103 # Callback after authentication has succeeded
     131
    104132def cbAuthentication(result, proto):
    105     # List a bunch of mailboxes
     133    """
     134    Callback after authentication has succeeded.
     135    List a bunch of mailboxes.
     136    """
    106137    return proto.list("", "*"
    107138        ).addCallback(cbMailboxList, proto
    108139        )
    109140
    110 # Errback invoked when authentication fails
     141
    111142def ebAuthentication(failure, proto, username, password):
    112     # If it failed because no SASL mechanisms match, offer the user the choice
    113     # of logging in insecurely.
     143    """
     144    Errback invoked when authentication fails.
     145    If it failed because no SASL mechanisms match, offer the user the choice
     146    of logging in insecurely.
     147    If you are trying to connect to your Gmail account, you will be here!
     148    """
    114149    failure.trap(imap4.NoSupportedAuthentication)
    115     return proto.prompt("No secure authentication available.  Login insecurely? (y/N) "
     150    return proto.prompt(
     151        "No secure authentication available. Login insecurely? (y/N) "
    116152        ).addCallback(cbInsecureLogin, proto, username, password
    117153        )
    118154
    119 # Callback for "insecure-login" prompt
     155
    120156def cbInsecureLogin(result, proto, username, password):
     157    """
     158    Callback for "insecure-login" prompt.
     159    """
    121160    if result.lower() == "y":
    122161        # If they said yes, do it.
    123162        return proto.login(username, password
     
    125164            )
    126165    return defer.fail(Exception("Login failed for security reasons."))
    127166
    128 # Callback invoked when a list of mailboxes has been retrieved
     167
    129168def cbMailboxList(result, proto):
     169    """
     170    Callback invoked when a list of mailboxes has been retrieved.
     171    """
    130172    result = [e[2] for e in result]
    131173    s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(result)), result)])
    132174    if not s:
     
    135177        ).addCallback(cbPickMailbox, proto, result
    136178        )
    137179
    138 # When the user selects a mailbox, "examine" it.
     180
    139181def cbPickMailbox(result, proto, mboxes):
     182    """
     183    When the user selects a mailbox, "examine" it.
     184    """
    140185    mbox = mboxes[int(result or '1') - 1]
    141186    return proto.examine(mbox
    142187        ).addCallback(cbExamineMbox, proto
    143188        )
    144189
    145 # Callback invoked when examine command completes.
     190
    146191def cbExamineMbox(result, proto):
    147     # Retrieve the subject header of every message on the mailbox.
     192    """
     193    Callback invoked when examine command completes.
     194    Retrieve the subject header of every message on the mailbox.
     195    """
    148196    return proto.fetchSpecific('1:*',
    149197                               headerType='HEADER.FIELDS',
    150                                headerArgs=['SUBJECT']
     198                               headerArgs=['SUBJECT'],
    151199        ).addCallback(cbFetch, proto
    152200        )
    153201
    154 # Finally, display headers.
     202
    155203def cbFetch(result, proto):
    156     keys = result.keys()
    157     keys.sort()
    158     for k in keys:
    159         proto.display('%s %s' % (k, result[k][0][2]))
     204    """
     205    Finally, display headers.
     206    """
     207    if result:
     208        keys = result.keys()
     209        keys.sort()
     210        for k in keys:
     211            proto.display('%s %s' % (k, result[k][0][2]))
     212    else:
     213        print "Hey, an empty mailbox!"
     214
    160215    return proto.logout()
    161216
    162 PORT = 143
    163217
     218def cbClose(result):
     219    """
     220    Close the connection when we finish everything.
     221    """
     222    from twisted.internet import reactor
     223    reactor.stop()
     224
     225
    164226def main():
    165227    hostname = raw_input('IMAP4 Server Hostname: ')
     228    port = raw_input('IMAP4 Server Port (the default is 143): ')
    166229    username = raw_input('IMAP4 Username: ')
    167230    password = util.getPassword('IMAP4 Password: ')
    168231   
    169232    onConn = defer.Deferred(
    170233        ).addCallback(cbServerGreeting, username, password
    171234        ).addErrback(ebConnection
    172         )
     235        ).addBoth(cbClose)
    173236
    174237    factory = SimpleIMAP4ClientFactory(username, onConn)
    175238   
    176239    from twisted.internet import reactor
    177     conn = reactor.connectTCP(hostname, PORT, factory)
     240    if port == '993':
     241        conn = reactor.connectSSL(hostname, int(port), factory, ssl.ClientContextFactory())
     242    else:
     243        if not port:
     244            port = 143
     245        conn = reactor.connectTCP(hostname, int(port), factory)
    178246    reactor.run()
    179247
     248
    180249if __name__ == '__main__':
    181250    main()