/offlineimap/head: changeset 259
Initial commit on initialization reorganization
This commit is contained in:
parent
ca56d8c899
commit
73199ad735
@ -17,311 +17,6 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
from offlineimap import imaplib, imapserver, repository, folder, mbnames, threadutil, version, localeval
|
revno = long('$Rev: 252 $'[6:-2])
|
||||||
from offlineimap.threadutil import InstanceLimitedThread, ExitNotifyThread
|
from offlineimap import init
|
||||||
from offlineimap.ui import UIBase
|
init.startup(revno)
|
||||||
import re, os, os.path, offlineimap, sys
|
|
||||||
from ConfigParser import ConfigParser
|
|
||||||
from threading import *
|
|
||||||
from getopt import getopt
|
|
||||||
|
|
||||||
options = {}
|
|
||||||
if '--help' in sys.argv[1:]:
|
|
||||||
sys.stdout.write(version.cmdhelp + "\n")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
for optlist in getopt(sys.argv[1:], 'P:1oa:c:d:u:h')[0]:
|
|
||||||
options[optlist[0]] = optlist[1]
|
|
||||||
|
|
||||||
if '-h' in options:
|
|
||||||
sys.stdout.write(version.cmdhelp)
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
sys.exit(0)
|
|
||||||
configfilename = os.path.expanduser("~/.offlineimaprc")
|
|
||||||
if '-c' in options:
|
|
||||||
configfilename = options['-c']
|
|
||||||
if '-P' in options:
|
|
||||||
if not '-1' in options:
|
|
||||||
sys.stderr.write("FATAL: profile mode REQUIRES -1\n")
|
|
||||||
sys.exit(100)
|
|
||||||
profiledir = options['-P']
|
|
||||||
os.mkdir(profiledir)
|
|
||||||
threadutil.setprofiledir(profiledir)
|
|
||||||
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config = ConfigParser()
|
|
||||||
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.LocalEval(path)
|
|
||||||
|
|
||||||
ui = offlineimap.ui.detector.findUI(config, localeval, options.get('-u'))
|
|
||||||
ui.init_banner()
|
|
||||||
|
|
||||||
|
|
||||||
if '-d' in options:
|
|
||||||
for debugtype in options['-d'].split(','):
|
|
||||||
ui.add_debug(debugtype.strip())
|
|
||||||
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)
|
|
||||||
|
|
||||||
accounts = config.get("general", "accounts")
|
|
||||||
if '-a' in options:
|
|
||||||
accounts = options['-a']
|
|
||||||
accounts = accounts.replace(" ", "")
|
|
||||||
accounts = accounts.split(",")
|
|
||||||
|
|
||||||
server = None
|
|
||||||
remoterepos = None
|
|
||||||
localrepos = None
|
|
||||||
passwords = {}
|
|
||||||
tunnels = {}
|
|
||||||
|
|
||||||
if '-1' in options:
|
|
||||||
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
|
|
||||||
else:
|
|
||||||
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)
|
|
||||||
else:
|
|
||||||
threadutil.initInstanceLimit(instancename,
|
|
||||||
config.getint(account, "maxconnections"))
|
|
||||||
|
|
||||||
mailboxes = []
|
|
||||||
servers = {}
|
|
||||||
|
|
||||||
def syncaccount(accountname, *args):
|
|
||||||
# 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, passwords)
|
|
||||||
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):
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
|
|
||||||
def syncitall():
|
|
||||||
global mailboxes
|
|
||||||
mailboxes = [] # Reset.
|
|
||||||
threads = []
|
|
||||||
for accountname in accounts:
|
|
||||||
thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
|
|
||||||
target = syncaccount,
|
|
||||||
name = "Account sync %s" % accountname,
|
|
||||||
args = (accountname,))
|
|
||||||
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():
|
|
||||||
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
|
||||||
syncitall()
|
|
||||||
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()
|
|
||||||
|
|
||||||
def threadexited(thread):
|
|
||||||
if thread.getExitCause() == 'EXCEPTION':
|
|
||||||
if isinstance(thread.getExitException(), SystemExit):
|
|
||||||
# Bring a SystemExit into the main thread.
|
|
||||||
# Do not send it back to UI layer right now.
|
|
||||||
# Maybe later send it to ui.terminate?
|
|
||||||
raise SystemExit
|
|
||||||
ui.threadException(thread) # Expected to terminate
|
|
||||||
sys.exit(100) # Just in case...
|
|
||||||
os._exit(100)
|
|
||||||
elif thread.getExitMessage() == 'SYNC_WITH_TIMER_TERMINATE':
|
|
||||||
ui.terminate()
|
|
||||||
# Just in case...
|
|
||||||
sys.exit(100)
|
|
||||||
os._exit(100)
|
|
||||||
else:
|
|
||||||
ui.threadExited(thread)
|
|
||||||
|
|
||||||
threadutil.initexitnotify()
|
|
||||||
t = ExitNotifyThread(target=sync_with_timer,
|
|
||||||
name='Sync Runner')
|
|
||||||
t.setDaemon(1)
|
|
||||||
t.start()
|
|
||||||
try:
|
|
||||||
threadutil.exitnotifymonitorloop(threadexited)
|
|
||||||
except SystemExit:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
ui.mainException() # Also expected to terminate.
|
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
offlineimap (3.2.9) unstable; urgency=low
|
offlineimap (3.99.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* The next few releases are adding features and reorganizing
|
||||||
|
code in preparation for 4.0.0.
|
||||||
* imaputil.py now logs information with IMAP debugging is enabled.
|
* imaputil.py now logs information with IMAP debugging is enabled.
|
||||||
* Added folderfilter capability to mbnames recorder. You can now omit
|
* Added folderfilter capability to mbnames recorder. You can now omit
|
||||||
specified folders from the mbnames output.
|
specified folders from the mbnames output.
|
||||||
* Added a workaround to imaputil.py to deal with a bug in imaplib.py's
|
* Added a workaround to imaputil.py to deal with a bug in imaplib.py's
|
||||||
tuple when a response contains a literal in certain cases.
|
tuple when a response contains a literal in certain cases.
|
||||||
|
* Split out the code in bin/offlineimap into offlineimap/init.py.
|
||||||
|
Retaining bin/offlineimap as a skeletal piece only. Contains
|
||||||
|
about three lines of code now. This will make many things
|
||||||
|
easier, including debugging.
|
||||||
|
* Added library version check to bin/offlineimap and
|
||||||
|
offlineimap/init.py.
|
||||||
|
* Moved __main__.ui to functions in UIBase: getglobalui() and
|
||||||
|
setglobalui().
|
||||||
|
|
||||||
-- John Goerzen <jgoerzen@complete.org> Mon, 30 Sep 2002 12:08:08 -0500
|
-- John Goerzen <jgoerzen@complete.org> Mon, 30 Sep 2002 12:08:08 -0500
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
import __main__
|
|
||||||
from threading import *
|
from threading import *
|
||||||
from offlineimap import threadutil
|
from offlineimap import threadutil
|
||||||
from offlineimap.threadutil import InstanceLimitedThread
|
from offlineimap.threadutil import InstanceLimitedThread
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
|
|
||||||
class BaseFolder:
|
class BaseFolder:
|
||||||
def getname(self):
|
def getname(self):
|
||||||
@ -161,7 +161,7 @@ class BaseFolder:
|
|||||||
for uid in self.getmessagelist().keys():
|
for uid in self.getmessagelist().keys():
|
||||||
if uid >= 0:
|
if uid >= 0:
|
||||||
continue
|
continue
|
||||||
__main__.ui.copyingmessage(uid, self, applyto)
|
UIBase.getglobalui().copyingmessage(uid, self, applyto)
|
||||||
successobject = None
|
successobject = None
|
||||||
successuid = None
|
successuid = None
|
||||||
message = self.getmessage(uid)
|
message = self.getmessage(uid)
|
||||||
@ -192,7 +192,7 @@ class BaseFolder:
|
|||||||
# synced to the status cache. This is only a problem with
|
# synced to the status cache. This is only a problem with
|
||||||
# self.getmessage(). So, don't call self.getmessage unless
|
# self.getmessage(). So, don't call self.getmessage unless
|
||||||
# really needed.
|
# really needed.
|
||||||
__main__.ui.copyingmessage(uid, self, applyto)
|
UIBase.getglobalui().copyingmessage(uid, self, applyto)
|
||||||
message = ''
|
message = ''
|
||||||
# If any of the destinations actually stores the message body,
|
# If any of the destinations actually stores the message body,
|
||||||
# load it up.
|
# load it up.
|
||||||
@ -249,7 +249,7 @@ class BaseFolder:
|
|||||||
if not uid in self.getmessagelist():
|
if not uid in self.getmessagelist():
|
||||||
deletelist.append(uid)
|
deletelist.append(uid)
|
||||||
if len(deletelist):
|
if len(deletelist):
|
||||||
__main__.ui.deletingmessages(deletelist, applyto)
|
UIBase.getglobalui().deletingmessages(deletelist, applyto)
|
||||||
for object in applyto:
|
for object in applyto:
|
||||||
object.deletemessages(deletelist)
|
object.deletemessages(deletelist)
|
||||||
|
|
||||||
@ -266,13 +266,13 @@ class BaseFolder:
|
|||||||
|
|
||||||
addflags = [x for x in selfflags if x not in destflags]
|
addflags = [x for x in selfflags if x not in destflags]
|
||||||
if len(addflags):
|
if len(addflags):
|
||||||
__main__.ui.addingflags(uid, addflags, applyto)
|
UIBase.getglobalui().addingflags(uid, addflags, applyto)
|
||||||
for object in applyto:
|
for object in applyto:
|
||||||
object.addmessageflags(uid, addflags)
|
object.addmessageflags(uid, addflags)
|
||||||
|
|
||||||
delflags = [x for x in destflags if x not in selfflags]
|
delflags = [x for x in destflags if x not in selfflags]
|
||||||
if len(delflags):
|
if len(delflags):
|
||||||
__main__.ui.deletingflags(uid, delflags, applyto)
|
UIBase.getglobalui().deletingflags(uid, delflags, applyto)
|
||||||
for object in applyto:
|
for object in applyto:
|
||||||
object.deletemessageflags(uid, delflags)
|
object.deletemessageflags(uid, delflags)
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
from Base import BaseFolder
|
from Base import BaseFolder
|
||||||
from offlineimap import imaputil, imaplib
|
from offlineimap import imaputil, imaplib
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
import rfc822, time, string
|
import rfc822, time, string
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
import __main__
|
|
||||||
|
|
||||||
class IMAPFolder(BaseFolder):
|
class IMAPFolder(BaseFolder):
|
||||||
def __init__(self, imapserver, name, visiblename, accountname, repository):
|
def __init__(self, imapserver, name, visiblename, accountname, repository):
|
||||||
@ -102,7 +102,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
try:
|
try:
|
||||||
imapobj.select(self.getfullname()) # Needed for search
|
imapobj.select(self.getfullname()) # Needed for search
|
||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
__main__.ui.msgtoreadonly(self, uid, content, flags)
|
UIBase.getglobalui().msgtoreadonly(self, uid, content, flags)
|
||||||
# Return indicating message taken, but no UID assigned.
|
# Return indicating message taken, but no UID assigned.
|
||||||
# Fudge it.
|
# Fudge it.
|
||||||
return 0
|
return 0
|
||||||
@ -168,7 +168,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
try:
|
try:
|
||||||
imapobj.select(self.getfullname())
|
imapobj.select(self.getfullname())
|
||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
__main__.ui.flagstoreadonly(self, [uid], flags)
|
UIBase.getglobalui().flagstoreadonly(self, [uid], flags)
|
||||||
return
|
return
|
||||||
result = imapobj.uid('store', '%d' % uid, 'FLAGS',
|
result = imapobj.uid('store', '%d' % uid, 'FLAGS',
|
||||||
imaputil.flagsmaildir2imap(flags))
|
imaputil.flagsmaildir2imap(flags))
|
||||||
@ -191,7 +191,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
try:
|
try:
|
||||||
imapobj.select(self.getfullname())
|
imapobj.select(self.getfullname())
|
||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
__main__.ui.flagstoreadonly(self, uidlist, flags)
|
UIBase.getglobalui().flagstoreadonly(self, uidlist, flags)
|
||||||
return
|
return
|
||||||
r = imapobj.uid('store',
|
r = imapobj.uid('store',
|
||||||
imaputil.listjoin(uidlist),
|
imaputil.listjoin(uidlist),
|
||||||
@ -242,7 +242,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
try:
|
try:
|
||||||
imapobj.select(self.getfullname())
|
imapobj.select(self.getfullname())
|
||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
__main__.ui.deletereadonly(self, uidlist)
|
UIBase.getglobalui().deletereadonly(self, uidlist)
|
||||||
return
|
return
|
||||||
assert(imapobj.expunge()[0] == 'OK')
|
assert(imapobj.expunge()[0] == 'OK')
|
||||||
finally:
|
finally:
|
||||||
|
@ -22,7 +22,7 @@ Public functions: Internaldate2tuple
|
|||||||
__version__ = "2.52"
|
__version__ = "2.52"
|
||||||
|
|
||||||
import binascii, re, socket, time, random, sys, os
|
import binascii, re, socket, time, random, sys, os
|
||||||
import __main__
|
from offlineimap.ui import UIBase
|
||||||
|
|
||||||
__all__ = ["IMAP4", "Internaldate2tuple",
|
__all__ = ["IMAP4", "Internaldate2tuple",
|
||||||
"Int2AP", "ParseFlags", "Time2Internaldate"]
|
"Int2AP", "ParseFlags", "Time2Internaldate"]
|
||||||
@ -988,7 +988,7 @@ class IMAP4:
|
|||||||
if secs is None:
|
if secs is None:
|
||||||
secs = time.time()
|
secs = time.time()
|
||||||
tm = time.strftime('%M:%S', time.localtime(secs))
|
tm = time.strftime('%M:%S', time.localtime(secs))
|
||||||
__main__.ui.debug('imap', ' %s.%02d %s' % (tm, (secs*100)%100, s))
|
UIBase.getglobalui().debug('imap', ' %s.%02d %s' % (tm, (secs*100)%100, s))
|
||||||
|
|
||||||
def _dump_ur(self, dict):
|
def _dump_ur(self, dict):
|
||||||
# Dump untagged responses (in `dict').
|
# Dump untagged responses (in `dict').
|
||||||
|
@ -17,9 +17,10 @@
|
|||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
from offlineimap import imaplib, imaputil, threadutil
|
from offlineimap import imaplib, imaputil, threadutil
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
from threading import *
|
from threading import *
|
||||||
import thread
|
import thread
|
||||||
import __main__
|
|
||||||
|
|
||||||
class UsefulIMAPMixIn:
|
class UsefulIMAPMixIn:
|
||||||
def getstate(self):
|
def getstate(self):
|
||||||
@ -121,7 +122,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
self.connectionlock.release() # Release until need to modify data
|
self.connectionlock.release() # Release until need to modify data
|
||||||
|
|
||||||
__main__.ui.connecting(self.hostname, self.port)
|
UIBase.getglobalui().connecting(self.hostname, self.port)
|
||||||
|
|
||||||
# Generate a new connection.
|
# Generate a new connection.
|
||||||
if self.tunnel:
|
if self.tunnel:
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
import re, string, types
|
import re, string, types
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
quotere = re.compile('^("(?:[^"]|\\\\")*")')
|
quotere = re.compile('^("(?:[^"]|\\\\")*")')
|
||||||
import __main__
|
|
||||||
|
|
||||||
def debug(*args):
|
def debug(*args):
|
||||||
msg = []
|
msg = []
|
||||||
for arg in args:
|
for arg in args:
|
||||||
msg.append(str(arg))
|
msg.append(str(arg))
|
||||||
__main__.ui.debug('imap', " ".join(msg))
|
UIBase.getglobalui().debug('imap', " ".join(msg))
|
||||||
|
|
||||||
def dequote(string):
|
def dequote(string):
|
||||||
"""Takes a string which may or may not be quoted and returns it, unquoted.
|
"""Takes a string which may or may not be quoted and returns it, unquoted.
|
||||||
|
335
offlineimap/head/offlineimap/init.py
Normal file
335
offlineimap/head/offlineimap/init.py
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# OfflineIMAP initialization code
|
||||||
|
# Copyright (C) 2002 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 imaplib, imapserver, repository, folder, mbnames, threadutil, version
|
||||||
|
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 threading import *
|
||||||
|
from getopt import getopt
|
||||||
|
|
||||||
|
def startup(revno):
|
||||||
|
assert revno == version.revno, "Revision of main program (%d) does not match that of library (%d). Please double-check your PYTHONPATH and installation locations." % (revno, version.revno)
|
||||||
|
options = {}
|
||||||
|
if '--help' in sys.argv[1:]:
|
||||||
|
sys.stdout.write(version.cmdhelp + "\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
for optlist in getopt(sys.argv[1:], 'P:1oa:c:d:u:h')[0]:
|
||||||
|
options[optlist[0]] = optlist[1]
|
||||||
|
|
||||||
|
if '-h' in options:
|
||||||
|
sys.stdout.write(version.cmdhelp)
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
sys.exit(0)
|
||||||
|
configfilename = os.path.expanduser("~/.offlineimaprc")
|
||||||
|
if '-c' in options:
|
||||||
|
configfilename = options['-c']
|
||||||
|
if '-P' in options:
|
||||||
|
if not '-1' in options:
|
||||||
|
sys.stderr.write("FATAL: profile mode REQUIRES -1\n")
|
||||||
|
sys.exit(100)
|
||||||
|
profiledir = options['-P']
|
||||||
|
os.mkdir(profiledir)
|
||||||
|
threadutil.setprofiledir(profiledir)
|
||||||
|
sys.stderr.write("WARNING: profile mode engaged;\nPotentially large data will be created in " + profiledir + "\n")
|
||||||
|
|
||||||
|
config = ConfigParser()
|
||||||
|
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.init_banner()
|
||||||
|
UIBase.setglobalui(ui)
|
||||||
|
print "UI is", UIBase.getglobalui()
|
||||||
|
|
||||||
|
if '-d' in options:
|
||||||
|
for debugtype in options['-d'].split(','):
|
||||||
|
ui.add_debug(debugtype.strip())
|
||||||
|
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)
|
||||||
|
|
||||||
|
accounts = config.get("general", "accounts")
|
||||||
|
if '-a' in options:
|
||||||
|
accounts = options['-a']
|
||||||
|
accounts = accounts.replace(" ", "")
|
||||||
|
accounts = accounts.split(",")
|
||||||
|
|
||||||
|
server = None
|
||||||
|
remoterepos = None
|
||||||
|
localrepos = None
|
||||||
|
passwords = {}
|
||||||
|
tunnels = {}
|
||||||
|
|
||||||
|
if '-1' in options:
|
||||||
|
threadutil.initInstanceLimit("ACCOUNTLIMIT", 1)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
print "Processing account", account
|
||||||
|
#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)
|
||||||
|
else:
|
||||||
|
threadutil.initInstanceLimit(instancename,
|
||||||
|
config.getint(account, "maxconnections"))
|
||||||
|
|
||||||
|
mailboxes = []
|
||||||
|
servers = {}
|
||||||
|
|
||||||
|
threadutil.initexitnotify()
|
||||||
|
t = ExitNotifyThread(target=sync_with_timer,
|
||||||
|
name='Sync Runner')
|
||||||
|
t.setDaemon(1)
|
||||||
|
t.start()
|
||||||
|
try:
|
||||||
|
threadutil.exitnotifymonitorloop(threadexited)
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
ui.mainException() # Also expected to terminate.
|
||||||
|
|
||||||
|
def syncaccount(accountname, *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, passwords)
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
# 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():
|
||||||
|
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,))
|
||||||
|
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():
|
||||||
|
ui = UIBase.getglobalui()
|
||||||
|
currentThread().setExitMessage('SYNC_WITH_TIMER_TERMINATE')
|
||||||
|
syncitall()
|
||||||
|
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()
|
||||||
|
|
||||||
|
def threadexited(thread):
|
||||||
|
ui = UIBase.getglobalui()
|
||||||
|
if thread.getExitCause() == 'EXCEPTION':
|
||||||
|
if isinstance(thread.getExitException(), SystemExit):
|
||||||
|
# Bring a SystemExit into the main thread.
|
||||||
|
# Do not send it back to UI layer right now.
|
||||||
|
# Maybe later send it to ui.terminate?
|
||||||
|
raise SystemExit
|
||||||
|
ui.threadException(thread) # Expected to terminate
|
||||||
|
sys.exit(100) # Just in case...
|
||||||
|
os._exit(100)
|
||||||
|
elif thread.getExitMessage() == 'SYNC_WITH_TIMER_TERMINATE':
|
||||||
|
ui.terminate()
|
||||||
|
# Just in case...
|
||||||
|
sys.exit(100)
|
||||||
|
os._exit(100)
|
||||||
|
else:
|
||||||
|
ui.threadExited(thread)
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
from Base import BaseRepository
|
from Base import BaseRepository
|
||||||
from offlineimap import folder, imaputil
|
from offlineimap import folder, imaputil
|
||||||
|
from offlineimap.ui import UIBase
|
||||||
from mailbox import Maildir
|
from mailbox import Maildir
|
||||||
import os
|
import os
|
||||||
import __main__
|
|
||||||
|
|
||||||
class MaildirRepository(BaseRepository):
|
class MaildirRepository(BaseRepository):
|
||||||
def __init__(self, root, accountname, config):
|
def __init__(self, root, accountname, config):
|
||||||
@ -31,7 +31,7 @@ class MaildirRepository(BaseRepository):
|
|||||||
self.folders = None
|
self.folders = None
|
||||||
self.accountname = accountname
|
self.accountname = accountname
|
||||||
self.config = config
|
self.config = config
|
||||||
self.ui = __main__.ui
|
self.ui = UIBase.getglobalui()
|
||||||
self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
|
self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
|
||||||
|
|
||||||
def debug(self, msg):
|
def debug(self, msg):
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
from offlineimap import repository
|
|
||||||
import offlineimap.version
|
import offlineimap.version
|
||||||
import re, time, sys, traceback
|
import re, time, sys, traceback
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
@ -24,6 +23,14 @@ from StringIO import StringIO
|
|||||||
debugtypes = {'imap': 'IMAP protocol debugging',
|
debugtypes = {'imap': 'IMAP protocol debugging',
|
||||||
'maildir': 'Maildir repository debugging'}
|
'maildir': 'Maildir repository debugging'}
|
||||||
|
|
||||||
|
globalui = None
|
||||||
|
def setglobalui(newui):
|
||||||
|
global globalui
|
||||||
|
globalui = newui
|
||||||
|
def getglobalui():
|
||||||
|
global globalui
|
||||||
|
return globalui
|
||||||
|
|
||||||
class UIBase:
|
class UIBase:
|
||||||
def __init__(s, config, verbose = 0):
|
def __init__(s, config, verbose = 0):
|
||||||
s.verbose = verbose
|
s.verbose = verbose
|
||||||
|
Loading…
Reference in New Issue
Block a user