From 7e8233131c5d9fe13656db94d5ab3fc994bf9460 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Tue, 3 Apr 2012 01:13:27 +0200 Subject: [PATCH 01/22] Don't fail if no dry-run setting has been specified Somehow we failed if no dry-run setting had been specified in the config file. This got caught thanks to extending the test suite with a stock configuration. Signed-off-by: Sebastian Spaeth --- Changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.rst b/Changelog.rst index e4c21ae..6996f98 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -24,6 +24,9 @@ OfflineIMAP v6.5.3.1 (2012-04-03) * Don't fail if no dry-run setting exists in offlineimap.conf (introduced in 6.5.3) +* Don't fail if no dry-run setting exists in offlineimap.conf + (introduced in 6.5.3) + OfflineIMAP v6.5.3 (2012-04-02) =============================== From a614f9a735b2dfe82302f728ea09e16bb1d3ee5f Mon Sep 17 00:00:00 2001 From: David Logie Date: Fri, 18 May 2012 10:31:08 +0100 Subject: [PATCH 02/22] Fix str.format() calls for Python 2.6. Python 2.6 requires a slightly different string formatting that >2.7. Signed-off-by: Sebastian Spaeth --- Changelog.rst | 2 ++ offlineimap/folder/Base.py | 2 +- offlineimap/ui/UIBase.py | 6 +++--- test/OLItest/TestRunner.py | 8 ++++---- test/tests/test_01_basic.py | 10 +++++----- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 6996f98..e6f021a 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,8 @@ ChangeLog WIP (add new stuff for the next release) ======================================== +* Fix str.format() calls for Python 2.6 (D. Logie) + OfflineIMAP v6.5.4 (2012-06-02) ================================= diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 6f6f364..b1f5a56 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -386,7 +386,7 @@ class BaseFolder(object): self.getmessageuidlist()) num_to_copy = len(copylist) if num_to_copy and self.repository.account.dryrun: - self.ui.info("[DRYRUN] Copy {} messages from {}[{}] to {}".format( + self.ui.info("[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format( num_to_copy, self, self.repository, dstfolder.repository)) return for num, uid in enumerate(copylist): diff --git a/offlineimap/ui/UIBase.py b/offlineimap/ui/UIBase.py index eea929d..fab3818 100644 --- a/offlineimap/ui/UIBase.py +++ b/offlineimap/ui/UIBase.py @@ -301,7 +301,7 @@ class UIBase(object): def makefolder(self, repo, foldername): """Called when a folder is created""" prefix = "[DRYRUN] " if self.dryrun else "" - self.info("{}Creating folder {}[{}]".format( + self.info("{0}Creating folder {1}[{2}]".format( prefix, foldername, repo)) def syncingfolder(self, srcrepos, srcfolder, destrepos, destfolder): @@ -346,7 +346,7 @@ class UIBase(object): def deletingmessages(self, uidlist, destlist): ds = self.folderlist(destlist) prefix = "[DRYRUN] " if self.dryrun else "" - self.info("{}Deleting {} messages ({}) in {}".format( + self.info("{0}Deleting {1} messages ({2}) in {3}".format( prefix, len(uidlist), offlineimap.imaputil.uid_sequence(uidlist), ds)) @@ -474,7 +474,7 @@ class UIBase(object): def callhook(self, msg): if self.dryrun: - self.info("[DRYRUN] {}".format(msg)) + self.info("[DRYRUN] {0}".format(msg)) else: self.info(msg) diff --git a/test/OLItest/TestRunner.py b/test/OLItest/TestRunner.py index 3535b61..828df59 100644 --- a/test/OLItest/TestRunner.py +++ b/test/OLItest/TestRunner.py @@ -127,7 +127,7 @@ class OLITestLib(): reponame: All on `reponame` or all IMAP-type repositories if None""" config = cls.get_default_config() if reponame: - sections = ['Repository {}'.format(reponame)] + sections = ['Repository {0}'.format(reponame)] else: sections = [r for r in config.sections() \ if r.startswith('Repository')] @@ -162,8 +162,8 @@ class OLITestLib(): dirs = [d for d in dirs if d.startswith(b'INBOX.OLItest')] for folder in dirs: res_t, data = imapobj.delete(b'\"'+folder+b'\"') - assert res_t == 'OK', "Folder deletion of {} failed with error"\ - ":\n{} {}".format(folder.decode('utf-8'), res_t, data) + assert res_t == 'OK', "Folder deletion of {0} failed with error"\ + ":\n{1} {2}".format(folder.decode('utf-8'), res_t, data) imapobj.logout() @classmethod @@ -197,7 +197,7 @@ class OLITestLib(): Use some default content if not given""" assert cls.testdir != None while True: # Loop till we found a unique filename - mailfile = '{}:2,'.format(random.randint(0,999999999)) + mailfile = '{0}:2,'.format(random.randint(0,999999999)) mailfilepath = os.path.join(cls.testdir, 'mail', folder, 'new', mailfile) if not os.path.isfile(mailfilepath): diff --git a/test/tests/test_01_basic.py b/test/tests/test_01_basic.py index 8e45a62..3cbd5c9 100644 --- a/test/tests/test_01_basic.py +++ b/test/tests/test_01_basic.py @@ -67,7 +67,7 @@ class TestBasicFunctions(unittest.TestCase): self.assertEqual(res, "") boxes, mails = OLITestLib.count_maildir_mails('') self.assertTrue((boxes, mails)==(0,0), msg="Expected 0 folders and 0 " - "mails, but sync led to {} folders and {} mails".format( + "mails, but sync led to {0} folders and {1} mails".format( boxes, mails)) def test_02_createdir(self): @@ -82,7 +82,7 @@ class TestBasicFunctions(unittest.TestCase): self.assertEqual(res, "") boxes, mails = OLITestLib.count_maildir_mails('') self.assertTrue((boxes, mails)==(2,0), msg="Expected 2 folders and 0 " - "mails, but sync led to {} folders and {} mails".format( + "mails, but sync led to {0} folders and {1} mails".format( boxes, mails)) def test_03_nametransmismatch(self): @@ -101,7 +101,7 @@ class TestBasicFunctions(unittest.TestCase): mismatch = "ERROR: INFINITE FOLDER CREATION DETECTED!" in res self.assertEqual(mismatch, True, msg="Mismatching nametrans rules did " "NOT trigger an 'infinite folder generation' error. Output was:\n" - "{}".format(res)) + "{0}".format(res)) # Write out default config file again OLITestLib.write_config_file() @@ -121,12 +121,12 @@ class TestBasicFunctions(unittest.TestCase): self.assertEqual(res, "") boxes, mails = OLITestLib.count_maildir_mails('') self.assertTrue((boxes, mails)==(1,1), msg="Expected 1 folders and 1 " - "mails, but sync led to {} folders and {} mails".format( + "mails, but sync led to {0} folders and {1} mails".format( boxes, mails)) # The local Mail should have been assigned a proper UID now, check! uids = OLITestLib.get_maildir_uids('INBOX.OLItest') self.assertFalse (None in uids, msg = "All mails should have been "+ \ - "assigned the IMAP's UID number, but {} messages had no valid ID "\ + "assigned the IMAP's UID number, but {0} messages had no valid ID "\ .format(len([None for x in uids if x==None]))) def test_05_createfolders(self): From 36156fa98545792649771d2599ee06a74d35169c Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Wed, 6 Jun 2012 10:02:42 +0200 Subject: [PATCH 03/22] Remove the APPENDUID hack, previously introduced As Gmail was only announcing the presence of the UIDPLUS extension after we logged in, and we were then only getting server capabilities before, a hack was introduced that checked the existence of an APPENDUID reply, even if the server did not claim to support it. However, John Wiegley reports problems, where the APPENDUID would be None, and we attempt to go this path (it seems that imaplib2 returns [None] if there is no such reply, so our test here for "!=" might fail. Given that this is an undocumented imaplib2 function anyway, and we do fetch gmail capabilities after authentication, this hack should no longer be necessary. We had problems there earlier, where imapobj.response() would return [None] although we had received a APPENDUID response from the server, this might need more debugging and testing. Signed-off-by: Sebastian Spaeth --- Changelog.rst | 2 ++ offlineimap/folder/IMAP.py | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index e6f021a..30b58eb 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -9,6 +9,8 @@ WIP (add new stuff for the next release) ======================================== * Fix str.format() calls for Python 2.6 (D. Logie) +* Remove APPENDUID hack, previously introduced to fix Gmail, no longer + necessary, it might have been breaking things. (J. Wiegley) OfflineIMAP v6.5.4 (2012-06-02) ================================= diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index bb1b51f..75520c2 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -575,9 +575,8 @@ class IMAPFolder(BaseFolder): (typ,dat) = imapobj.check() assert(typ == 'OK') - # get the new UID. Test for APPENDUID response even if the - # server claims to not support it, as e.g. Gmail does :-( - if use_uidplus or imapobj._get_untagged_response('APPENDUID', True): + # get the new UID, do we use UIDPLUS? + if use_uidplus: # get new UID from the APPENDUID response, it could look # like OK [APPENDUID 38505 3955] APPEND completed with # 38505 bein folder UIDvalidity and 3955 the new UID. @@ -585,7 +584,7 @@ class IMAPFolder(BaseFolder): # often seems to return [None], even though we have # data. TODO resp = imapobj._get_untagged_response('APPENDUID') - if resp == [None]: + if resp == [None] or resp is None: self.ui.warn("Server supports UIDPLUS but got no APPENDUID " "appending a message.") return 0 From d45872e59cfed26b779544a1dec35313cf70a2ac Mon Sep 17 00:00:00 2001 From: Dmitrijs Ledkovs Date: Tue, 5 Jun 2012 01:45:10 +0100 Subject: [PATCH 04/22] Add missing OfflineImapError import in folder/UIDMaps.py Bug-Debian: http://bugs.debian.org/671279 Reported-by: Ansgar Burchardt Signed-off-by: Dmitrijs Ledkovs Signed-off-by: Nicolas Sebrecht --- offlineimap/folder/UIDMaps.py | 1 + 1 file changed, 1 insertion(+) diff --git a/offlineimap/folder/UIDMaps.py b/offlineimap/folder/UIDMaps.py index f1c11e4..f571772 100644 --- a/offlineimap/folder/UIDMaps.py +++ b/offlineimap/folder/UIDMaps.py @@ -15,6 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from threading import Lock +from offlineimap import OfflineImapError from .IMAP import IMAPFolder import os.path From 799905325ceba72b2038df132298816c1a2a06eb Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Mon, 23 Jul 2012 18:05:46 +0200 Subject: [PATCH 05/22] docs: split long lines (up to 80 characters) Not everybody has a wide screen. Signed-off-by: Nicolas Sebrecht --- README | 8 ++++++-- docs/INSTALL.rst | 27 ++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/README b/README index dc95198..5f70360 100644 --- a/README +++ b/README @@ -10,7 +10,9 @@ messages on each computer, and changes you make one place will be visible on all other systems. For instance, you can delete a message on your home computer, and it will appear deleted on your work computer as well. OfflineIMAP is also useful if you want to use a mail reader that does not have IMAP support, has poor IMAP -support, or does not provide disconnected operation. It's homepage at http://offlineimap.org contains more information, source code, and online documentation. +support, or does not provide disconnected operation. It's homepage at +http://offlineimap.org contains more information, source code, and online +documentation. OfflineIMAP does not require additional python dependencies beyond python >=2.6 (although python-sqlite is strongly recommended). @@ -87,7 +89,9 @@ Mailing list & bug reporting ---------------------------- The user discussion, development and all exciting stuff take place in the -OfflineImap mailing list at http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project. You do not need to subscribe to send emails. +OfflineImap mailing list at +http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project. You do not +need to subscribe to send emails. Bugs, issues and contributions should be reported to the mailing list. Bugs can also be reported in the issue tracker at https://github.com/spaetz/offlineimap/issues. diff --git a/docs/INSTALL.rst b/docs/INSTALL.rst index d14c574..f86849d 100644 --- a/docs/INSTALL.rst +++ b/docs/INSTALL.rst @@ -37,29 +37,40 @@ In order to use `OfflineIMAP`_, you need to have these conditions satisfied: Installation ------------ -Installing OfflineImap should usually be quite easy, as you can simply unpack and run OfflineImap in place if you wish to do so. There are a number of options though: +Installing OfflineImap should usually be quite easy, as you can simply unpack +and run OfflineImap in place if you wish to do so. There are a number of options +though: #. system-wide :ref:`installation via your distribution package manager ` #. system-wide or single user :ref:`installation from the source package ` #. system-wide or single user :ref:`installation from a git checkout ` -Having installed OfflineImap, you will need to configure it, to be actually useful. Please check the :ref:`Configuration` section in the :doc:`MANUAL` for more information on the configuration step. +Having installed OfflineImap, you will need to configure it, to be actually +useful. Please check the :ref:`Configuration` section in the :doc:`MANUAL` for +more information on the configuration step. .. _inst_pkg_man: System-Wide Installation via distribution ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The easiest way to install OfflineIMAP is via your distribution's package manager. OfflineImap is available under the name `offlineimap` in most Linux and BSD distributions. +The easiest way to install OfflineIMAP is via your distribution's package +manager. OfflineImap is available under the name `offlineimap` in most Linux and +BSD distributions. .. _inst_src_tar: Installation from source package ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Download the latest source archive from our `download page `_. Simply click the "Download as .zip" or "Download as .tar.gz" buttons to get the latest "stable" code from the master branch. If you prefer command line, you will want to use: - wget https://github.com/spaetz/offlineimap/tarball/master -Unpack and continue with the :ref:`system-wide installation ` or the :ref:`single-user installation ` section. +Download the latest source archive from our `download page +`_. Simply click the "Download +as .zip" or "Download as .tar.gz" buttons to get the latest "stable" code from +the master branch. If you prefer command line, you will want to use: wget +https://github.com/spaetz/offlineimap/tarball/master + +Unpack and continue with the :ref:`system-wide installation ` +or the :ref:`single-user installation ` section. .. _inst_git: @@ -78,7 +89,9 @@ checkout a particular release like this:: cd offlineimap git checkout v6.5.2.1 -You have now a source tree available and proceed with either the :ref:`system-wide installation ` or the :ref:`single-user installation `. +You have now a source tree available and proceed with either the +:ref:`system-wide installation ` or the :ref:`single-user +installation `. .. _system_wide_inst: From 131ac5c8276b7359d307073130406e64835b8f49 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 5 Aug 2012 22:40:52 +0400 Subject: [PATCH 06/22] IMAPlib mixin class: pass 'readonly' exception to our callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow our callers who are capable of dealing with readonly folders to properly detect this condition and act accordingly. One example is Gmail's "Chats" folder that is read-only, but contains logs of the quick chats. Minor Changelog improvements. Tested-by: Abdó Roig-Maranges Signed-off-by: Eygene Ryabinkin Signed-off-by: Nicolas Sebrecht --- Changelog.rst | 14 +++++++------- offlineimap/imaplibutil.py | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 30b58eb..4b31f39 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,17 +8,20 @@ ChangeLog WIP (add new stuff for the next release) ======================================== +* Learn to deal with readonly folders to properly detect this condition and act + accordingly. One example is Gmail's "Chats" folder that is read-only, + but contains logs of the quick chats. (E. Ryabinkin) * Fix str.format() calls for Python 2.6 (D. Logie) * Remove APPENDUID hack, previously introduced to fix Gmail, no longer necessary, it might have been breaking things. (J. Wiegley) OfflineIMAP v6.5.4 (2012-06-02) -================================= +=============================== * bump bundled imaplib2 library 2.29 --> 2.33 * Actually perform the SSL fingerprint check (reported by J. Cook) * Curses UI, don't use colors after we shut down curses already (C.Höger) -* Document that '%' needs encoding as '%%' in *.conf +* Document that '%' needs encoding as '%%' in configuration files. * Fix crash when IMAP.quickchanged() led to an Error (reported by sharat87) * Implement the createfolders setting to disable folder propagation (see docs) @@ -28,9 +31,6 @@ OfflineIMAP v6.5.3.1 (2012-04-03) * Don't fail if no dry-run setting exists in offlineimap.conf (introduced in 6.5.3) -* Don't fail if no dry-run setting exists in offlineimap.conf - (introduced in 6.5.3) - OfflineIMAP v6.5.3 (2012-04-02) =============================== @@ -67,7 +67,7 @@ OfflineIMAP v6.5.3 (2012-04-02) * Improve compatability of the curses UI with python 2.6 OfflineIMAP v6.5.2.1 (2012-04-04) -===================================== +================================= * Fix python2.6 compatibility with the TTYUI backend (crash) @@ -123,7 +123,7 @@ Smallish bug fixes that deserve to be put out. * Add filter information to the filter list in --info output OfflineIMAP v6.5.1.1 (2012-01-07) - "Das machine control is nicht fur gerfinger-poken und mittengrabben" -================================================================================================================== +======================================================================================================== Blinkenlights UI 6.5.0 regression fixes only. diff --git a/offlineimap/imaplibutil.py b/offlineimap/imaplibutil.py index aa165f0..4290b2b 100644 --- a/offlineimap/imaplibutil.py +++ b/offlineimap/imaplibutil.py @@ -49,6 +49,9 @@ class UsefulIMAPMixIn(object): del self.untagged_responses[:] try: result = super(UsefulIMAPMixIn, self).select(mailbox, readonly) + except self.readonly as e: + # pass self.readonly to our callers + raise except self.abort as e: # self.abort is raised when we are supposed to retry errstr = "Server '%s' closed connection, error on SELECT '%s'. Ser"\ From 44d2199a00e731495117e613e29629504eca82e0 Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Mon, 23 Jul 2012 18:03:43 +0200 Subject: [PATCH 07/22] docs: update links to the newly created github organization Acually, these are the minimal expected changes. More might be done and/or discussed. Signed-off-by: Nicolas Sebrecht --- README | 4 +++- docs/INSTALL.rst | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README b/README index 5f70360..8f80d7a 100644 --- a/README +++ b/README @@ -93,7 +93,9 @@ OfflineImap mailing list at http://lists.alioth.debian.org/mailman/listinfo/offlineimap-project. You do not need to subscribe to send emails. -Bugs, issues and contributions should be reported to the mailing list. Bugs can also be reported in the issue tracker at https://github.com/spaetz/offlineimap/issues. +Bugs, issues and contributions should be reported to the mailing list. Bugs can +also be reported in the issue tracker at +https://github.com/OfflineIMAP/offlineimap/issues. Configuration Examples ====================== diff --git a/docs/INSTALL.rst b/docs/INSTALL.rst index f86849d..83608d4 100644 --- a/docs/INSTALL.rst +++ b/docs/INSTALL.rst @@ -1,6 +1,6 @@ .. -*- coding: utf-8 -*- -.. _OfflineIMAP: https://github.com/spaetz/offlineimap -.. _OLI_git_repo: git://github.com/spaetz/offlineimap.git +.. _OfflineIMAP: https://github.com/OfflineIMAP/offlineimap +.. _OLI_git_repo: git://github.com/OfflineIMAP/offlineimap.git ============ Installation From 6e5fbebc0e5e6388cf03ce0f7267638081294b77 Mon Sep 17 00:00:00 2001 From: Vladimir Nesov Date: Fri, 17 Aug 2012 17:52:20 +0000 Subject: [PATCH 08/22] fix: createfolders setting is ignored if readonly is True If 'readonly' is True, folders shouldn't be created (regardless of 'createfolders' option). With old behavior, instead folders were always created when 'readonly' is True (even if 'createfolders' was also False), which is a serious bug (offlineimap was creating folders in all read-only repositories). 'createfolders' should only play a role if 'readonly' is False, in which case folders should only be created if 'createfolders' is True. Submitted-by: Vladimir Nesov Signed-off-by: Nicolas Sebrecht --- Changelog.rst | 1 + offlineimap/repository/Base.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.rst b/Changelog.rst index 4b31f39..aa0d903 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,7 @@ ChangeLog WIP (add new stuff for the next release) ======================================== +* Don't create folders if readonly is enabled. * Learn to deal with readonly folders to properly detect this condition and act accordingly. One example is Gmail's "Chats" folder that is read-only, but contains logs of the quick chats. (E. Ryabinkin) diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py index 81050b0..febaf3f 100644 --- a/offlineimap/repository/Base.py +++ b/offlineimap/repository/Base.py @@ -134,7 +134,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object): It is disabled by either setting the whole repository 'readonly' or by using the 'createfolders' setting.""" - return self._readonly or self.getconfboolean('createfolders', True) + return (not self._readonly) and self.getconfboolean('createfolders', Tr def makefolder(self, foldername): """Create a new folder""" From a134301ac505678593a42ac8c483cf1b42ad6bad Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Tue, 21 Aug 2012 16:48:26 +0200 Subject: [PATCH 09/22] Improve 'find first quotation' regex Reported by http://www.dfranke.com/blog/2012/08/20/offlineimap-error-beim-syncen-mit-lotus-domino/ our 'find the first quote possible containing encoded quotation marks' regex did not seem to have caught all cases. E.g. "\\". Verified the fix as good. Thanks Daniel Franke. Signed-off-by: Sebastian Spaeth --- Changelog.rst | 2 ++ offlineimap/imaputil.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.rst b/Changelog.rst index aa0d903..f126075 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -15,6 +15,8 @@ WIP (add new stuff for the next release) * Fix str.format() calls for Python 2.6 (D. Logie) * Remove APPENDUID hack, previously introduced to fix Gmail, no longer necessary, it might have been breaking things. (J. Wiegley) +* Improve regex that could lead to 'NoneType' object has no attribute 'group' + (D. Franke) OfflineIMAP v6.5.4 (2012-06-02) =============================== diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py index 557064b..fe69b7a 100644 --- a/offlineimap/imaputil.py +++ b/offlineimap/imaputil.py @@ -23,7 +23,7 @@ from offlineimap.ui import getglobalui # find the first quote in a string quotere = re.compile( - r"""(?P"(?:\\"|[^"])*") # Quote, possibly containing encoded + r"""(?P"[^\"\\]*(?:\\"|[^"])*") # Quote, possibly containing encoded # quotation mark \s*(?P.*)$ # Whitespace & remainder of string""", re.VERBOSE) From 925a5bcae16b855e1f2cd990c8e947972bdebb6e Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Tue, 21 Aug 2012 16:59:52 +0200 Subject: [PATCH 10/22] Fix cut-off line Commit 6e5fbebc introduced a cut-off line. Fixing that. All tests ran successfully. Signed-off-by: Sebastian Spaeth --- offlineimap/repository/Base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py index febaf3f..0c41c2d 100644 --- a/offlineimap/repository/Base.py +++ b/offlineimap/repository/Base.py @@ -133,8 +133,9 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object): """Is folder creation enabled on this repository? It is disabled by either setting the whole repository - 'readonly' or by using the 'createfolders' setting.""" - return (not self._readonly) and self.getconfboolean('createfolders', Tr + 'readonly' or by using the 'createfolders' setting.""" + return (not self._readonly) and \ + self.getconfboolean('createfolders', True) def makefolder(self, foldername): """Create a new folder""" From 103524c97970c3f4aaeefcf86f73f91dc0924629 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 31 Aug 2012 20:05:01 +0200 Subject: [PATCH 11/22] Throw OfflineImapErrors rather than weird Exceptions When misconfiguring OLI, e.g. by specifying a repository name that was not configured anywhere, we would bomb out with cryptic "NoSectionError". Throw OfflineImapError that explains what has happened. We still need to avoid throwing exceptions with Tracebacks here though. Signed-off-by: Sebastian Spaeth --- Changelog.rst | 1 + offlineimap/repository/__init__.py | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index f126075..0a57919 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -17,6 +17,7 @@ WIP (add new stuff for the next release) necessary, it might have been breaking things. (J. Wiegley) * Improve regex that could lead to 'NoneType' object has no attribute 'group' (D. Franke) +* Improved error throwing on repository misconfiguration OfflineIMAP v6.5.4 (2012-06-02) =============================== diff --git a/offlineimap/repository/__init__.py b/offlineimap/repository/__init__.py index 0059bdf..22cd128 100644 --- a/offlineimap/repository/__init__.py +++ b/offlineimap/repository/__init__.py @@ -15,10 +15,17 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +try: + from configparser import NoSectionError +except ImportError: #python2 + from ConfigParser import NoSectionError + from offlineimap.repository.IMAP import IMAPRepository, MappedIMAPRepository from offlineimap.repository.Gmail import GmailRepository from offlineimap.repository.Maildir import MaildirRepository from offlineimap.repository.LocalStatus import LocalStatusRepository +from offlineimap.error import OfflineImapError + class Repository(object): """Abstract class that returns the correct Repository type @@ -47,17 +54,26 @@ class Repository(object): return LocalStatusRepository(name, account) else: - raise ValueError("Request type %s not supported" % reqtype) + errstr = "Repository type %s not supported" % reqtype + raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO) + # Get repository type config = account.getconfig() - repostype = config.get('Repository ' + name, 'type').strip() + try: + repostype = config.get('Repository ' + name, 'type').strip() + except NoSectionError as e: + errstr = ("Could not find section '%s' in configuration. Required " + "for account '%s'." % ('Repository %s' % name, account)) + raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO) + try: repo = typemap[repostype] except KeyError: - raise ValueError("'%s' repository not supported for %s repositories" - "." % (repostype, reqtype)) - return repo(name, account) + errstr = "'%s' repository not supported for '%s' repositories." \ + % (repostype, reqtype) + raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO) + return repo(name, account) def __init__(self, account, reqtype): """Load the correct Repository type and return that. The From 04ffae2f004648cf2b4a0d4bffb029eca3b19b8d Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 31 Aug 2012 20:34:54 +0200 Subject: [PATCH 12/22] Exit with nice error message failing to get repositories If we throw an OfflineImapError in case of the Repository() initialization, we display the nice error message and exit rather than bomb out with a traceback. Misconfiguring a repository name in the configuration file is now nicely pointed out to the user. Signed-off-by: Sebastian Spaeth --- offlineimap/accounts.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index 8b39c7e..c199861 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -217,13 +217,19 @@ class SyncableAccount(Account): def syncrunner(self): self.ui.registerthread(self) - accountmetadata = self.getaccountmeta() - if not os.path.exists(accountmetadata): - os.mkdir(accountmetadata, 0o700) + try: + accountmetadata = self.getaccountmeta() + if not os.path.exists(accountmetadata): + os.mkdir(accountmetadata, 0o700) - self.remoterepos = Repository(self, 'remote') - self.localrepos = Repository(self, 'local') - self.statusrepos = Repository(self, 'status') + self.remoterepos = Repository(self, 'remote') + self.localrepos = Repository(self, 'local') + self.statusrepos = Repository(self, 'status') + except OfflineImapError as e: + self.ui.error(e, exc_info()[2]) + if e.severity >= OfflineImapError.ERROR.CRITICAL: + raise + return # Loop account sync if needed (bail out after 3 failures) looping = 3 From 3cb2ddccb897cfd1d6bcb4a2b9393040b899bd3e Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 31 Aug 2012 22:34:53 +0200 Subject: [PATCH 13/22] Use "with lock" pattern While looking at the code to investigate if an why we sometimes don't seem to honor the write lock, I made it use the more modern "with lock:" pattern. Still have not found out how we could ever be using 2 instances of the LocalStatusFolder for the same folder. Signed-off-by: Sebastian Spaeth --- offlineimap/folder/LocalStatus.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/offlineimap/folder/LocalStatus.py b/offlineimap/folder/LocalStatus.py index b3779fd..6f65f83 100644 --- a/offlineimap/folder/LocalStatus.py +++ b/offlineimap/folder/LocalStatus.py @@ -85,8 +85,7 @@ class LocalStatusFolder(BaseFolder): file.close() def save(self): - self.savelock.acquire() - try: + with self.savelock: file = open(self.filename + ".tmp", "wt") file.write(magicline + "\n") for msg in self.messagelist.values(): @@ -104,9 +103,6 @@ class LocalStatusFolder(BaseFolder): os.fsync(fd) os.close(fd) - finally: - self.savelock.release() - def getmessagelist(self): return self.messagelist From a1dc76ae917d93d0c387bbd506faea3487211a6c Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Fri, 31 Aug 2012 23:11:11 +0200 Subject: [PATCH 14/22] Don't output initial blurb in "quiet" mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When logging to a file using the -l switch, we would still write an initial banner to the file. This was never intended. Quiet should be really quiet unless it experiences an error. Simplify the logging statement, to do nothing if logevel is set to "WARNING" aka quiet. Signed-off-by: Sebastian Spaeth --- offlineimap/ui/UIBase.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/offlineimap/ui/UIBase.py b/offlineimap/ui/UIBase.py index fab3818..2c30b94 100644 --- a/offlineimap/ui/UIBase.py +++ b/offlineimap/ui/UIBase.py @@ -88,7 +88,6 @@ class UIBase(object): def setlogfile(self, logfile): """Create file handler which logs to file""" fh = logging.FileHandler(logfile, 'at') - #fh.setLevel(logging.DEBUG) file_formatter = logging.Formatter("%(asctime)s %(levelname)s: " "%(message)s", '%Y-%m-%d %H:%M:%S') fh.setFormatter(file_formatter) @@ -98,9 +97,7 @@ class UIBase(object): msg = "OfflineImap %s starting...\n Python: %s Platform: %s\n "\ "Args: %s" % (offlineimap.__version__, p_ver, sys.platform, " ".join(sys.argv)) - record = logging.LogRecord('OfflineImap', logging.INFO, __file__, - None, msg, None, None) - fh.emit(record) + self.logger.info(msg) def _msg(self, msg): """Display a message.""" From 1b54b85f2026be053bad4dcbcba9b16bbdd95a3d Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Sat, 1 Sep 2012 01:02:20 +0200 Subject: [PATCH 15/22] Reuse LocalStatus() folders rather than recreate instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we ask twice for a LocalStatusFolder via getfolder(), we would get a newly created instance each time. This can lead to problems, as e.g. write locks protecting files only work within the same Folder instance. Make it so, that we cache all Folder instances that we have asked for and hand back the existing one if we ask again for it, rather than recreate a new instance. Also, make getfolders() a noop for LocalStatus. We attempted to derive the foldername from the name of the LocalStatusfile. However, this is not really possible, as we do file name mangling (".$" -> "dot", "/" -> ".") and there is no way to get the original folder name from the LocalStatus file name anyway. This commit could potentially solve the "file not found" errors, that people have been seeing with their LocalStatusCache files. If we have 2 instances of a LocalStatusFolder pointing to the same file, our locking system would not work. Signed-off-by: Sebastian Spaeth --- offlineimap/accounts.py | 1 - offlineimap/repository/LocalStatus.py | 26 +++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index c199861..1ce9b85 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -295,7 +295,6 @@ class SyncableAccount(Account): # folder delimiter etc) remoterepos.getfolders() localrepos.getfolders() - statusrepos.getfolders() remoterepos.sync_folder_structure(localrepos, statusrepos) # replicate the folderstructure between REMOTE to LOCAL diff --git a/offlineimap/repository/LocalStatus.py b/offlineimap/repository/LocalStatus.py index 3e5db2f..73a6a62 100644 --- a/offlineimap/repository/LocalStatus.py +++ b/offlineimap/repository/LocalStatus.py @@ -43,8 +43,8 @@ class LocalStatusRepository(BaseRepository): if not os.path.exists(self.root): os.mkdir(self.root, 0o700) - # self._folders is a list of LocalStatusFolders() - self._folders = None + # self._folders is a dict of name:LocalStatusFolders() + self._folders = {} def getsep(self): return '.' @@ -83,19 +83,23 @@ class LocalStatusRepository(BaseRepository): def getfolder(self, foldername): """Return the Folder() object for a foldername""" - return self.LocalStatusFolderClass(foldername, self) + if foldername in self._folders: + return self._folders[foldername] + + folder = self.LocalStatusFolderClass(foldername, self) + self._folders[foldername] = folder + return folder def getfolders(self): - """Returns a list of all cached folders.""" - if self._folders != None: - return self._folders + """Returns a list of all cached folders. - self._folders = [] - for folder in os.listdir(self.root): - self._folders.append(self.getfolder(folder)) - return self._folders + Does nothing for this backend. We mangle the folder file names + (see getfolderfilename) so we can not derive folder names from + the file names that we have available. TODO: need to store a + list of folder names somehow?""" + pass def forgetfolders(self): """Forgets the cached list of folders, if any. Useful to run after a sync run.""" - self._folders = None + self._folders = {} From e7ca5b25cba0679aa800b490d063b24c40864839 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 1 Sep 2012 02:16:06 +0200 Subject: [PATCH 16/22] Combine checks for ignored folders in one place - Factor out the code to find a local folder given a remote folder Patch by Dave, split and modified by Sebastian. Signed-off-by: Sebastian Spaeth --- offlineimap/accounts.py | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index 1ce9b85..f6aae6a 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -261,6 +261,12 @@ class SyncableAccount(Account): if looping and self.sleeper() >= 2: looping = 0 + def get_local_folder(self, remotefolder): + """Return the corresponding local folder for a given remotefolder""" + return self.localrepos.getfolder( + remotefolder.getvisiblename(). + replace(self.remoterepos.getsep(), self.localrepos.getsep())) + def sync(self): """Synchronize the account once, then return @@ -305,10 +311,13 @@ class SyncableAccount(Account): for remotefolder in remoterepos.getfolders(): # check for CTRL-C or SIGTERM if Account.abort_NOW_signal.is_set(): break - if not remotefolder.sync_this: - self.ui.debug('', "Not syncing filtered remote folder '%s'" + + localfolder = self.get_local_folder(remotefolder) + if not (remotefolder.sync_this + and localfolder.sync_this): + self.ui.debug('', "Not syncing filtered folder '%s'" "[%s]" % (remotefolder, remoterepos)) - continue # Filtered out remote folder + continue # Ignore filtered folder thread = InstanceLimitedThread(\ instancename = 'FOLDER_' + self.remoterepos.getname(), target = syncfolder, @@ -372,17 +381,8 @@ def syncfolder(account, remotefolder, quick): ui.registerthread(account) try: # Load local folder. - localfolder = localrepos.\ - getfolder(remotefolder.getvisiblename().\ - replace(remoterepos.getsep(), localrepos.getsep())) + localfolder = account.get_local_folder(remotefolder) - #Filtered folders on the remote side will not invoke this - #function, but we need to NOOP if the local folder is filtered - #out too: - if not localfolder.sync_this: - ui.debug('', "Not syncing filtered local folder '%s'" \ - % localfolder) - return # Write the mailboxes mbnames.add(account.name, localfolder.getname()) @@ -462,15 +462,8 @@ def syncfolder(account, remotefolder, quick): if e.severity > OfflineImapError.ERROR.FOLDER: raise else: - #if the initial localfolder assignement bailed out, the localfolder var will not be available, so we need ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' " - "[acc: '%s']" % ( - remotefolder.getvisiblename().\ - replace(remoterepos.getsep(), localrepos.getsep()), - account)) - # we reconstruct foldername above rather than using - # localfolder, as the localfolder var is not - # available if assignment fails. + "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (account, remotefolder.getvisiblename(), From e94642bb4d591a1ea12b837daeaafb94076a9f0b Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 1 Sep 2012 02:30:46 +0200 Subject: [PATCH 17/22] Determine folder syncing on Folder initialization Rather than later when retrieving them by IMAP, we determine the "sync_this" value of a folder on Folder() initialization. This also fixes a bug where folder structure was not propagated when a folder was filtered out but included in the folderincludes list. Patch by Dave, split and modified slightly by Sebastian Signed-off-by: Sebastian Spaeth --- offlineimap/folder/Base.py | 12 ++++++++++-- offlineimap/repository/Base.py | 6 +++++- offlineimap/repository/IMAP.py | 5 ----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index b1f5a56..c132713 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -31,9 +31,12 @@ class BaseFolder(object): :para name: Path & name of folder minus root or reference :para repository: Repository() in which the folder is. """ - self.sync_this = True - """Should this folder be included in syncing?""" self.ui = getglobalui() + """Should this folder be included in syncing?""" + self._sync_this = repository.should_sync_folder(name) + if not self._sync_this: + self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter" \ + % (name, repository)) # Top level dir name is always '' self.name = name if not name == self.getsep() else '' self.repository = repository @@ -57,6 +60,11 @@ class BaseFolder(object): """Account name as string""" return self.repository.accountname + @property + def sync_this(self): + """Should this folder be synced or is it e.g. filtered out?""" + return self._sync_this + def suggeststhreads(self): """Returns true if this folder suggests using threads for actions; false otherwise. Probably only IMAP will return true.""" diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py index 0c41c2d..575c993 100644 --- a/offlineimap/repository/Base.py +++ b/offlineimap/repository/Base.py @@ -129,6 +129,10 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object): def getsep(self): raise NotImplementedError + def should_sync_folder(self, fname): + """Should this folder be synced?""" + return fname in self.folderincludes or self.folderfilter(fname) + def get_create_folders(self): """Is folder creation enabled on this repository? @@ -202,7 +206,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object): # Does nametrans back&forth lead to identical names? # 1) would src repo filter out the new folder name? In this # case don't create it on it: - if not self.folderfilter(dst_name_t): + if not self.should_sync_folder(dst_name_t): self.ui.debug('', "Not creating folder '%s' (repository '%s" "') as it would be filtered out on that repository." % (dst_name_t, self)) diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index 5ad787a..be8c858 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -287,11 +287,6 @@ class IMAPRepository(BaseRepository): foldername = imaputil.dequote(name) retval.append(self.getfoldertype()(self.imapserver, foldername, self)) - # filter out the folder? - if not self.folderfilter(foldername): - self.ui.debug('imap', "Filtering out '%s'[%s] due to folderfilt" - "er" % (foldername, self)) - retval[-1].sync_this = False # Add all folderincludes if len(self.folderincludes): imapobj = self.imapserver.acquireconnection() From 7d1d5283f8c058e23200817bd0c47c04982075a4 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 1 Sep 2012 02:49:00 +0200 Subject: [PATCH 18/22] No need to filter Maildir folders here commit e94642bb4d591a1ea12 centralized folder filtering by using the repository.should_sync_folder() function. Therefore there is no need to check for folderfilter in the Maildir backend separately. Origina patch by Dave, split into 3 by Sebastian Signed-off-by: Sebastian Spaeth --- offlineimap/repository/Maildir.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/offlineimap/repository/Maildir.py b/offlineimap/repository/Maildir.py index f197002..f2d5581 100644 --- a/offlineimap/repository/Maildir.py +++ b/offlineimap/repository/Maildir.py @@ -181,11 +181,6 @@ class MaildirRepository(BaseRepository): foldername, self.getsep(), self)) - # filter out the folder? - if not self.folderfilter(foldername): - self.debug("Filtering out '%s'[%s] due to folderfilt" - "er" % (foldername, self)) - retval[-1].sync_this = False if self.getsep() == '/' and dirname != '': # Recursively check sub-directories for folders too. From 49c11fc8f0ea8cb72de558758b9870863b20ad85 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Sat, 1 Sep 2012 02:58:14 +0200 Subject: [PATCH 19/22] LocalStatus._folders needs to be {} not None the LocalStatus._folders cache was changed to be a dict that can be searched for names. One instance were _folders was set to "None" was accidentally left over. Fix this. Signed-off-by: Sebastian Spaeth --- offlineimap/repository/LocalStatus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/offlineimap/repository/LocalStatus.py b/offlineimap/repository/LocalStatus.py index 73a6a62..b1b763a 100644 --- a/offlineimap/repository/LocalStatus.py +++ b/offlineimap/repository/LocalStatus.py @@ -79,7 +79,7 @@ class LocalStatusRepository(BaseRepository): file.close() os.rename(filename + ".tmp", filename) # Invalidate the cache. - self._folders = None + self._folders = {} def getfolder(self, foldername): """Return the Folder() object for a foldername""" From 3476e9ab36b861fefde5211cb39a71623738dd71 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Sat, 1 Sep 2012 03:12:16 +0200 Subject: [PATCH 20/22] Fix fallout when filtering folders Previous commit e7ca5b25cba0679aa800b490d063b24c40864839 combined checks for filtered folders in one place. However, it turns out there was a reason to have them separate. getfolder() on a non-existent Maildir fails and there might not be an equivalent local Maildir folder for a filtered out IMAP folder. Fix this by first checking if the remote folder should be filtered, and only then retrieving the local folder (which should exist then). This bug was found by our test suite! Signed-off-by: Sebastian Spaeth --- offlineimap/accounts.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index f6aae6a..dd65842 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -312,12 +312,15 @@ class SyncableAccount(Account): # check for CTRL-C or SIGTERM if Account.abort_NOW_signal.is_set(): break - localfolder = self.get_local_folder(remotefolder) - if not (remotefolder.sync_this - and localfolder.sync_this): + if not remotefolder.sync_this: self.ui.debug('', "Not syncing filtered folder '%s'" "[%s]" % (remotefolder, remoterepos)) continue # Ignore filtered folder + localfolder = self.get_local_folder(remotefolder) + if not localfolder.sync_this: + self.ui.debug('', "Not syncing filtered folder '%s'" + "[%s]" % (localfolder, localfolder.repository)) + continue # Ignore filtered folder thread = InstanceLimitedThread(\ instancename = 'FOLDER_' + self.remoterepos.getname(), target = syncfolder, From ad66a4648e0bcc253e4c28427641358f7f53687d Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Sat, 25 Aug 2012 20:37:06 +0200 Subject: [PATCH 21/22] add official maintainers list with minimal informations Signed-off-by: Nicolas Sebrecht --- MAINTAINERS | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 MAINTAINERS diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..f237f00 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,19 @@ + +Official maintainers +==================== + +Dmitrijs Ledkovs + email: xnox at debian.org + github: xnox + +Eygene Ryabinkin + email: rea at freebsd.org + github: konvpalto + +Nicolas Sebrecht + email: nicolas.s-dev at laposte.net + github: nicolas33 + +Sebastian Spaeth + email: sebastian at sspaeth.de + github: spaetz From 721035eacef450247efd8aa2eb4b964e41407f1b Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Wed, 5 Sep 2012 17:40:04 +0200 Subject: [PATCH 22/22] Release v6.5.5-rc2 (Same as rc1, but with Changelog updated and version number bumped) Signed-off-by: Sebastian Spaeth --- Changelog.rst | 9 ++++++++- offlineimap/__init__.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 0a57919..643e63e 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -4,10 +4,17 @@ ChangeLog :website: http://offlineimap.org - WIP (add new stuff for the next release) ======================================== +OfflineIMAP v6.5.5-rc1 (2012-09-05) +=================================== + +* Bump version number + +OfflineIMAP v6.5.5-rc1 (2012-09-05) +=================================== + * Don't create folders if readonly is enabled. * Learn to deal with readonly folders to properly detect this condition and act accordingly. One example is Gmail's "Chats" folder that is read-only, diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py index 77495e4..a7d043c 100644 --- a/offlineimap/__init__.py +++ b/offlineimap/__init__.py @@ -1,7 +1,7 @@ __all__ = ['OfflineImap'] __productname__ = 'OfflineIMAP' -__version__ = "6.5.4" +__version__ = "6.5.5-rc2" __copyright__ = "Copyright 2002-2012 John Goerzen & contributors" __author__ = "John Goerzen" __author_email__= "john@complete.org"