/offlineimap/head: changeset 278

Moved password promting into imapserver.py. Passwords are now asked
for on-demand and typos will no longer crash the program (the user
will be re-prompted). Closes: #162672.
This commit is contained in:
jgoerzen 2002-11-05 00:15:42 +01:00
parent d64138c228
commit 4527b82221
7 changed files with 95 additions and 64 deletions

View File

@ -1,3 +1,11 @@
offlineimap (3.99.3) unstable; urgency=low
* Moved password promting into imapserver.py. Passwords are now asked
for on-demand and typos will no longer crash the program (the user
will be re-prompted). Closes: #162672.
-- John Goerzen <jgoerzen@complete.org> Mon, 4 Nov 2002 11:15:11 -0600
offlineimap (3.99.2) unstable; urgency=low
* Further attempts to fix imapsplit problems.

View File

@ -48,11 +48,15 @@ class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplib.IMAP4_SSL): pass
class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplib.IMAP4_Tunnel): pass
class IMAPServer:
def __init__(self, username = None, password = None, hostname = None,
def __init__(self, config, accountname,
username = None, password = None, hostname = None,
port = None, ssl = 1, maxconnections = 1, tunnel = None,
reference = '""'):
self.account = accountname
self.config = config
self.username = username
self.password = password
self.passworderror = None
self.hostname = hostname
self.tunnel = tunnel
self.port = port
@ -72,6 +76,16 @@ class IMAPServer:
self.connectionlock = Lock()
self.reference = reference
def getpassword(self):
if self.password != None and self.passworderror == None:
return self.password
self.password = UIBase.getglobalui().getpass(self.account, self.config,
self.passworderror)
self.passworderror = None
return self.password
def getdelim(self):
"""Returns this server's folder delimiter. Can only be called
after one or more calls to acquireconnection."""
@ -92,7 +106,7 @@ class IMAPServer:
def md5handler(self, response):
challenge = response.strip()
msg = self.password
msg = self.getpassword()
while len(msg) < 64:
msg += "\0"
@ -135,23 +149,31 @@ class IMAPServer:
UIBase.getglobalui().connecting(self.hostname, self.port)
# Generate a new connection.
if self.tunnel:
imapobj = UsefulIMAP4_Tunnel(self.tunnel)
elif self.usessl:
imapobj = UsefulIMAP4_SSL(self.hostname, self.port)
else:
imapobj = UsefulIMAP4(self.hostname, self.port)
if not self.tunnel:
if 'AUTH=CRAM-MD5' in imapobj.capabilities:
UIBase.getglobalui().debug('imap',
'Attempting CRAM-MD5 authentication')
imapobj.authenticate('CRAM-MD5', self.md5handler)
success = 0
while not success:
# Generate a new connection.
if self.tunnel:
imapobj = UsefulIMAP4_Tunnel(self.tunnel)
elif self.usessl:
imapobj = UsefulIMAP4_SSL(self.hostname, self.port)
else:
UIBase.getglobalui().debug('imap',
'Attempting plain authentication')
imapobj.login(self.username, self.password)
imapobj = UsefulIMAP4(self.hostname, self.port)
if not self.tunnel:
try:
if 'AUTH=CRAM-MD5' in imapobj.capabilities:
UIBase.getglobalui().debug('imap',
'Attempting CRAM-MD5 authentication')
imapobj.authenticate('CRAM-MD5', self.md5handler)
else:
UIBase.getglobalui().debug('imap',
'Attempting plain authentication')
imapobj.login(self.username, self.getpassword())
# Would bail by here if there was a failure.
success = 1
except imapobj.error, val:
self.passworderror = str(val)
self.password = None
if self.delim == None:
listres = imapobj.list(self.reference, '""')[1]
@ -249,13 +271,19 @@ class ConfigedIMAPServer(IMAPServer):
# Connect to the remote server.
if usetunnel:
IMAPServer.__init__(self,
IMAPServer.__init__(self, config, accountname,
tunnel = config.get(accountname, "preauthtunnel"),
reference = reference,
maxconnections = config.getint(accountname, "maxconnections"))
else:
if not password:
password = config.get(accountname, 'remotepass')
IMAPServer.__init__(self, user, password, host, port, ssl,
if config.has_option(accountname, 'remotepass'):
password = config.get(accountname, 'remotepass')
elif config.has_option(accountname, 'remotepassfile'):
passfile = open(os.path.expanduser(config.get(accountname, "remotepassfile")))
password = passfile.readline().strip()
passfile.close()
IMAPServer.__init__(self, config, accountname,
user, password, host, port, ssl,
config.getint(accountname, "maxconnections"),
reference = reference)

View File

@ -90,8 +90,6 @@ def startup(versionno):
server = None
remoterepos = None
localrepos = None
passwords = {}
tunnels = {}
if '-1' in options:
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
@ -99,23 +97,7 @@ def startup(versionno):
threadutil.initInstanceLimit("ACCOUNTLIMIT",
config.getint("general", "maxsyncaccounts"))
# We have to gather passwords here -- don't want to have two threads
# asking for passwords simultaneously.
for account in accounts:
#if '.' in account:
# raise ValueError, "Account '%s' contains a dot; dots are not " \
# "allowed in account names." % account
if config.has_option(account, "preauthtunnel"):
tunnels[account] = config.get(account, "preauthtunnel")
elif config.has_option(account, "remotepass"):
passwords[account] = config.get(account, "remotepass")
elif config.has_option(account, "remotepassfile"):
passfile = open(os.path.expanduser(config.get(account, "remotepassfile")))
passwords[account] = passfile.readline().strip()
passfile.close()
else:
passwords[account] = ui.getpass(account, config)
for instancename in ["FOLDER_" + account, "MSGCOPY_" + account]:
if '-1' in options:
threadutil.initInstanceLimit(instancename, 1)
@ -133,7 +115,6 @@ def startup(versionno):
'metadatadir': metadatadir,
'servers': servers,
'config': config,
'passwords': passwords,
'localeval': localeval})
t.setDaemon(1)
t.start()

View File

@ -23,7 +23,7 @@ import re, os, os.path, offlineimap, sys
from ConfigParser import ConfigParser
from threading import *
def syncaccount(accountname, metadatadir, servers, config, passwords,
def syncaccount(accountname, metadatadir, servers, config,
localeval, *args):
ui = UIBase.getglobalui()
# We don't need an account lock because syncitall() goes through
@ -38,7 +38,7 @@ def syncaccount(accountname, metadatadir, servers, config, passwords,
if accountname in servers:
server = servers[accountname]
else:
server = imapserver.ConfigedIMAPServer(config, accountname, passwords)
server = imapserver.ConfigedIMAPServer(config, accountname)
servers[accountname] = server
remoterepos = repository.IMAP.IMAPRepository(config, localeval, accountname, server)
@ -147,7 +147,7 @@ def syncfolder(accountname, remoterepos, remotefolder, localrepos,
def syncitall(accounts, metadatadir, servers, config, passwords, localeval):
def syncitall(accounts, metadatadir, servers, config, localeval):
ui = UIBase.getglobalui()
global mailboxes
mailboxes = [] # Reset.
@ -157,7 +157,7 @@ def syncitall(accounts, metadatadir, servers, config, passwords, localeval):
target = syncaccount,
name = "Account sync %s" % accountname,
args = (accountname, metadatadir,
servers, config, passwords,
servers, config,
localeval))
thread.setDaemon(1)
thread.start()
@ -166,11 +166,11 @@ def syncitall(accounts, metadatadir, servers, config, passwords, localeval):
threadutil.threadsreset(threads)
mbnames.genmbnames(config, localeval, mailboxes)
def sync_with_timer(accounts, metadatadir, servers, config, passwords,
def sync_with_timer(accounts, metadatadir, servers, config,
localeval):
ui = UIBase.getglobalui()
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
syncitall(accounts, metadatadir, servers, config, passwords, localeval)
syncitall(accounts, metadatadir, servers, config, localeval)
if config.has_option('general', 'autorefresh'):
refreshperiod = config.getint('general', 'autorefresh') * 60
while 1:
@ -200,5 +200,5 @@ def sync_with_timer(accounts, metadatadir, servers, config, passwords,
event.set()
for thread in kathreads.values():
thread.join()
syncitall(accounts, metadatadir, servers, config, passwords,
syncitall(accounts, metadatadir, servers, config,
localeval)

View File

@ -25,21 +25,32 @@ class TTYUI(UIBase):
def __init__(s, config, verbose = 0):
UIBase.__init__(s, config, verbose)
s.iswaiting = 0
s.outputlock = Lock()
def isusable(s):
return sys.stdout.isatty() and sys.stdin.isatty()
def _msg(s, msg):
if (currentThread().getName() == 'MainThread'):
print msg
else:
print "%s:\n %s" % (currentThread().getName(), msg)
sys.stdout.flush()
s.outputlock.acquire()
try:
if (currentThread().getName() == 'MainThread'):
print msg
else:
print "%s:\n %s" % (currentThread().getName(), msg)
sys.stdout.flush()
finally:
s.outputlock.release()
def getpass(s, accountname, config):
return getpass("%s: Enter password for %s on %s: " %
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost")))
def getpass(s, accountname, config, errmsg = None):
if errmsg:
s._msg("%s: %s" % (accountname, errmsg))
s.outputlock.acquire()
try:
return getpass("%s: Enter password for %s on %s: " %
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost")))
finally:
s.outputlock.release()
def sleep(s, sleepsecs):
s.iswaiting = 1

View File

@ -27,13 +27,16 @@ from Queue import Queue
from UIBase import UIBase
class PasswordDialog:
def __init__(self, accountname, config, master=None):
def __init__(self, accountname, config, master=None, errmsg = None):
self.top = Toplevel(master)
self.top.title(version.productname + " Password Entry")
self.label = Label(self.top,
text = "%s: Enter password for %s on %s: " % \
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost")))
text = ''
if errmsg:
text = '%s: %s\n' % (accountname, errmsg)
text += "%s: Enter password for %s on %s: " % \
(accountname, config.get(accountname, "remoteuser"),
config.get(accountname, "remotehost"))
self.label = Label(self.top, text = text)
self.label.pack()
self.entry = Entry(self.top, show='*')
@ -171,8 +174,8 @@ class VerboseUI(UIBase):
s.top.mainloop()
s.notdeleted = 0
def getpass(s, accountname, config):
pd = PasswordDialog(accountname, config)
def getpass(s, accountname, config, errmsg = None):
pd = PasswordDialog(accountname, config, errmsg = errmsg)
return pd.getpassword()
def gettf(s, newtype=ThreadFrame, master = None):

View File

@ -92,7 +92,7 @@ class UIBase:
################################################## INPUT
def getpass(s, accountname, config):
def getpass(s, accountname, config, errmsg = None):
raise NotImplementedError
def folderlist(s, list):