我有以下代码
# logging
from twisted.python import log
import sys
# MIME Multipart handling
import email
import email.mime.application
import uuid
# IMAP Connection
from twisted.mail import imap4
from twisted.internet import protocol
#SMTP Sending
import os.path
from OpenSSL.SSL import SSLv3_METHOD
from twisted.internet import ssl
from twisted.mail.smtp import ESMTPSenderFactory
from twisted.internet.ssl import ClientContextFactory
from twisted.internet.defer import Deferred
from twisted.internet import reactor
#class AccountsManager(object):
def connectToIMAPServer(imap_server, username, password):
factory = IMAP4ClientFactory(username, password, login_insecure = True)
host, port = imap_server.split(":")
# connect to reactor
if port == '993':
reactor.connectSSL(host, int(port), factory, ssl.ClientContextFactory())
else:
if not port:
port = 143
reactor.connectTCP(host, int(port), factory)
d = factory.deferred
d.addCallback(lambda r: factory.proto)
return d
class IMAP4Client(imap4.IMAP4Client):
"""
A client with callbacks for greeting messages from an IMAP server.
"""
greetDeferred = None
def serverGreeting(self, caps):
self.serverCapabilities = caps
if self.greetDeferred is not None:
d, self.greetDeferred = self.greetDeferred, None
d.callback(self)
class IMAP4ClientFactory(protocol.ClientFactory):
usedUp = False
protocol = IMAP4Client
def __init__(self, username, password, mailbox = "INBOX", login_insecure = False):
self.ctx = ssl.ClientContextFactory()
self.username = username
self.password = password
self.mailbox = mailbox
self.login_insecure = login_insecure
self.deferred = Deferred()
def buildProtocol(self, addr):
"""
Initiate the protocol instance. Since we are building a simple IMAP
client, we don't bother checking what capabilities the server has. We
just add all the authenticators twisted.mail has. Note: Gmail no
longer uses any of the methods below, it's been using XOAUTH since
2010.
"""
assert not self.usedUp
self.usedUp = True
p = self.protocol(self.ctx)
p.factory = self
p.greetDeferred = self.deferred
p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
p.registerAuthenticator(imap4.CramMD5ClientAuthenticator(self.username))
self.deferred.addCallback(self.GreetingCallback)
self.deferred.addErrback(self.GreetingErrback)
self.proto = p
return p
def GreetingCallback(self, result):
print "Secure Login"
auth_d = self.proto.authenticate(self.password)
auth_d.addCallback(self.AuthenticationCallback)
auth_d.addErrback(self.AuthenticationErrback)
return auth_d # attach it to the main deferred
def GreetingErrback(self, error):
log.err(error)
self.CloseConnection()
return error
def AuthenticationCallback(self, result):
print "Selecting Mailbox"
d = self.proto.examine(self.mailbox)
return d
def AuthenticationErrback(self, failure):
if self.login_insecure:
failure.trap(imap4.NoSupportedAuthentication)
return self.InsecureLogin()
else:
return error
def InsecureLogin(self):
print "Insecure Login"
d = self.proto.login(self.username, self.password)
d.addCallback(self.AuthenticationCallback)
return d
def CloseConnection(self):
self.proto.transport.loseConnection()
def clientConnectionFailed(self, connector, reason):
d, self.deferred = self.deferred, None
d.errback(reason)
class MailServer(object):
"Manages a server"
size = 0
used_space = 0
def __init__(self, smtp_server, imap_server, username, password):
self.smtp_server, self.smtp_port = smtp_server.split(":")
self.imap_server, self.imap_port = imap_server.split(":")
self.username = username
self.password = password
self.imap_connection = IMAP4ClientFactory(username, password)
def upload_data(self, data):
"""
Uploads data to email server returns deferred that will return with the imap uid
"""
# Create a text/plain message
id = str(uuid.uuid4()).upper()
msg = email.mime.Multipart.MIMEMultipart()
msg['Subject'] = 'GMA ID: %s' % id
msg['From'] = self.email_address
msg['To'] = self.email_address
# The main body is just another attachment
body = email.mime.Text.MIMEText("GMA ID: %s" % (self.uuid_id))
msg.attach(body)
att = email.mime.application.MIMEApplication(data,_subtype="raw")
att.add_header('Content-Disposition','attachment',filename = os.path.basename(self.filename))
msg.attach(att)
# Create a context factory which only allows SSLv3 and does not verify
# the peer's certificate.
contextFactory = ClientContextFactory()
contextFactory.method = SSLv3_METHOD
d = Deferred()
mime_obj = StringIO(str(msg))
senderFactory = ESMTPSenderFactory(
self.username,
self.password,
self.email_address,
self.email_address,
mime_obj,
d,
contextFactory=contextFactory)
d.addCallback(lambda r: self.email_sent(id, int(self.parts)) )
d.addErrback(self.email_error)
reactor.connectTCP(self.smtp_server, self.smtp_port, senderFactory)
d.addCallback(self.upload_success, *args, **kw)
d.addErrback(self.upload_error, 1)
return d
def upload_success(self, result):
print "upload was succesful!"
def upload_error(self, result):
print "upload error"
def download_data(self, uid):
"""
Downloads data from the email server returns a deferred that will return with the data
"""
print "uid"
if __name__ == "__main__":
log.startLogging(sys.stdout)
d = connectToIMAPServer("imap.gmail.com:993", "username", "password")
def f(s):
print s
d.addCallback(lambda r: f("These are fired before the auth and examine callbacks, why?"))
d.addCallback(lambda r: f("These are fired before the auth and examine callbacks, why?"))
reactor.run()
该类假设处理登录并选择邮箱并很好地返回准备使用的IMAP原型,但是底部的两个回调在其他回调之前被触发,我明白了,回调是在其他回调之前添加的因为尚未调用buildProtocol,所以处理这个问题的最佳方法是什么,只需在 init 中添加一个虚拟回调来“保留”第一个位置?
答案 0 :(得分:2)
from twisted.internet.endpoints import TCP4ClientEndpoint
d = TCP4ClientEndpoint(reactor, host, int(port)).connect(factory)
和
d.addCallback(lambda r: factory.deferred)
而不是
d = factory.deferred
connectToIMAPServer
中的应该这样做 - 只有在协议准备就绪后才会返回factory.deferred
。 (Twisted Documentation on writing clients)
答案 1 :(得分:1)
我最终编辑了代码,并在内部管理了延迟的回调或错误 更新代码
# logging
from twisted.python import log
import sys
# MIME Multipart handling
import email
import email.mime.application
import uuid
# IMAP Connection
from twisted.mail import imap4
from twisted.internet import protocol
#SMTP Sending
import os.path
from OpenSSL.SSL import SSLv3_METHOD
from twisted.internet import ssl
from twisted.mail.smtp import ESMTPSenderFactory
from twisted.internet.ssl import ClientContextFactory
from twisted.internet.defer import Deferred
from twisted.internet import reactor
#class AccountsManager(object):
def connectToIMAPServer(imap_server, username, password):
factory = IMAP4ClientFactory(username, password, login_insecure = True)
host, port = imap_server.split(":")
# connect to reactor
if port == '993':
reactor.connectSSL(host, int(port), factory, ssl.ClientContextFactory())
else:
if not port:
port = 143
reactor.connectTCP(host, int(port), factory)
return factory.deferred
class IMAP4Client(imap4.IMAP4Client):
"""
A client with callbacks for greeting messages from an IMAP server.
"""
greetDeferred = None
def serverGreeting(self, caps):
self.serverCapabilities = caps
if self.greetDeferred is not None:
d, self.greetDeferred = self.greetDeferred, None
d.callback(self)
class IMAP4ClientFactory(protocol.ClientFactory):
usedUp = False
protocol = IMAP4Client
def __init__(self, username, password, mailbox = "INBOX", login_insecure = False):
self.ctx = ssl.ClientContextFactory()
self.username = username
self.password = password
self.mailbox = mailbox
self.login_insecure = login_insecure
# called when the protocol is all set up or there is an error setting it up
self.deferred = Deferred()
def buildProtocol(self, addr):
"""
Initiate the protocol instance. Since we are building a simple IMAP
client, we don't bother checking what capabilities the server has. We
just add all the authenticators twisted.mail has. Note: Gmail no
longer uses any of the methods below, it's been using XOAUTH since
2010.
"""
assert not self.usedUp
self.usedUp = True
p = self.protocol(self.ctx)
p.factory = self
# deferred for when the IMAP Greeting is done
p.greetDeferred = Deferred()
p.registerAuthenticator(imap4.PLAINAuthenticator(self.username))
p.registerAuthenticator(imap4.LOGINAuthenticator(self.username))
p.registerAuthenticator(imap4.CramMD5ClientAuthenticator(self.username))
p.greetDeferred.addCallback(self.GreetingCallback)
p.greetDeferred.addErrback(self.GreetingErrback)
self.proto = p
return p
def GreetingCallback(self, result):
log.msg("Succesfully sent IMAP Greeting.")
auth_d = self.proto.authenticate(self.password)
auth_d.addCallback(self.AuthenticationCallback)
auth_d.addErrback(self.AuthenticationErrback)
return auth_d
def GreetingErrback(self, error):
log.msg("Problem sending greeting")
log.err(error)
self.CloseConnection()
self.deferred.errback(error)
def AuthenticationCallback(self, result):
log.msg("Authenticated")
log.msg("Selecting Mailbox")
d = self.proto.examine(self.mailbox)
d.addCallback(self.MailboxSelectCallback)
d.addErrback(self.MailboxSelectErrback)
return d
def AuthenticationErrback(self, failure):
log.msg("Unable to authenticate securly")
if self.login_insecure:
log.msg("Trying to login insecurly")
failure.trap(imap4.NoSupportedAuthentication)
return self.InsecureLogin()
else:
log.err(failure)
self.deferred.errback(failure)
def InsecureLogin(self):
log.msg("Logging in insecurly")
d = self.proto.login(self.username, self.password)
d.addCallback(self.AuthenticationCallback)
return d
def MailboxSelectCallback(self, result):
# connected and protocol set up
log.msg("IMAP4 protocol setup")
self.deferred.callback(self.proto)
def MailboxSelectErrback(self, error):
log.msg("Cannot select mailbox %s" % self.mailbox)
log.err(error)
self.deferred.errback(error)
def CloseConnection(self):
self.proto.transport.loseConnection()
def clientConnectionFailed(self, connector, reason):
log.msg("Connecting was lost")
log.err(reason)
d, self.deferred = self.deferred, None
d.errback(reason)
class MailServer(object):
"Manages a server"
size = 0
used_space = 0
def __init__(self, smtp_server, imap_server, username, password):
self.smtp_server, self.smtp_port = smtp_server.split(":")
self.imap_server, self.imap_port = imap_server.split(":")
self.username = username
self.password = password
self.imap_connection = IMAP4ClientFactory(username, password)
def upload_data(self, data):
"""
Uploads data to email server returns deferred that will return with the imap uid
"""
# Create a text/plain message
id = str(uuid.uuid4()).upper()
msg = email.mime.Multipart.MIMEMultipart()
msg['Subject'] = 'GMA ID: %s' % id
msg['From'] = self.email_address
msg['To'] = self.email_address
# The main body is just another attachment
body = email.mime.Text.MIMEText("GMA ID: %s" % (self.uuid_id))
msg.attach(body)
att = email.mime.application.MIMEApplication(data,_subtype="raw")
att.add_header('Content-Disposition','attachment',filename = os.path.basename(self.filename))
msg.attach(att)
# Create a context factory which only allows SSLv3 and does not verify
# the peer's certificate.
contextFactory = ClientContextFactory()
contextFactory.method = SSLv3_METHOD
d = Deferred()
mime_obj = StringIO(str(msg))
senderFactory = ESMTPSenderFactory(
self.username,
self.password,
self.email_address,
self.email_address,
mime_obj,
d,
contextFactory=contextFactory)
d.addCallback(lambda r: self.email_sent(id, int(self.parts)) )
d.addErrback(self.email_error)
reactor.connectTCP(self.smtp_server, self.smtp_port, senderFactory)
d.addCallback(self.upload_success, *args, **kw)
d.addErrback(self.upload_error, 1)
return d
def upload_success(self, result):
print "upload was succesful!"
def upload_error(self, result):
print "upload error"
def download_data(self, uid):
"""
Downloads data from the email server returns a deferred that will return with the data
"""
print "uid"
if __name__ == "__main__":
log.startLogging(sys.stdout)
d = connectToIMAPServer("imap.gmail.com:993", "email", "password")
def f(s):
print s
d.addCallback(lambda r: f("These are fired before the auth and examine callbacks, why?"))
d.addCallback(lambda r: f("These are fired before the auth and examine callbacks, why?"))
reactor.run()