# -*- Mode: Python -*- import imaplib import select class NoIdleError (Exception): pass class imap_filter (imaplib.IMAP4): idle_state = False idle_exit_flag = False def send (self, data): import sys if data.find ('LOGIN ') != -1: sys.stderr.write ('=> LOGIN \n') else: sys.stderr.write ('=> %r\n' % (data,)) imaplib.IMAP4.send (self, data) def get_delimiter (self): status, data = self.list ("", "INBOX") if status == 'OK': delim = data[0].split()[-2] # hopefully not too scary delim = eval (delim) self.delimiter = delim print 'delimiter character = %r' % (delim,) def go_idle (self): if 'IDLE' not in self.capabilities: raise NoIdleError ("idle not supported by server. sorry.") self.idle_hooks = [] fd = self.sock.fileno() while 1: # this loop send the IDLE command repeatedly self.send ('%s IDLE\r\n' % (self._new_tag(),)) self.idle_state = True try: # this loop fetches multiple untagged responses # from the IDLE command... while 1: r = None # refresh every 15 minutes rfd, wfd, efd = select.select ( [fd], [], [], 15 * 60 ) if rfd: # _get_response() will trigger calls to # _append_untagged(), which will look for # 'hooked' events. If any hooks need to run, # they'll collect in self.idle_hooks, which # will tell us to exit the IDLE command so we # can move some stuff around. r = self._get_response() print 'response=%r' % (r,) else: # timeout, force a refresh self.idle_exit_flag = True print 'refresh' if self.idle_exit_flag: print 'sending DONE' # no tag on this one... self.send ('DONE\r\n') print 'DONE response=', self._get_response() self.idle_state = False self.process_hooks() self.idle_exit_flag = False break finally: self.idle_state = False def _append_untagged (self, typ, dat): if self.idle_state: hook_probe = getattr (self, 'hook_%s' % (typ.lower(),), None) if hook_probe: if self.idle_hooks is None: self.idle_hooks = [(hook_probe, dat)] else: self.idle_hooks.append ((hook_probe, dat)) self.idle_exit_flag = True else: imaplib.IMAP4._append_untagged (self, typ, dat) def process_hooks (self): for fun, dat in self.idle_hooks: fun (dat) self.idle_hooks = [] movement_rules = [ ('\r\nX-Spam-Flag: YES', "INBOX.spam"), ('\r\nX-Fnord-Flag: YES', "INBOX.spam"), # for testing ('\r\nSubject: cvs[ \r\n\t]+\\[', "INBOX.cvs"), ('\r\nFrom: dogfood-euq@ironport.com', "INBOX.euq"), ('\r\nList-Id: [^\r\n]+\r\n', "INBOX.stackless"), ('\r\nList-Id: \r\n', "INBOX.mac-users") ] def hook_exists (self, dat): import re uid, header = self.fetch (dat, "(UID RFC822.HEADER)") # pull the actual header text out header = header[0][1] #print 'header= %r' % (header,) moved = False for search, moveto in self.movement_rules: if re.search (search, header) is not None: self.store (dat, 'FLAGS', '(\Seen)') # replace delimiter in mailbox names moveto = moveto.replace ('.', self.delimiter) self.copy (dat, moveto) self.store (dat, 'FLAGS', '(\Deleted)') moved = True if __name__ == '__main__': import getpass import sys host = sys.argv[1] user = sys.argv[2] if len(sys.argv) > 3: port = int (sys.argv[3]) pwd = getpass.getpass() c = imap_filter (host) #c.debug = 5 c.login (user, pwd) c.get_delimiter() c.select ("INBOX") c.go_idle()