Merge branch 'next'

Conflicts:
	Changelog.rst
This commit is contained in:
Sebastian Spaeth 2012-01-06 13:24:50 +01:00
commit b569ef551f
7 changed files with 47 additions and 22 deletions

View File

@ -11,7 +11,23 @@ ChangeLog
on releases. And because I'm lazy, it will also be used as a draft for the on releases. And because I'm lazy, it will also be used as a draft for the
releases announces. releases announces.
======= OfflineIMAP v6.4.4 (2012-01-06)
===============================
This is a bugfix release, fixing regressions occurring in or since 6.4.0.
* Fix the missing folder error that occured when a new remote folder was
detected (IMAP<->Maildir)
* Possibly fixed bug that prevented us from ever re-reading Maildir
folders, so flag changes and deletions were not detected when running
in a refresh loop. This is a regression that was introduced in about
6.4.0.
* Never mangle maildir file names when using nonstandard Maildir flags
(such as 'a'), note that they will still be deleted as they are not
supported in the sync to an IMAP server.
OfflineIMAP v6.4.3 (2012-01-04) OfflineIMAP v6.4.3 (2012-01-04)
=============================== ===============================

View File

@ -1,7 +1,7 @@
__all__ = ['OfflineImap'] __all__ = ['OfflineImap']
__productname__ = 'OfflineIMAP' __productname__ = 'OfflineIMAP'
__version__ = "6.4.3" __version__ = "6.4.4"
__copyright__ = "Copyright 2002-2012 John Goerzen & contributors" __copyright__ = "Copyright 2002-2012 John Goerzen & contributors"
__author__ = "John Goerzen" __author__ = "John Goerzen"
__author_email__= "john@complete.org" __author_email__= "john@complete.org"

View File

@ -289,10 +289,10 @@ class SyncableAccount(Account):
localrepos.getfolders() localrepos.getfolders()
statusrepos.getfolders() statusrepos.getfolders()
remoterepos.sync_folder_structure(localrepos, statusrepos)
# replicate the folderstructure between REMOTE to LOCAL # replicate the folderstructure between REMOTE to LOCAL
if not localrepos.getconfboolean('readonly', False): if not localrepos.getconfboolean('readonly', False):
self.ui.syncfolders(remoterepos, localrepos) self.ui.syncfolders(remoterepos, localrepos)
remoterepos.syncfoldersto(localrepos, statusrepos)
# iterate through all folders on the remote repo and sync # iterate through all folders on the remote repo and sync
for remotefolder in remoterepos.getfolders(): for remotefolder in remoterepos.getfolders():

View File

@ -64,13 +64,13 @@ class MaildirFolder(BaseFolder):
self.root = root self.root = root
self.sep = sep self.sep = sep
self.messagelist = None self.messagelist = None
# check if we should use a different infosep to support Win file systems
self.wincompatible = self.config.getdefaultboolean( self.wincompatible = self.config.getdefaultboolean(
"Account "+self.accountname, "maildir-windows-compatible", False) "Account "+self.accountname, "maildir-windows-compatible", False)
self.infosep = '!' if self.wincompatible else ':' self.infosep = '!' if self.wincompatible else ':'
"""infosep is the separator between maildir name and flag appendix""" """infosep is the separator between maildir name and flag appendix"""
self.flagmatchre = re.compile(self.infosep + '.*2,([A-Z]+)') self.flagmatchre = re.compile('(%s2,)(\w*)' % self.infosep)
#self.ui is set in BaseFolder.init() #self.ui is set in BaseFolder.init()
# Cache the full folder path, as we use getfullname() very often # Cache the full folder path, as we use getfullname() very often
self._fullname = os.path.join(self.getroot(), self.getname()) self._fullname = os.path.join(self.getroot(), self.getname())
@ -162,7 +162,7 @@ class MaildirFolder(BaseFolder):
#identify flags in the path name #identify flags in the path name
flagmatch = self.flagmatchre.search(messagename) flagmatch = self.flagmatchre.search(messagename)
if flagmatch: if flagmatch:
flags = set(flagmatch.group(1)) flags = set(flagmatch.group(2))
else: else:
flags = set() flags = set()
# 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S # 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S
@ -185,7 +185,7 @@ class MaildirFolder(BaseFolder):
def cachemessagelist(self): def cachemessagelist(self):
if self.messagelist is None: if self.messagelist is None:
self.messagelist = self._scanfolder() self.messagelist = self._scanfolder()
def getmessagelist(self): def getmessagelist(self):
return self.messagelist return self.messagelist
@ -259,7 +259,7 @@ class MaildirFolder(BaseFolder):
self.savemessageflags(uid, flags) self.savemessageflags(uid, flags)
self.ui.debug('maildir', 'savemessage: returning uid %d' % uid) self.ui.debug('maildir', 'savemessage: returning uid %d' % uid)
return uid return uid
def getmessageflags(self, uid): def getmessageflags(self, uid):
return self.messagelist[uid]['flags'] return self.messagelist[uid]['flags']
@ -274,15 +274,14 @@ class MaildirFolder(BaseFolder):
else: else:
dir_prefix = 'new' dir_prefix = 'new'
infostr = self.infosep # Strip off existing infostring (preserving small letter flags, that
infomatch = re.search('(' + self.infosep + '.*)$', newname) # dovecot uses)
if infomatch: # If the info string is present.. infomatch = self.flagmatchre.search(newname)
infostr = infomatch.group(1) if infomatch:
newname = newname.split(self.infosep)[0] # Strip off the info string. newname = newname[:-len(infomatch.group())] #strip off
infostr = re.sub('2,[A-Z]*', '', infostr) infostr = '%s2,%s' % (self.infosep, ''.join(sorted(flags)))
infostr += '2,' + ''.join(sorted(flags))
newname += infostr newname += infostr
newfilename = os.path.join(dir_prefix, newname) newfilename = os.path.join(dir_prefix, newname)
if (newfilename != oldfilename): if (newfilename != oldfilename):
try: try:
@ -292,7 +291,7 @@ class MaildirFolder(BaseFolder):
raise OfflineImapError("Can't rename file '%s' to '%s': %s" % ( raise OfflineImapError("Can't rename file '%s' to '%s': %s" % (
oldfilename, newfilename, e[1]), oldfilename, newfilename, e[1]),
OfflineImapError.ERROR.FOLDER) OfflineImapError.ERROR.FOLDER)
self.messagelist[uid]['flags'] = flags self.messagelist[uid]['flags'] = flags
self.messagelist[uid]['filename'] = newfilename self.messagelist[uid]['filename'] = newfilename

View File

@ -132,8 +132,8 @@ class BaseRepository(object, CustomConfig.ConfigHelperMixin):
def getfolder(self, foldername): def getfolder(self, foldername):
raise NotImplementedError raise NotImplementedError
def syncfoldersto(self, dst_repo, status_repo): def sync_folder_structure(self, dst_repo, status_repo):
"""Syncs the folders in this repository to those in dest. """Syncs the folders in this repository to those in dest.
It does NOT sync the contents of those folders. nametrans rules It does NOT sync the contents of those folders. nametrans rules
@ -158,6 +158,9 @@ class BaseRepository(object, CustomConfig.ConfigHelperMixin):
# Find new folders on src_repo. # Find new folders on src_repo.
for src_name, src_folder in src_hash.iteritems(): for src_name, src_folder in src_hash.iteritems():
# Don't create on dst_repo, if it is readonly
if dst_repo.getconfboolean('readonly', False):
break
if src_folder.sync_this and not src_name in dst_hash: if src_folder.sync_this and not src_name in dst_hash:
try: try:
dst_repo.makefolder(src_name) dst_repo.makefolder(src_name)
@ -171,6 +174,10 @@ class BaseRepository(object, CustomConfig.ConfigHelperMixin):
status_repo.getsep())) status_repo.getsep()))
# Find new folders on dst_repo. # Find new folders on dst_repo.
for dst_name, dst_folder in dst_hash.iteritems(): for dst_name, dst_folder in dst_hash.iteritems():
if self.getconfboolean('readonly', False):
# Don't create missing folder on readonly repo.
break
if dst_folder.sync_this and not dst_name in src_hash: if dst_folder.sync_this and not dst_name in src_hash:
# nametrans sanity check! # nametrans sanity check!
# Does nametrans back&forth lead to identical names? # Does nametrans back&forth lead to identical names?
@ -202,7 +209,6 @@ class BaseRepository(object, CustomConfig.ConfigHelperMixin):
src_repo, newdst_name), src_repo, newdst_name),
OfflineImapError.ERROR.REPO) OfflineImapError.ERROR.REPO)
# end sanity check, actually create the folder # end sanity check, actually create the folder
try: try:
src_repo.makefolder(newsrc_name) src_repo.makefolder(newsrc_name)
src_haschanged = True # Need to refresh list src_haschanged = True # Need to refresh list

View File

@ -190,4 +190,8 @@ class MaildirRepository(BaseRepository):
if self.folders == None: if self.folders == None:
self.folders = self._getfolders_scandir(self.root) self.folders = self._getfolders_scandir(self.root)
return self.folders return self.folders
def forgetfolders(self):
"""Forgets the cached list of folders, if any. Useful to run
after a sync run."""
self.folders = None

View File

@ -285,7 +285,7 @@ class UIBase(object):
"""Output that we finished syncing an account (in which time)""" """Output that we finished syncing an account (in which time)"""
sec = time.time() - self.acct_startimes[account] sec = time.time() - self.acct_startimes[account]
del self.acct_startimes[account] del self.acct_startimes[account]
self._msg("*** Finished account '%s' in %d:%02d" % self.logger.info("*** Finished account '%s' in %d:%02d" %
(account, sec // 60, sec % 60)) (account, sec // 60, sec % 60))
def syncfolders(self, src_repo, dst_repo): def syncfolders(self, src_repo, dst_repo):