/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:
parent
d64138c228
commit
4527b82221
@ -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
|
offlineimap (3.99.2) unstable; urgency=low
|
||||||
|
|
||||||
* Further attempts to fix imapsplit problems.
|
* Further attempts to fix imapsplit problems.
|
||||||
|
@ -48,11 +48,15 @@ class UsefulIMAP4_SSL(UsefulIMAPMixIn, imaplib.IMAP4_SSL): pass
|
|||||||
class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplib.IMAP4_Tunnel): pass
|
class UsefulIMAP4_Tunnel(UsefulIMAPMixIn, imaplib.IMAP4_Tunnel): pass
|
||||||
|
|
||||||
class IMAPServer:
|
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,
|
port = None, ssl = 1, maxconnections = 1, tunnel = None,
|
||||||
reference = '""'):
|
reference = '""'):
|
||||||
|
self.account = accountname
|
||||||
|
self.config = config
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self.passworderror = None
|
||||||
self.hostname = hostname
|
self.hostname = hostname
|
||||||
self.tunnel = tunnel
|
self.tunnel = tunnel
|
||||||
self.port = port
|
self.port = port
|
||||||
@ -72,6 +76,16 @@ class IMAPServer:
|
|||||||
self.connectionlock = Lock()
|
self.connectionlock = Lock()
|
||||||
self.reference = reference
|
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):
|
def getdelim(self):
|
||||||
"""Returns this server's folder delimiter. Can only be called
|
"""Returns this server's folder delimiter. Can only be called
|
||||||
after one or more calls to acquireconnection."""
|
after one or more calls to acquireconnection."""
|
||||||
@ -92,7 +106,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
def md5handler(self, response):
|
def md5handler(self, response):
|
||||||
challenge = response.strip()
|
challenge = response.strip()
|
||||||
msg = self.password
|
msg = self.getpassword()
|
||||||
while len(msg) < 64:
|
while len(msg) < 64:
|
||||||
msg += "\0"
|
msg += "\0"
|
||||||
|
|
||||||
@ -135,23 +149,31 @@ class IMAPServer:
|
|||||||
|
|
||||||
UIBase.getglobalui().connecting(self.hostname, self.port)
|
UIBase.getglobalui().connecting(self.hostname, self.port)
|
||||||
|
|
||||||
# Generate a new connection.
|
success = 0
|
||||||
if self.tunnel:
|
while not success:
|
||||||
imapobj = UsefulIMAP4_Tunnel(self.tunnel)
|
# Generate a new connection.
|
||||||
elif self.usessl:
|
if self.tunnel:
|
||||||
imapobj = UsefulIMAP4_SSL(self.hostname, self.port)
|
imapobj = UsefulIMAP4_Tunnel(self.tunnel)
|
||||||
else:
|
elif self.usessl:
|
||||||
imapobj = UsefulIMAP4(self.hostname, self.port)
|
imapobj = UsefulIMAP4_SSL(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)
|
|
||||||
else:
|
else:
|
||||||
UIBase.getglobalui().debug('imap',
|
imapobj = UsefulIMAP4(self.hostname, self.port)
|
||||||
'Attempting plain authentication')
|
|
||||||
imapobj.login(self.username, self.password)
|
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:
|
if self.delim == None:
|
||||||
listres = imapobj.list(self.reference, '""')[1]
|
listres = imapobj.list(self.reference, '""')[1]
|
||||||
@ -249,13 +271,19 @@ class ConfigedIMAPServer(IMAPServer):
|
|||||||
|
|
||||||
# Connect to the remote server.
|
# Connect to the remote server.
|
||||||
if usetunnel:
|
if usetunnel:
|
||||||
IMAPServer.__init__(self,
|
IMAPServer.__init__(self, config, accountname,
|
||||||
tunnel = config.get(accountname, "preauthtunnel"),
|
tunnel = config.get(accountname, "preauthtunnel"),
|
||||||
reference = reference,
|
reference = reference,
|
||||||
maxconnections = config.getint(accountname, "maxconnections"))
|
maxconnections = config.getint(accountname, "maxconnections"))
|
||||||
else:
|
else:
|
||||||
if not password:
|
if not password:
|
||||||
password = config.get(accountname, 'remotepass')
|
if config.has_option(accountname, 'remotepass'):
|
||||||
IMAPServer.__init__(self, user, password, host, port, ssl,
|
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"),
|
config.getint(accountname, "maxconnections"),
|
||||||
reference = reference)
|
reference = reference)
|
||||||
|
@ -90,8 +90,6 @@ def startup(versionno):
|
|||||||
server = None
|
server = None
|
||||||
remoterepos = None
|
remoterepos = None
|
||||||
localrepos = None
|
localrepos = None
|
||||||
passwords = {}
|
|
||||||
tunnels = {}
|
|
||||||
|
|
||||||
if '-1' in options:
|
if '-1' in options:
|
||||||
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
|
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
|
||||||
@ -99,23 +97,7 @@ def startup(versionno):
|
|||||||
threadutil.initInstanceLimit("ACCOUNTLIMIT",
|
threadutil.initInstanceLimit("ACCOUNTLIMIT",
|
||||||
config.getint("general", "maxsyncaccounts"))
|
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:
|
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]:
|
for instancename in ["FOLDER_" + account, "MSGCOPY_" + account]:
|
||||||
if '-1' in options:
|
if '-1' in options:
|
||||||
threadutil.initInstanceLimit(instancename, 1)
|
threadutil.initInstanceLimit(instancename, 1)
|
||||||
@ -133,7 +115,6 @@ def startup(versionno):
|
|||||||
'metadatadir': metadatadir,
|
'metadatadir': metadatadir,
|
||||||
'servers': servers,
|
'servers': servers,
|
||||||
'config': config,
|
'config': config,
|
||||||
'passwords': passwords,
|
|
||||||
'localeval': localeval})
|
'localeval': localeval})
|
||||||
t.setDaemon(1)
|
t.setDaemon(1)
|
||||||
t.start()
|
t.start()
|
||||||
|
@ -23,7 +23,7 @@ import re, os, os.path, offlineimap, sys
|
|||||||
from ConfigParser import ConfigParser
|
from ConfigParser import ConfigParser
|
||||||
from threading import *
|
from threading import *
|
||||||
|
|
||||||
def syncaccount(accountname, metadatadir, servers, config, passwords,
|
def syncaccount(accountname, metadatadir, servers, config,
|
||||||
localeval, *args):
|
localeval, *args):
|
||||||
ui = UIBase.getglobalui()
|
ui = UIBase.getglobalui()
|
||||||
# We don't need an account lock because syncitall() goes through
|
# 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:
|
if accountname in servers:
|
||||||
server = servers[accountname]
|
server = servers[accountname]
|
||||||
else:
|
else:
|
||||||
server = imapserver.ConfigedIMAPServer(config, accountname, passwords)
|
server = imapserver.ConfigedIMAPServer(config, accountname)
|
||||||
servers[accountname] = server
|
servers[accountname] = server
|
||||||
|
|
||||||
remoterepos = repository.IMAP.IMAPRepository(config, localeval, 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()
|
ui = UIBase.getglobalui()
|
||||||
global mailboxes
|
global mailboxes
|
||||||
mailboxes = [] # Reset.
|
mailboxes = [] # Reset.
|
||||||
@ -157,7 +157,7 @@ def syncitall(accounts, metadatadir, servers, config, passwords, localeval):
|
|||||||
target = syncaccount,
|
target = syncaccount,
|
||||||
name = "Account sync %s" % accountname,
|
name = "Account sync %s" % accountname,
|
||||||
args = (accountname, metadatadir,
|
args = (accountname, metadatadir,
|
||||||
servers, config, passwords,
|
servers, config,
|
||||||
localeval))
|
localeval))
|
||||||
thread.setDaemon(1)
|
thread.setDaemon(1)
|
||||||
thread.start()
|
thread.start()
|
||||||
@ -166,11 +166,11 @@ def syncitall(accounts, metadatadir, servers, config, passwords, localeval):
|
|||||||
threadutil.threadsreset(threads)
|
threadutil.threadsreset(threads)
|
||||||
mbnames.genmbnames(config, localeval, mailboxes)
|
mbnames.genmbnames(config, localeval, mailboxes)
|
||||||
|
|
||||||
def sync_with_timer(accounts, metadatadir, servers, config, passwords,
|
def sync_with_timer(accounts, metadatadir, servers, config,
|
||||||
localeval):
|
localeval):
|
||||||
ui = UIBase.getglobalui()
|
ui = UIBase.getglobalui()
|
||||||
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
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'):
|
if config.has_option('general', 'autorefresh'):
|
||||||
refreshperiod = config.getint('general', 'autorefresh') * 60
|
refreshperiod = config.getint('general', 'autorefresh') * 60
|
||||||
while 1:
|
while 1:
|
||||||
@ -200,5 +200,5 @@ def sync_with_timer(accounts, metadatadir, servers, config, passwords,
|
|||||||
event.set()
|
event.set()
|
||||||
for thread in kathreads.values():
|
for thread in kathreads.values():
|
||||||
thread.join()
|
thread.join()
|
||||||
syncitall(accounts, metadatadir, servers, config, passwords,
|
syncitall(accounts, metadatadir, servers, config,
|
||||||
localeval)
|
localeval)
|
||||||
|
@ -25,21 +25,32 @@ class TTYUI(UIBase):
|
|||||||
def __init__(s, config, verbose = 0):
|
def __init__(s, config, verbose = 0):
|
||||||
UIBase.__init__(s, config, verbose)
|
UIBase.__init__(s, config, verbose)
|
||||||
s.iswaiting = 0
|
s.iswaiting = 0
|
||||||
|
s.outputlock = Lock()
|
||||||
|
|
||||||
def isusable(s):
|
def isusable(s):
|
||||||
return sys.stdout.isatty() and sys.stdin.isatty()
|
return sys.stdout.isatty() and sys.stdin.isatty()
|
||||||
|
|
||||||
def _msg(s, msg):
|
def _msg(s, msg):
|
||||||
if (currentThread().getName() == 'MainThread'):
|
s.outputlock.acquire()
|
||||||
print msg
|
try:
|
||||||
else:
|
if (currentThread().getName() == 'MainThread'):
|
||||||
print "%s:\n %s" % (currentThread().getName(), msg)
|
print msg
|
||||||
sys.stdout.flush()
|
else:
|
||||||
|
print "%s:\n %s" % (currentThread().getName(), msg)
|
||||||
|
sys.stdout.flush()
|
||||||
|
finally:
|
||||||
|
s.outputlock.release()
|
||||||
|
|
||||||
def getpass(s, accountname, config):
|
def getpass(s, accountname, config, errmsg = None):
|
||||||
return getpass("%s: Enter password for %s on %s: " %
|
if errmsg:
|
||||||
(accountname, config.get(accountname, "remoteuser"),
|
s._msg("%s: %s" % (accountname, errmsg))
|
||||||
config.get(accountname, "remotehost")))
|
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):
|
def sleep(s, sleepsecs):
|
||||||
s.iswaiting = 1
|
s.iswaiting = 1
|
||||||
|
@ -27,13 +27,16 @@ from Queue import Queue
|
|||||||
from UIBase import UIBase
|
from UIBase import UIBase
|
||||||
|
|
||||||
class PasswordDialog:
|
class PasswordDialog:
|
||||||
def __init__(self, accountname, config, master=None):
|
def __init__(self, accountname, config, master=None, errmsg = None):
|
||||||
self.top = Toplevel(master)
|
self.top = Toplevel(master)
|
||||||
self.top.title(version.productname + " Password Entry")
|
self.top.title(version.productname + " Password Entry")
|
||||||
self.label = Label(self.top,
|
text = ''
|
||||||
text = "%s: Enter password for %s on %s: " % \
|
if errmsg:
|
||||||
(accountname, config.get(accountname, "remoteuser"),
|
text = '%s: %s\n' % (accountname, errmsg)
|
||||||
config.get(accountname, "remotehost")))
|
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.label.pack()
|
||||||
|
|
||||||
self.entry = Entry(self.top, show='*')
|
self.entry = Entry(self.top, show='*')
|
||||||
@ -171,8 +174,8 @@ class VerboseUI(UIBase):
|
|||||||
s.top.mainloop()
|
s.top.mainloop()
|
||||||
s.notdeleted = 0
|
s.notdeleted = 0
|
||||||
|
|
||||||
def getpass(s, accountname, config):
|
def getpass(s, accountname, config, errmsg = None):
|
||||||
pd = PasswordDialog(accountname, config)
|
pd = PasswordDialog(accountname, config, errmsg = errmsg)
|
||||||
return pd.getpassword()
|
return pd.getpassword()
|
||||||
|
|
||||||
def gettf(s, newtype=ThreadFrame, master = None):
|
def gettf(s, newtype=ThreadFrame, master = None):
|
||||||
|
@ -92,7 +92,7 @@ class UIBase:
|
|||||||
|
|
||||||
################################################## INPUT
|
################################################## INPUT
|
||||||
|
|
||||||
def getpass(s, accountname, config):
|
def getpass(s, accountname, config, errmsg = None):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def folderlist(s, list):
|
def folderlist(s, list):
|
||||||
|
Loading…
Reference in New Issue
Block a user