Merge branch 'next' of github.com:OfflineIMAP/offlineimap into next

This commit is contained in:
Eygene Ryabinkin 2015-01-31 10:08:07 +03:00
commit fd79baade5
6 changed files with 94 additions and 45 deletions

View File

@ -5,13 +5,67 @@ ChangeLog
:website: http://offlineimap.org :website: http://offlineimap.org
OfflineIMAP v6.5.7-rc3 (2015- - )
===================================
OfflineIMAP v6.5.7-rc2 (2015-01-18)
===================================
Notes
-----
This release candidate should be minor for most users.
The best points are about SSL not falling back on other authentication methods
when failing, better RAM footprint and reduced I/O access.
Documentation had our attention, too.
There's some code cleanups and code refactoring, as usual.
Features
--------
* Do not keep reloading pyhtonfile, make it stateful.
* HACKING: how to create tags.
* MANUAL: add minor sample on how to retrieve a password with a helper python file.
Fixes
-----
* Make OS-default CA certificate file to be requested explicitely.
* SSL: do not fallback on other authentication mode if it fails.
* Fix regression introduced while style patching.
* API documentation: properly auto-document main class, fixes.
* ui: Machine: remove offending param for a _printData() call.
* Drop caches after having processed folders.
Changes
-------
* Fix unexpected garbage code.
* Properly re-raise exception to save original tracebacks.
* Refactoring: avoid redefining various Python keywords.
* Code: improvements of comments and more style consistency.
* Configuration file: better design and other small improvements.
* nametrans documentation: fix minor error.
* Unused import removal.
* Add a note about the incorrect rendering of the docstring with Sphinx.
* Errors handling: log the messages with level ERROR.
* MAINTAINERS: add mailing list maintainers.
* Fixed copyright statement.
* COPYING: fix unexpected characters.
OfflineIMAP v6.5.7-rc1 (2015-01-07) OfflineIMAP v6.5.7-rc1 (2015-01-07)
=================================== ===================================
Notes Notes
----- -----
I think it's time for a new release candidate. Our release cycle are long I think it's time for a new release candidate. Our release cycles are long
enough and users are asked to use the current TIP of the next branch to test enough and users are asked to use the current TIP of the next branch to test
our recent patches. our recent patches.
@ -114,7 +168,7 @@ OfflineIMAP v6.5.6 (2014-05-14)
to Tomasz Żok) to Tomasz Żok)
OfflineIMAP v6.5.6-RC1 (2014-05-14) OfflineIMAP v6.5.6-rc1 (2014-05-14)
=================================== ===================================
* Add knob to invoke folderfilter dynamically on each sync (GitHub#73) * Add knob to invoke folderfilter dynamically on each sync (GitHub#73)

View File

@ -2,7 +2,7 @@ __all__ = ['OfflineImap']
__productname__ = 'OfflineIMAP' __productname__ = 'OfflineIMAP'
__version__ = "6.5.7" __version__ = "6.5.7"
__revision__ = "-rc1" __revision__ = "-rc2"
__bigversion__ = __version__ + __revision__ __bigversion__ = __version__ + __revision__
__copyright__ = "Copyright 2002-2015 John Goerzen & contributors" __copyright__ = "Copyright 2002-2015 John Goerzen & contributors"
__author__ = "John Goerzen" __author__ = "John Goerzen"

View File

@ -45,10 +45,6 @@ class LocalStatusFolder(BaseFolder):
def isnewfolder(self): def isnewfolder(self):
return not os.path.exists(self.filename) return not os.path.exists(self.filename)
# Interface from BaseFolder
def getname(self):
return self.name
# Interface from BaseFolder # Interface from BaseFolder
def getfullname(self): def getfullname(self):
return self.filename return self.filename
@ -117,33 +113,33 @@ class LocalStatusFolder(BaseFolder):
self.messagelist = {} self.messagelist = {}
return return
# loop as many times as version, and update format # Loop as many times as version, and update format.
for i in range(1, self.cur_version+1): for i in range(1, self.cur_version + 1):
file = open(self.filename, "rt")
self.messagelist = {} self.messagelist = {}
line = file.readline().strip() cache = open(self.filename, "rt")
line = cache.readline().strip()
# convert from format v1 # Format is up to date. break.
if line == (self.magicline % 1): if line == (self.magicline % self.cur_version):
self.ui._msg('Upgrading LocalStatus cache from version 1 to version 2 for %s:%s' %\ break
(self.repository, self))
self.readstatus_v1(file) # Convert from format v1.
file.close() elif line == (self.magicline % 1):
self.ui._msg('Upgrading LocalStatus cache from version 1'
'to version 2 for %s:%s'% (self.repository, self))
self.readstatus_v1(cache)
cache.close()
self.save() self.save()
# NOTE: Add other format transitions here in the future. # NOTE: Add other format transitions here in the future.
# elif line == (self.magicline % 2): # elif line == (self.magicline % 2):
# self.ui._msg('Upgrading LocalStatus cache from version 2 to version 3 for %s:%s' %\ # self.ui._msg(u'Upgrading LocalStatus cache from version 2'
# (self.repository, self)) # 'to version 3 for %s:%s'% (self.repository, self))
# self.readstatus_v2(file) # self.readstatus_v2(cache)
# file.close() # cache.close()
# file.save() # cache.save()
# format is up to date. break # Something is wrong.
elif line == (self.magicline % self.cur_version):
break
# something is wrong
else: else:
errstr = "Unrecognized cache magicline in '%s'" % self.filename errstr = "Unrecognized cache magicline in '%s'" % self.filename
self.ui.warn(errstr) self.ui.warn(errstr)
@ -152,14 +148,14 @@ class LocalStatusFolder(BaseFolder):
if not line: if not line:
# The status file is empty - should not have happened, # The status file is empty - should not have happened,
# but somehow did. # but somehow did.
errstr = "Cache file '%s' is empty. Closing..." % self.filename errstr = "Cache file '%s' is empty."% self.filename
self.ui.warn(errstr) self.ui.warn(errstr)
file.close() cache.close()
return return
assert(line == (self.magicline % self.cur_version)) assert(line == (self.magicline % self.cur_version))
self.readstatus(file) self.readstatus(cache)
file.close() cache.close()
def dropmessagelistcache(self): def dropmessagelistcache(self):
self.messagelist = None self.messagelist = None

View File

@ -15,15 +15,15 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os import os
import re
from sys import exc_info from sys import exc_info
from threading import Lock from threading import Lock
from .Base import BaseFolder
try: try:
import sqlite3 as sqlite import sqlite3 as sqlite
except: except:
pass #fail only if needed later on, not on import pass #fail only if needed later on, not on import
from .Base import BaseFolder
class LocalStatusSQLiteFolder(BaseFolder): class LocalStatusSQLiteFolder(BaseFolder):
"""LocalStatus backend implemented with an SQLite database """LocalStatus backend implemented with an SQLite database
@ -56,7 +56,8 @@ class LocalStatusSQLiteFolder(BaseFolder):
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
if not os.path.isdir(dirname): if not os.path.isdir(dirname):
raise UserWarning("SQLite database path '%s' is not a directory." % dirname) raise UserWarning("SQLite database path '%s' is not a directory."%
dirname)
# dblock protects against concurrent writes in same connection # dblock protects against concurrent writes in same connection
self._dblock = Lock() self._dblock = Lock()
@ -67,14 +68,15 @@ class LocalStatusSQLiteFolder(BaseFolder):
except NameError: except NameError:
# sqlite import had failed # sqlite import had failed
raise UserWarning('SQLite backend chosen, but no sqlite python ' raise UserWarning('SQLite backend chosen, but no sqlite python '
'bindings available. Please install.'), None, exc_info()[2] 'bindings available. Please install.'), None, exc_info()[2]
#Make sure sqlite is in multithreading SERIALIZE mode #Make sure sqlite is in multithreading SERIALIZE mode
assert sqlite.threadsafety == 1, 'Your sqlite is not multithreading safe.' assert sqlite.threadsafety == 1, 'Your sqlite is not multithreading safe.'
#Test if db version is current enough and if db is readable. #Test if db version is current enough and if db is readable.
try: try:
cursor = self.connection.execute("SELECT value from metadata WHERE key='db_version'") cursor = self.connection.execute(
"SELECT value from metadata WHERE key='db_version'")
except sqlite.DatabaseError: except sqlite.DatabaseError:
#db file missing or corrupt, recreate it. #db file missing or corrupt, recreate it.
self.__create_db() self.__create_db()
@ -88,9 +90,6 @@ class LocalStatusSQLiteFolder(BaseFolder):
def storesmessages(self): def storesmessages(self):
return False return False
def getname(self):
return self.name
def getfullname(self): def getfullname(self):
return self.filename return self.filename

View File

@ -44,13 +44,13 @@ class Repository(object):
name = account.getconf('remoterepository') name = account.getconf('remoterepository')
# We don't support Maildirs on the remote side. # We don't support Maildirs on the remote side.
typemap = {'IMAP': IMAPRepository, typemap = {'IMAP': IMAPRepository,
'Gmail': GmailRepository} 'Gmail': GmailRepository}
elif reqtype == 'local': elif reqtype == 'local':
name = account.getconf('localrepository') name = account.getconf('localrepository')
typemap = {'IMAP': MappedIMAPRepository, typemap = {'IMAP': MappedIMAPRepository,
'Maildir': MaildirRepository, 'Maildir': MaildirRepository,
'GmailMaildir': GmailMaildirRepository} 'GmailMaildir': GmailMaildirRepository}
elif reqtype == 'status': elif reqtype == 'status':
# create and return a LocalStatusRepository # create and return a LocalStatusRepository
@ -69,7 +69,7 @@ class Repository(object):
errstr = ("Could not find section '%s' in configuration. Required " errstr = ("Could not find section '%s' in configuration. Required "
"for account '%s'." % ('Repository %s' % name, account)) "for account '%s'." % ('Repository %s' % name, account))
raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \ raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \
None, exc_info()[2] None, exc_info()[2]
try: try:
repo = typemap[repostype] repo = typemap[repostype]
@ -77,7 +77,7 @@ class Repository(object):
errstr = "'%s' repository not supported for '%s' repositories." \ errstr = "'%s' repository not supported for '%s' repositories." \
% (repostype, reqtype) % (repostype, reqtype)
raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \ raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \
None, exc_info()[2] None, exc_info()[2]
return repo(name, account) return repo(name, account)

View File

@ -539,7 +539,7 @@ class UIBase(object):
abortsleep = False abortsleep = False
while sleepsecs > 0 and not abortsleep: while sleepsecs > 0 and not abortsleep:
if account.get_abort_event(): if account.get_abort_event():
abortsleep = True abortsleep = True
else: else:
abortsleep = self.sleeping(10, sleepsecs) abortsleep = self.sleeping(10, sleepsecs)
sleepsecs -= 10 sleepsecs -= 10