Hi, all! twisted.mail so far has not been coming along nicely. However, I now have some code which when tested will finally bring us to the point where we can have a working e-mail system. Well, not really -- we'll still need to handle *sending* e-mail, which isn't as easy async as you may think... :( Let me say a few general words about the way it works: I wanted to have the same seperation of responsibilities that brings us seperate handler/server/protocol-transformer to continue onto actually saving e-mails. Towards this end, I've made the server responsible for managing the list of domains, each of which is an object which can check if it wants to accepts for certain localparts, and to save the e-mail. This should bring us a higher level of testability, since we can have dummy domains which just remember the e-mail messages saved, and then we can bombard the handler with .handleData from us, and so on. But, off to a better life and so on, here is the code for the pre-alpha SMTP server: from twisted.protocols import smtp class VirtualSMTPHandler(smtp.SMTPHandler): seq = 0 def validateTo(self, helo, destination): try: domain = string.split(destination, '@', 1)[1] except IndexError: return 0 if not self.handler.server.domains.has_key(domain): return 0 if not self.handler.server.domains[domain].exists(user): return 0 return 1 def handleMessage(self, helo, origin, recipients, message): for recipient in recipients: user, domain = string.split(destination, '@', 1) self.handler.server.domains[domain].save_message(user, message) class SMTPNetHandler(net.GenericHandler): handler = None def handleData(self, data): if self.handler is None: self.handler = VirtualMaildirSMTPHandler(self) self.handler.handleData(data) def connectionLost(self, why): self.handler = None net.GenericHandler.connectionLost(self, why) class VirtualSMTPServer(net.GenericServer): handler = SMTPNetHandler def __init__(self, *args, **kw): apply(net.GenericServer.__init__, (self,)+args, kw) self.domains = {} def addDomain(self, name, domain): self.domains[name] = domain n = 0 class AbstractMaildirDomain: def __init__(self, root): self.root = root def user_directory(self, user): return None def exists(self, name): return self.user_directory(user) is not None def save_message(self, name, message): dir = self.user_directory(user) name = self._generateMaildirName() filename = os.path.join(dir, 'new', name) fp = open(filename, 'w') try: fp.write(message) finally: fp.close() def _generateMaildirName(self): global n t = str(int(time.time())) s = socket.gethostname() p = os.getpid() n = n+1 return '%s.%s_%s.%s' % (t, p, n, s) class BounceDomain: def exists(self, name): return 0 class DirectoryExistanceMaildirDomain(AbstractMaildirDomain): def user_directory(self, name): dir = os.path.join(self.root, name)): if os.path.isdir(dir): return dir class PostmasterMaildirDomain(AbstractMaildirDomain): def user_directory(self, name): dir = os.path.join(self.root, name)): if os.path.isdir(dir): return dir return os.path.join(self.root, 'postmaster') # This wasn't in the original code, but is a nice example: class SubdomainManager: def __init__(self, domains={}): self.domains = {} self.domains.update(domains) def addDomain(self, name, domain): self.domains[name] = domain def exists(self, user): if not '%' in user: return 0 localpart, remote = string.split(user, '%', 1) if not self.domains.has_key(remote): return 0 if not self.domains[remote].exists(localpart): return 0 return 1 def save_message(self, name, message): localpart, remote = string.split(user, '%', 1) return self.domains[remote].save_message(localpart, message) -- "I'll be ex-DPL soon anyway so I'm |LUKE: Is Perl better than Python? looking for someplace else to grab power."|YODA: No...no... no. Quicker, -- Wichert Akkerman (on debian-private)| easier, more seductive. For public key, finger moshez@debian.org |http://www.{python,debian,gnu}.org