+++ /dev/null
-#!/usr/bin/python
-
-# Original source by Sam Burnett <sburnett@cc.gatech.edu>
-# http://www.cc.gatech.edu/~sburnett/posts/2010-11-21-imap-idle.html
-#
-# Amended by Julien Valroff <julien@kirya.net> to support getting passwords from
-# GNOME keyring and falling back to using netrc file
-# This is done through ~/.offlineimap.py from
-# http://git.kirya.net/?p=dotfiles/offlineimap.git;a=blob_plain;f=.offlineimap.py;hb=master
-#
-# requires:
-# * offlineimap
-# * python-twisted-core
-# * python-openssl
-
-from OpenSSL import SSL
-import sys
-import os.path
-import subprocess
-import re
-import imp
-import ConfigParser
-from optparse import OptionParser
-
-from twisted.internet.protocol import ReconnectingClientFactory
-from twisted.protocols.basic import LineOnlyReceiver
-from twisted.internet import ssl, reactor
-from twisted.protocols.policies import TimeoutMixin
-from twisted.python import log
-
-class ImapIdleClient(LineOnlyReceiver, TimeoutMixin):
- def __init__(self, username, password, mailbox, account, idle_timeout):
- self.init_state = 'init'
- self.state_machine = { 'init': ('login', self.wait_for_hello)
- , 'login': ('examine', self.login)
- , 'examine': ('idle', self.examine)
- , 'idle': ('done', self.idle)
- , 'done': ('idle', self.done_idle)
- }
-
- self.state = self.init_state
- self.username = username
- self.password = password
- self.mailbox = mailbox
- self.account = account
- self.idle_timeout = idle_timeout
-
- def connectionMade(self):
- self.factory.resetDelay()
-
- def connectionLost(self, reason):
- print 'connection for %s:%s lost (%s)' % (self.account, self.mailbox, reason)
-
- def lineReceived(self, line):
- log.msg('[%s:%s] (%s) %s' % (self.account, self.mailbox, self.state, line))
-
- (next_state, action) = self.state_machine[self.state]
- if action(line):
- self.state = next_state
- log.msg('[%s:%s] Transitioning to state %s' % (self.account, self.mailbox, self.state))
-
- def wait_for_hello(self, line):
- if re.match(r'\* OK', line) is not None:
- self.sendLine('. login %s "%s"' % (self.username, self.password))
- return True
- print 'Logging in'
- else:
- return False
-
- def login(self, line):
- if re.match(r'\. OK', line) is not None:
- self.sendLine('. examine %s' % self.mailbox)
- return True
- print 'Examining mailbox'
- else:
- return False
-
- def examine(self, line):
- if re.match(r'\. OK', line) is not None:
- self.sendLine('. idle')
- self.setTimeout(self.idle_timeout)
- return True
- print 'Going idle'
- else:
- return False
-
- def idle(self, line):
- if re.match(r'\* \d+ (EXISTS|EXPUNGE)', line) is not None:
- subprocess.Popen(['offlineimap', '-a', self.account, '-f', self.mailbox, '-o', '-k', 'mbnames:enabled=no'])
- self.sendLine('DONE')
- return True
- print 'Done idleing (new mail)'
- else:
- return False
-
- def done_idle(self, line):
- if re.match(r'\. OK', line) is not None:
- self.sendLine('. idle')
- self.setTimeout(self.idle_timeout)
- return True
- print 'Idling again'
- else:
- return False
-
- def timeoutConnection(self):
- self.state = 'done'
- self.sendLine('DONE')
- print 'Done idle (timeout)'
-
-class ImapClientFactory(ReconnectingClientFactory):
- def __init__(self, server, mailbox, account, timeout):
- path = os.path.expanduser("~/.offlineimap.py")
- pythonfile = imp.load_source("pythonfile", path)
-
- self.username = pythonfile.get_username(server)
- self.password = pythonfile.get_password(server)
- self.mailbox = mailbox
- self.account = account
- self.timeout = 60*timeout
-
- def buildProtocol(self, addr):
- proto = ImapIdleClient(self.username, self.password, self.mailbox, self.account, self.timeout)
- proto.factory = self
- return proto
-
-class AccountOptions(object):
- server = None
- port = 993
- mailboxes = None
- timeout = 29
-
-def parse_options():
- usage = "usage: %s [options] [accounts to sync (overrides config file)]"
- parser = OptionParser(usage=usage)
- parser.set_defaults(config=None)
- parser.add_option('-c', '--configuration', dest='config',
- action='store', type='string',
- help='Alternate location of configuration file')
- (options, args) = parser.parse_args()
-
- if options.config is not None:
- config_path = options.config
- else:
- config_path = os.path.expanduser('~/.idlemailrc')
-
- if not os.path.isfile(config_path):
- parser.error("Configuration file %s doesn't exist" % config_path)
-
- if len(args) > 0:
- accounts = args
- else:
- accounts = None
-
- cfg = ConfigParser.ConfigParser()
- cfg.read(config_path)
-
- if cfg.has_section('general'):
- if cfg.has_option('general', 'accounts') and accounts is None:
- accounts = re.split('[, \t]+', cfg.get('general', 'accounts'))
-
- account_settings = {}
-
- for section in cfg.sections():
- if section.split()[0].lower() != 'account':
- continue
-
- name = section.split(None, 1)[1].strip()
- if accounts is not None and name not in accounts:
- continue
-
- account = AccountOptions()
- account.server = cfg.get(section, 'server')
- if cfg.has_option(section, 'port'):
- account.port = cfg.getint(section, 'port')
- boxes = cfg.get(section, 'mailboxes')
- account.mailboxes = re.split('[, \t]+', boxes)
- if cfg.has_option(section, 'timeout'):
- account.timeout = cfg.getfloat(section, 'timeout')
-
- account_settings[name] = account
-
- if accounts is not None:
- for account in accounts:
- if account not in account_settings:
- parser.error('Account not found: %s' % account)
-
- if len(account_settings.keys()) == 0:
- parser.error('No accounts defined')
-
- return account_settings
-
-def main():
- accounts = parse_options()
-
- log.startLogging(sys.stdout)
-
- for (name, settings) in accounts.items():
- for mailbox in settings.mailboxes:
- factory = ImapClientFactory(settings.server, mailbox, name, settings.timeout)
- reactor.connectSSL(settings.server, settings.port, factory, ssl.ClientContextFactory())
-
- reactor.run()
-
-if __name__ == '__main__':
- main()