/offlineimap/head: changeset 297
Changed to a more account-centric behavior. The refresh time is now a per-account variable. Implemented new account classes. User interfaces must now be updated to take advantage of this.
This commit is contained in:
46
offlineimap/head/offlineimap/CustomConfig.py
Normal file
46
offlineimap/head/offlineimap/CustomConfig.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright (C) 2003 John Goerzen
|
||||
# <jgoerzen@complete.org>
|
||||
#
|
||||
# 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
|
||||
|
||||
from ConfigParser import ConfigParser
|
||||
from offlineimap.localeval import LocalEval
|
||||
import os
|
||||
|
||||
class CustomConfigParser(ConfigParser):
|
||||
def getdefault(self, section, option, default, *args, **kwargs):
|
||||
"""Same as config.get, but returns the "default" option if there
|
||||
is no such option specified."""
|
||||
if self.has_option(section, option):
|
||||
return apply(self.get, [section, option] + list(args), kwargs)
|
||||
else:
|
||||
return default
|
||||
|
||||
def getmetadatadir(self):
|
||||
metadatadir = os.path.expanduser(self.getdefault("general", "metadata", "~/.offlineimap"))
|
||||
if not os.path.exists(metadatadir):
|
||||
os.mkdir(metadatadir, 0700)
|
||||
return metadatadir
|
||||
|
||||
def getlocaleval(self):
|
||||
if self.has_option("general", "pythonfile"):
|
||||
path = os.path.expanduser(self.get("general", "pythonfile"))
|
||||
else:
|
||||
path = None
|
||||
return LocalEval(path)
|
||||
|
||||
def getaccountlist(self):
|
||||
return [x for x in self.sections() if x != 'general']
|
||||
|
211
offlineimap/head/offlineimap/accounts.py
Normal file
211
offlineimap/head/offlineimap/accounts.py
Normal file
@ -0,0 +1,211 @@
|
||||
# Copyright (C) 2003 John Goerzen
|
||||
# <jgoerzen@complete.org>
|
||||
#
|
||||
# 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
|
||||
|
||||
from offlineimap import imapserver, repository, threadutil
|
||||
from offlineimap.ui import UIBase
|
||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||
from threading import Event
|
||||
import os
|
||||
|
||||
mailboxes = []
|
||||
|
||||
class Account:
|
||||
def __init__(self, config, name):
|
||||
self.config = config
|
||||
self.name = name
|
||||
self.metadatadir = config.getmetadatadir()
|
||||
self.localeval = config.getlocaleval()
|
||||
self.server = imapserver.ConfigedIMAPServer(config, self.name)
|
||||
self.ui = UIBase.getglobalui()
|
||||
if self.config.has_option(self.name, 'autorefresh'):
|
||||
self.refreshperiod = self.config.getint(self.name, 'autorefresh')
|
||||
else:
|
||||
self.refreshperiod = None
|
||||
self.hold = self.config.has_option(self.name, 'holdconnectionopen') \
|
||||
and self.config.getboolean(self.name, 'holdconnectionopen')
|
||||
if self.config.has_option(self.name, 'keepalive'):
|
||||
self.keepalive = self.config.getint(self.name, 'keepalive')
|
||||
else:
|
||||
self.keepalive = None
|
||||
|
||||
def getconf(self, option, default = None):
|
||||
if default != None:
|
||||
return self.config.get(self.name, option)
|
||||
else:
|
||||
return self.config.getdefault(self.name, option,
|
||||
default)
|
||||
|
||||
def sleeper(self):
|
||||
"""Sleep handler. Returns same value as UIBase.sleep:
|
||||
0 if timeout expired, 1 if there was a request to cancel the timer,
|
||||
and 2 if there is a request to abort the program.
|
||||
|
||||
Also, returns 100 if configured to not sleep at all."""
|
||||
|
||||
if not self.refreshperiod:
|
||||
return 100
|
||||
refreshperiod = self.refreshperiod * 60
|
||||
if self.keepalive:
|
||||
kaevent = Event()
|
||||
kathread = ExitNotifyThread(target = self.server.keepalive,
|
||||
name = "Keep alive " + self.name,
|
||||
args = (self.keepalive, kaevent))
|
||||
kathread.setDaemon(1)
|
||||
kathread.start()
|
||||
sleepresult = self.ui.sleep(refreshperiod)
|
||||
if sleepresult == 2:
|
||||
# Cancel keep-alive, but don't bother terminating threads
|
||||
if self.keepalive:
|
||||
kaevent.set()
|
||||
return sleepresult
|
||||
else:
|
||||
# Cancel keep-alive and wait for thread to terminate.
|
||||
if self.keepalive:
|
||||
kaevent.set()
|
||||
kathread.join()
|
||||
return sleepresult
|
||||
|
||||
class AccountSynchronizationMixin:
|
||||
def syncrunner(self):
|
||||
self.ui.acct(self.name)
|
||||
if not self.refreshperiod:
|
||||
self.sync()
|
||||
self.ui.acctdone(self.name)
|
||||
return
|
||||
looping = 1
|
||||
while looping:
|
||||
self.sync()
|
||||
looping = self.sleeper() != 2
|
||||
self.ui.acctdone(self.name)
|
||||
|
||||
def sync(self):
|
||||
# We don't need an account lock because syncitall() goes through
|
||||
# each account once, then waits for all to finish.
|
||||
try:
|
||||
accountmetadata = os.path.join(self.metadatadir, self.name)
|
||||
if not os.path.exists(accountmetadata):
|
||||
os.mkdir(accountmetadata, 0700)
|
||||
|
||||
remoterepos = repository.IMAP.IMAPRepository(self.config,
|
||||
self.localeval,
|
||||
self.name,
|
||||
self.server)
|
||||
|
||||
# Connect to the Maildirs.
|
||||
localrepos = repository.Maildir.MaildirRepository(os.path.expanduser(self.config.get(self.name, "localfolders")), self.name, self.config)
|
||||
|
||||
# Connect to the local cache.
|
||||
statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata)
|
||||
|
||||
self.ui.syncfolders(remoterepos, localrepos)
|
||||
remoterepos.syncfoldersto(localrepos)
|
||||
|
||||
folderthreads = []
|
||||
for remotefolder in remoterepos.getfolders():
|
||||
thread = InstanceLimitedThread(\
|
||||
instancename = 'FOLDER_' + self.name,
|
||||
target = syncfolder,
|
||||
name = "Folder sync %s[%s]" % \
|
||||
(self.name, remotefolder.getvisiblename()),
|
||||
args = (self.name, remoterepos, remotefolder, localrepos,
|
||||
statusrepos))
|
||||
thread.setDaemon(1)
|
||||
thread.start()
|
||||
folderthreads.append(thread)
|
||||
threadutil.threadsreset(folderthreads)
|
||||
if not self.hold:
|
||||
server.close()
|
||||
finally:
|
||||
pass
|
||||
|
||||
class SyncableAccount(Account, AccountSynchronizationMixin):
|
||||
pass
|
||||
|
||||
def syncfolder(accountname, remoterepos, remotefolder, localrepos,
|
||||
statusrepos):
|
||||
global mailboxes
|
||||
ui = UIBase.getglobalui()
|
||||
# Load local folder.
|
||||
localfolder = localrepos.\
|
||||
getfolder(remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(), localrepos.getsep()))
|
||||
# Write the mailboxes
|
||||
mailboxes.append({'accountname': accountname,
|
||||
'foldername': localfolder.getvisiblename()})
|
||||
# Load local folder
|
||||
ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder)
|
||||
ui.loadmessagelist(localrepos, localfolder)
|
||||
localfolder.cachemessagelist()
|
||||
ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys()))
|
||||
|
||||
|
||||
# Load status folder.
|
||||
statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(),
|
||||
statusrepos.getsep()))
|
||||
if localfolder.getuidvalidity() == None:
|
||||
# This is a new folder, so delete the status cache to be sure
|
||||
# we don't have a conflict.
|
||||
statusfolder.deletemessagelist()
|
||||
|
||||
statusfolder.cachemessagelist()
|
||||
|
||||
|
||||
# If either the local or the status folder has messages and
|
||||
# there is a UID validity problem, warn and abort.
|
||||
# If there are no messages, UW IMAPd loses UIDVALIDITY.
|
||||
# But we don't really need it if both local folders are empty.
|
||||
# So, in that case, save it off.
|
||||
if (len(localfolder.getmessagelist()) or \
|
||||
len(statusfolder.getmessagelist())) and \
|
||||
not localfolder.isuidvalidityok(remotefolder):
|
||||
ui.validityproblem(remotefolder)
|
||||
return
|
||||
else:
|
||||
localfolder.saveuidvalidity(remotefolder.getuidvalidity())
|
||||
|
||||
# Load remote folder.
|
||||
ui.loadmessagelist(remoterepos, remotefolder)
|
||||
remotefolder.cachemessagelist()
|
||||
ui.messagelistloaded(remoterepos, remotefolder,
|
||||
len(remotefolder.getmessagelist().keys()))
|
||||
|
||||
|
||||
#
|
||||
|
||||
if not statusfolder.isnewfolder():
|
||||
# Delete local copies of remote messages. This way,
|
||||
# if a message's flag is modified locally but it has been
|
||||
# deleted remotely, we'll delete it locally. Otherwise, we
|
||||
# try to modify a deleted message's flags! This step
|
||||
# need only be taken if a statusfolder is present; otherwise,
|
||||
# there is no action taken *to* the remote repository.
|
||||
|
||||
remotefolder.syncmessagesto_delete(localfolder, [localfolder,
|
||||
statusfolder])
|
||||
ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder)
|
||||
localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder])
|
||||
|
||||
# Synchronize remote changes.
|
||||
ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder)
|
||||
remotefolder.syncmessagesto(localfolder)
|
||||
|
||||
# Make sure the status folder is up-to-date.
|
||||
ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder)
|
||||
localfolder.syncmessagesto(statusfolder)
|
||||
statusfolder.save()
|
||||
|
@ -21,7 +21,7 @@ from offlineimap.localeval import LocalEval
|
||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||
from offlineimap.ui import UIBase
|
||||
import re, os, os.path, offlineimap, sys
|
||||
from ConfigParser import ConfigParser
|
||||
from offlineimap.CustomConfig import CustomConfigParser
|
||||
from threading import *
|
||||
from getopt import getopt
|
||||
|
||||
@ -51,20 +51,14 @@ def startup(versionno):
|
||||
threadutil.setprofiledir(profiledir)
|
||||
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
|
||||
|
||||
config = ConfigParser()
|
||||
config = CustomConfigParser()
|
||||
if not os.path.exists(configfilename):
|
||||
sys.stderr.write(" *** Config file %s does not exist; aborting!\n" % configfilename)
|
||||
sys.exit(1)
|
||||
|
||||
config.read(configfilename)
|
||||
|
||||
if config.has_option("general", "pythonfile"):
|
||||
path=os.path.expanduser(config.get("general", "pythonfile"))
|
||||
else:
|
||||
path=None
|
||||
localeval = LocalEval(path)
|
||||
|
||||
ui = offlineimap.ui.detector.findUI(config, localeval, options.get('-u'))
|
||||
ui = offlineimap.ui.detector.findUI(config, options.get('-u'))
|
||||
ui.init_banner()
|
||||
UIBase.setglobalui(ui)
|
||||
|
||||
@ -74,12 +68,9 @@ def startup(versionno):
|
||||
if debugtype == 'imap':
|
||||
imaplib.Debug = 5
|
||||
|
||||
if '-o' in options and config.has_option("general", "autorefresh"):
|
||||
config.remove_option("general", "autorefresh")
|
||||
|
||||
metadatadir = os.path.expanduser(config.get("general", "metadata"))
|
||||
if not os.path.exists(metadatadir):
|
||||
os.mkdir(metadatadir, 0700)
|
||||
if '-o' in options:
|
||||
for section in config.getaccountlist():
|
||||
config.remove_option(section, "autorefresh")
|
||||
|
||||
accounts = config.get("general", "accounts")
|
||||
if '-a' in options:
|
||||
@ -105,17 +96,11 @@ def startup(versionno):
|
||||
threadutil.initInstanceLimit(instancename,
|
||||
config.getint(account, "maxconnections"))
|
||||
|
||||
mailboxes = []
|
||||
servers = {}
|
||||
|
||||
threadutil.initexitnotify()
|
||||
t = ExitNotifyThread(target=syncmaster.sync_with_timer,
|
||||
t = ExitNotifyThread(target=syncmaster.syncitall,
|
||||
name='Sync Runner',
|
||||
kwargs = {'accounts': accounts,
|
||||
'metadatadir': metadatadir,
|
||||
'servers': servers,
|
||||
'config': config,
|
||||
'localeval': localeval})
|
||||
'config': config})
|
||||
t.setDaemon(1)
|
||||
t.start()
|
||||
try:
|
||||
|
@ -19,9 +19,10 @@
|
||||
import os.path
|
||||
import re # for folderfilter
|
||||
|
||||
def genmbnames(config, localeval, boxlist):
|
||||
def genmbnames(config, boxlist):
|
||||
"""Takes a configparser object and a boxlist, which is a list of hashes
|
||||
containing 'accountname' and 'foldername' keys."""
|
||||
localeval = config.getlocaleval()
|
||||
if not config.getboolean("mbnames", "enabled"):
|
||||
return
|
||||
file = open(os.path.expanduser(config.get("mbnames", "filename")), "wt")
|
||||
|
@ -18,187 +18,29 @@
|
||||
|
||||
from offlineimap import imaplib, imapserver, repository, folder, mbnames, threadutil, version
|
||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
||||
import offlineimap.accounts
|
||||
from offlineimap.accounts import SyncableAccount
|
||||
from offlineimap.ui import UIBase
|
||||
import re, os, os.path, offlineimap, sys
|
||||
from ConfigParser import ConfigParser
|
||||
from threading import *
|
||||
|
||||
def syncaccount(accountname, metadatadir, servers, config,
|
||||
localeval, *args):
|
||||
ui = UIBase.getglobalui()
|
||||
# We don't need an account lock because syncitall() goes through
|
||||
# each account once, then waits for all to finish.
|
||||
try:
|
||||
ui.acct(accountname)
|
||||
accountmetadata = os.path.join(metadatadir, accountname)
|
||||
if not os.path.exists(accountmetadata):
|
||||
os.mkdir(accountmetadata, 0700)
|
||||
|
||||
server = None
|
||||
if accountname in servers:
|
||||
server = servers[accountname]
|
||||
else:
|
||||
server = imapserver.ConfigedIMAPServer(config, accountname)
|
||||
servers[accountname] = server
|
||||
|
||||
remoterepos = repository.IMAP.IMAPRepository(config, localeval, accountname, server)
|
||||
|
||||
# Connect to the Maildirs.
|
||||
localrepos = repository.Maildir.MaildirRepository(os.path.expanduser(config.get(accountname, "localfolders")), accountname, config)
|
||||
|
||||
# Connect to the local cache.
|
||||
statusrepos = repository.LocalStatus.LocalStatusRepository(accountmetadata)
|
||||
|
||||
ui.syncfolders(remoterepos, localrepos)
|
||||
remoterepos.syncfoldersto(localrepos)
|
||||
ui.acct(accountname)
|
||||
|
||||
folderthreads = []
|
||||
for remotefolder in remoterepos.getfolders():
|
||||
thread = InstanceLimitedThread(\
|
||||
instancename = 'FOLDER_' + accountname,
|
||||
target = syncfolder,
|
||||
name = "Folder sync %s[%s]" % \
|
||||
(accountname, remotefolder.getvisiblename()),
|
||||
args = (accountname, remoterepos, remotefolder, localrepos,
|
||||
statusrepos))
|
||||
thread.setDaemon(1)
|
||||
thread.start()
|
||||
folderthreads.append(thread)
|
||||
threadutil.threadsreset(folderthreads)
|
||||
if not (config.has_option(accountname, 'holdconnectionopen') and \
|
||||
config.getboolean(accountname, 'holdconnectionopen')):
|
||||
server.close()
|
||||
finally:
|
||||
pass
|
||||
|
||||
def syncfolder(accountname, remoterepos, remotefolder, localrepos,
|
||||
statusrepos):
|
||||
ui = UIBase.getglobalui()
|
||||
# Load local folder.
|
||||
localfolder = localrepos.\
|
||||
getfolder(remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(), localrepos.getsep()))
|
||||
# Write the mailboxes
|
||||
mailboxes.append({'accountname': accountname,
|
||||
'foldername': localfolder.getvisiblename()})
|
||||
# Load local folder
|
||||
ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder)
|
||||
ui.loadmessagelist(localrepos, localfolder)
|
||||
localfolder.cachemessagelist()
|
||||
ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys()))
|
||||
|
||||
|
||||
# Load status folder.
|
||||
statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\
|
||||
replace(remoterepos.getsep(),
|
||||
statusrepos.getsep()))
|
||||
if localfolder.getuidvalidity() == None:
|
||||
# This is a new folder, so delete the status cache to be sure
|
||||
# we don't have a conflict.
|
||||
statusfolder.deletemessagelist()
|
||||
|
||||
statusfolder.cachemessagelist()
|
||||
|
||||
def syncaccount(threads, config, accountname):
|
||||
account = SyncableAccount(config, accountname)
|
||||
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
|
||||
target = account.syncrunner,
|
||||
name = "Account sync %s" % accountname)
|
||||
thread.setDaemon(1)
|
||||
thread.start()
|
||||
threads.add(thread)
|
||||
|
||||
# If either the local or the status folder has messages and
|
||||
# there is a UID validity problem, warn and abort.
|
||||
# If there are no messages, UW IMAPd loses UIDVALIDITY.
|
||||
# But we don't really need it if both local folders are empty.
|
||||
# So, in that case, save it off.
|
||||
if (len(localfolder.getmessagelist()) or \
|
||||
len(statusfolder.getmessagelist())) and \
|
||||
not localfolder.isuidvalidityok(remotefolder):
|
||||
ui.validityproblem(remotefolder)
|
||||
return
|
||||
else:
|
||||
localfolder.saveuidvalidity(remotefolder.getuidvalidity())
|
||||
|
||||
# Load remote folder.
|
||||
ui.loadmessagelist(remoterepos, remotefolder)
|
||||
remotefolder.cachemessagelist()
|
||||
ui.messagelistloaded(remoterepos, remotefolder,
|
||||
len(remotefolder.getmessagelist().keys()))
|
||||
|
||||
|
||||
#
|
||||
|
||||
if not statusfolder.isnewfolder():
|
||||
# Delete local copies of remote messages. This way,
|
||||
# if a message's flag is modified locally but it has been
|
||||
# deleted remotely, we'll delete it locally. Otherwise, we
|
||||
# try to modify a deleted message's flags! This step
|
||||
# need only be taken if a statusfolder is present; otherwise,
|
||||
# there is no action taken *to* the remote repository.
|
||||
|
||||
remotefolder.syncmessagesto_delete(localfolder, [localfolder,
|
||||
statusfolder])
|
||||
ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder)
|
||||
localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder])
|
||||
|
||||
# Synchronize remote changes.
|
||||
ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder)
|
||||
remotefolder.syncmessagesto(localfolder)
|
||||
|
||||
# Make sure the status folder is up-to-date.
|
||||
ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder)
|
||||
localfolder.syncmessagesto(statusfolder)
|
||||
statusfolder.save()
|
||||
|
||||
|
||||
|
||||
def syncitall(accounts, metadatadir, servers, config, localeval):
|
||||
ui = UIBase.getglobalui()
|
||||
global mailboxes
|
||||
mailboxes = [] # Reset.
|
||||
threads = []
|
||||
for accountname in accounts:
|
||||
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
|
||||
target = syncaccount,
|
||||
name = "Account sync %s" % accountname,
|
||||
args = (accountname, metadatadir,
|
||||
servers, config,
|
||||
localeval))
|
||||
thread.setDaemon(1)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
# Wait for the threads to finish.
|
||||
threadutil.threadsreset(threads)
|
||||
mbnames.genmbnames(config, localeval, mailboxes)
|
||||
|
||||
def sync_with_timer(accounts, metadatadir, servers, config,
|
||||
localeval):
|
||||
ui = UIBase.getglobalui()
|
||||
def syncitall(accounts, config):
|
||||
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
||||
syncitall(accounts, metadatadir, servers, config, localeval)
|
||||
if config.has_option('general', 'autorefresh'):
|
||||
refreshperiod = config.getint('general', 'autorefresh') * 60
|
||||
while 1:
|
||||
# Set up keep-alives.
|
||||
kaevents = {}
|
||||
kathreads = {}
|
||||
for accountname in accounts:
|
||||
if config.has_option(accountname, 'holdconnectionopen') and \
|
||||
config.getboolean(accountname, 'holdconnectionopen') and \
|
||||
config.has_option(accountname, 'keepalive'):
|
||||
event = Event()
|
||||
kaevents[accountname] = event
|
||||
thread = ExitNotifyThread(target = servers[accountname].keepalive,
|
||||
name = "Keep alive " + accountname,
|
||||
args = (config.getint(accountname, 'keepalive'), event))
|
||||
thread.setDaemon(1)
|
||||
thread.start()
|
||||
kathreads[accountname] = thread
|
||||
if ui.sleep(refreshperiod) == 2:
|
||||
# Cancel keep-alives, but don't bother terminating threads
|
||||
for event in kaevents.values():
|
||||
event.set()
|
||||
break
|
||||
else:
|
||||
# Cancel keep-alives and wait for threads to terminate.
|
||||
for event in kaevents.values():
|
||||
event.set()
|
||||
for thread in kathreads.values():
|
||||
thread.join()
|
||||
syncitall(accounts, metadatadir, servers, config,
|
||||
localeval)
|
||||
ui = UIBase.getglobalui()
|
||||
threads = threadutil.threadlist()
|
||||
offlineimap.accounts.mailboxes = [] # Reset.
|
||||
for accountname in accounts:
|
||||
syncaccount(threads, config, accountname)
|
||||
# Wait for the threads to finish.
|
||||
threads.reset()
|
||||
mbnames.genmbnames(config, offlineimap.accounts.mailboxes)
|
||||
|
17
offlineimap/head/offlineimap/test.py
Normal file
17
offlineimap/head/offlineimap/test.py
Normal file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/python2.2 -i
|
||||
import hmac
|
||||
def getpassword():
|
||||
return 'tanstaaftanstaaf'
|
||||
|
||||
def md5handler(response):
|
||||
challenge = response.strip()
|
||||
print "challenge is", challenge
|
||||
msg = getpassword()
|
||||
reply = hmac.new(challenge, msg)
|
||||
retval = 'tim' + ' ' + \
|
||||
reply.hexdigest()
|
||||
while len(retval) < 64:
|
||||
retval += "\0"
|
||||
|
||||
print "md5handler returning", retval
|
||||
return retval
|
@ -48,6 +48,42 @@ def threadsreset(threadlist):
|
||||
for thr in threadlist:
|
||||
thr.join()
|
||||
|
||||
class threadlist:
|
||||
def __init__(self):
|
||||
self.lock = Lock()
|
||||
self.list = []
|
||||
|
||||
def add(self, thread):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.list.append(thread)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def remove(self, thread):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.list.remove(thread)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def pop(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if not len(self.list):
|
||||
return None
|
||||
return self.list.pop()
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def reset(self):
|
||||
while 1:
|
||||
thread = self.pop()
|
||||
if not thread:
|
||||
return
|
||||
thread.join()
|
||||
|
||||
|
||||
######################################################################
|
||||
# Exit-notify threads
|
||||
######################################################################
|
||||
|
@ -20,7 +20,7 @@ import sys, time
|
||||
from UIBase import UIBase
|
||||
|
||||
class Basic(UIBase):
|
||||
def getpass(s, accountname, config):
|
||||
def getpass(s, accountname, config, errmsg = None):
|
||||
raise NotImplementedError, "Prompting for a password is not supported in noninteractive mode."
|
||||
|
||||
def _msg(s, msg):
|
||||
|
@ -140,6 +140,10 @@ class UIBase:
|
||||
if s.verbose >= 0:
|
||||
s._msg("***** Processing account %s" % accountname)
|
||||
|
||||
def acctdone(s, accountname):
|
||||
if s.verbose >= 0:
|
||||
s._msg("***** Finished processing account " + accountname)
|
||||
|
||||
def syncfolders(s, srcrepos, destrepos):
|
||||
if s.verbose >= 0:
|
||||
s._msg("Copying folder structure from %s to %s" % \
|
||||
|
@ -19,7 +19,7 @@
|
||||
import offlineimap.ui
|
||||
import sys
|
||||
|
||||
def findUI(config, localeval, chosenUI=None):
|
||||
def findUI(config, chosenUI=None):
|
||||
uistrlist = ['Tk.Blinkenlights', 'Tk.VerboseUI', 'TTY.TTYUI',
|
||||
'Noninteractive.Basic', 'Noninteractive.Quiet']
|
||||
namespace={}
|
||||
@ -34,7 +34,7 @@ def findUI(config, localeval, chosenUI=None):
|
||||
uistrlist = config.get("general", "ui").replace(" ", "").split(",")
|
||||
|
||||
for uistr in uistrlist:
|
||||
uimod = getUImod(uistr, localeval, namespace)
|
||||
uimod = getUImod(uistr, config.getlocaleval(), namespace)
|
||||
if uimod:
|
||||
uiinstance = uimod(config)
|
||||
if uiinstance.isusable():
|
||||
|
Reference in New Issue
Block a user