Merge branch 'next'
Conflicts: Changelog.rst
This commit is contained in:
commit
b569ef551f
@ -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)
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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():
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user