diff -urN --exclude '*.pyc' offlineimap-4.0.0/offlineimap/imapserver.py offlineimap/offlineimap/imapserver.py --- offlineimap-4.0.0/offlineimap/imapserver.py 2003-06-11 03:19:29.000000000 +0200 +++ offlineimap/offlineimap/imapserver.py 2003-08-24 08:31:39.000000000 +0200 @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -from offlineimap import imaplib, imaputil, threadutil +from offlineimap import imaplib, imaputil, threadutil, passwordcache from offlineimap.ui import UIBase from threading import * import thread, hmac, os @@ -75,16 +75,28 @@ self.semaphore = BoundedSemaphore(self.maxconnections) self.connectionlock = Lock() self.reference = reference + self.pCache = passwordcache.PasswordCache() def getpassword(self): if self.password != None and self.passworderror == None: return self.password + self.pCache.acquire() + + pc = self.pCache.getPassword(self.username, self.hostname, self.port) + if pc != None and self.pCache.isPasswordFresh(self.username, self.hostname, self.port): + self.pCache.release() + self.passworderror = None + return pc + self.password = UIBase.getglobalui().getpass(self.reposname, self.config, self.passworderror) self.passworderror = None + self.pCache.setPassword(self.password, self.username, self.hostname, self.port) + self.pCache.release() + return self.password def getdelim(self): @@ -182,6 +194,7 @@ success = 1 except imapobj.error, val: self.passworderror = str(val) + self.pCache.registerError(self.username, self.hostname, self.port) self.password = None if self.delim == None: diff -urN --exclude '*.pyc' offlineimap-4.0.0/offlineimap/passwordcache.py offlineimap/offlineimap/passwordcache.py --- offlineimap-4.0.0/offlineimap/passwordcache.py 1970-01-01 01:00:00.000000000 +0100 +++ offlineimap/offlineimap/passwordcache.py 2003-08-24 08:29:12.000000000 +0200 @@ -0,0 +1,75 @@ +# Password cache +# Copyright (C) 2003 Hugo Haas +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# This class implement a password cache for OfflineIMAP's IMAP server. +# This is used when different accounts are accessing the same host. +# This is an improved version of the solution I posted originally for bug #48: +# Ticket #48 Password prompts too often +# http://bugs.complete.org/Ticket/Display.html?id=48 + +import thread +from threading import * +from time import time + +class PasswordCache: + lock = Lock() + cache = {} + cacheTS = {} + cacheErrorTS = {} + + # Locking operations + def acquire(self): + self.lock.acquire() + def release(self): + self.lock.release() + + # Generate a key for a (user, host, port) triple + def __key(self, user, host, port): + return "%s@%s:%s" % (user, host, port) + + # Get a password from the cache + def getPassword(self, u, h, p): + try: + return self.cache[self.__key(u, h, p)] + except KeyError: + return None + + # Set a password for an entry + def setPassword(self, pwd, u, h, p): + key = self.__key(u, h, p) + self.cache[key] = pwd + self.cacheTS[key] = time() + + # Register an error + def registerError(self, u, h, p): + self.cacheErrorTS[self.__key(u, h, p)] = time() + + # Check whether an error occured after the password was entered + # or whether a password was reentered after an error occured + # (global passworderror) + def isPasswordFresh(self, u, h, p): + key = self.__key(u, h, p) + if not self.cacheTS[key]: + return 0 + try: + if self.cacheTS[key] - self.cacheErrorTS[key] > 0: + return 1 + else: + return 0 + except KeyError: + return 1