From ee89610f3ad9a489bcd165273cb1d32b53946b96 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Wed, 14 May 2014 18:02:53 +0400 Subject: [PATCH 1/7] Hacking manual: documented how to create tags Signed-off-by: Eygene Ryabinkin --- docs/doc-src/HACKING.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/doc-src/HACKING.rst b/docs/doc-src/HACKING.rst index 54a89ef..d4b3001 100644 --- a/docs/doc-src/HACKING.rst +++ b/docs/doc-src/HACKING.rst @@ -120,6 +120,15 @@ A typical release cycle works like this: 5. When we think a release is stable enough, we restart from step 1. +Tagging release or RC +--------------------- + +It is done via Git's ``tag`` command, but you must do ``git tag -a`` +to create annotated tag. + +Release tags are named ``vX.Y.Z`` and release candidate tags are named +``vX.Y.Z-rcN``. + .. _contribution checklist: From 0005bcb2f013082ae13f3a466a8b3a9d75e7dea7 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Fri, 23 May 2014 10:24:55 +0400 Subject: [PATCH 2/7] Make IDLE mode to work again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactoring in commit 6cbd2498 touched wrong class's "idle" call. Found-by: Tomasz Żok Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 8 ++++++++ offlineimap/imapserver.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Changelog.rst b/Changelog.rst index b45d430..a05dec2 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,14 @@ ChangeLog OfflineIMAP v6.5.6 (YYYY-MM-DD) =============================== +* Fix IDLE mode regression (it didn't worked) introduced + after v6.5.5 (pointy hat goes to Eygene Ryabinkin, kudos -- + to Tomasz Żok) + + +OfflineIMAP v6.5.6-RC1 (2014-05-14) +=================================== + * Add knob to invoke folderfilter dynamically on each sync (GitHub#73) * Add knob to apply compression to IMAP connections (Abdó Roig-Maranges) * Add knob to filter some headers before uploading message diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 676dc31..844012e 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -703,7 +703,7 @@ class IdleThread(object): else: success = True if "IDLE" in imapobj.capabilities: - imapobj.__idle(callback=callback) + imapobj.idle(callback=callback) else: self.ui.warn("IMAP IDLE not supported on server '%s'." "Sleep until next refresh cycle." % imapobj.identifier) From 7770b5ff73737d1269eb1ba7554b8d3486c7f5ec Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Fri, 23 May 2014 21:50:46 +0400 Subject: [PATCH 3/7] Add friendly message about Python 3.x being unsupported A bit better than a traceback. Also introduce environment variable DEVELOPING_OFFLINEIMAP_PYTHON3_SUPPORT that will allow developers to grok the code under Python 3.x. Signed-off-by: Eygene Ryabinkin --- offlineimap.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/offlineimap.py b/offlineimap.py index ce1c70a..05da925 100755 --- a/offlineimap.py +++ b/offlineimap.py @@ -17,6 +17,19 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +import os +import sys + +if not 'DEVELOPING_OFFLINEIMAP_PYTHON3_SUPPORT' in os.environ: + if sys.version_info[0] > 2: + sys.stderr.write("""IIMAPS! + +Sorry, OfflineIMAP currently doesn't support Python higher than 2.x. +We're doing our best to bring in support for 3.x really soon. You can +also join us at https://github.com/OfflineIMAP/offlineimap/ and help. +""") + sys.exit(1) + from offlineimap import OfflineImap oi = OfflineImap() From e8db1217d43010173adbf937a3a46e956f5abcfd Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 1 Jun 2014 22:09:44 +0400 Subject: [PATCH 4/7] Fix improper header separator for X-OfflineIMAP header For servers without UIDPLUS we are inserting additional header just after transformation '\n' -> CRLF was done. addmessageheaders() was written to work with just '\n' as the separator, so X-OfflineIMAP header wasn't preceeded by the CRLF, but just by '\n'. Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 10 +++++++++- offlineimap/folder/Base.py | 22 ++++++++++++++++------ offlineimap/folder/Gmail.py | 2 +- offlineimap/folder/GmailMaildir.py | 2 +- offlineimap/folder/IMAP.py | 3 ++- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index a05dec2..4a4db93 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -5,7 +5,15 @@ ChangeLog :website: http://offlineimap.org -OfflineIMAP v6.5.6 (YYYY-MM-DD) +OfflineIMAP v6.5.6.1 (YYYY-MM-DD) +================================= + +* Fix mangled message headers for servers without UIDPLUS: + X-OfflineIMAP was added with preceeding '\n' instead of + '\r\n' just before message was uploaded to the IMAP server. + + +OfflineIMAP v6.5.6 (2014-05-14) =============================== * Fix IDLE mode regression (it didn't worked) introduced diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 43e79ff..bca52a2 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -413,16 +413,26 @@ next line\n """ - def addmessageheader(self, content, headername, headervalue): + def addmessageheader(self, content, crlf, headername, headervalue): + """ + Adds new header to the provided message. + + Arguments: + - content: message content, headers and body as a single string + - crlf: string that carries line ending + - headername: name of the header to add + - headervalue: value of the header to add + + """ self.ui.debug('', 'addmessageheader: called to add %s: %s' % (headername, headervalue)) - prefix = '\n' + prefix = crlf suffix = '' - insertionpoint = content.find('\n\n') + insertionpoint = content.find(crlf + crlf) if insertionpoint == 0 or insertionpoint == -1: prefix = '' - suffix = '\n' + suffix = crlf if insertionpoint == -1: insertionpoint = 0 # When body starts immediately, without preceding '\n' @@ -430,8 +440,8 @@ next line\n # we seen many broken ones), we should add '\n' to make # new (and the only header, in this case) to be properly # separated from the message body. - if content[0] != '\n': - suffix = suffix + '\n' + if content[0:len(crlf)] != crlf: + suffix = suffix + crlf self.ui.debug('', 'addmessageheader: insertionpoint = %d' % insertionpoint) headers = content[0:insertionpoint] diff --git a/offlineimap/folder/Gmail.py b/offlineimap/folder/Gmail.py index f051938..2a598de 100644 --- a/offlineimap/folder/Gmail.py +++ b/offlineimap/folder/Gmail.py @@ -93,7 +93,7 @@ class GmailFolder(IMAPFolder): labels = set() labels = labels - self.ignorelabels labels_str = imaputil.format_labels_string(self.labelsheader, sorted(labels)) - body = self.addmessageheader(body, self.labelsheader, labels_str) + body = self.addmessageheader(body, '\n', self.labelsheader, labels_str) if len(body)>200: dbg_output = "%s...%s" % (str(body)[:150], str(body)[-50:]) diff --git a/offlineimap/folder/GmailMaildir.py b/offlineimap/folder/GmailMaildir.py index 3f37b02..be0d20d 100644 --- a/offlineimap/folder/GmailMaildir.py +++ b/offlineimap/folder/GmailMaildir.py @@ -141,7 +141,7 @@ class GmailMaildirFolder(MaildirFolder): # Change labels into content labels_str = imaputil.format_labels_string(self.labelsheader, sorted(labels | ignoredlabels)) - content = self.addmessageheader(content, self.labelsheader, labels_str) + content = self.addmessageheader(content, '\n', self.labelsheader, labels_str) rtime = self.messagelist[uid].get('rtime', None) # write file with new labels to a unique file in tmp diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index 4801eef..3b506e5 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -526,6 +526,7 @@ class IMAPFolder(BaseFolder): # NB: imapobj to None. try: while retry_left: + # XXX: we can mangle message only once, out of the loop # UIDPLUS extension provides us with an APPENDUID response. use_uidplus = 'UIDPLUS' in imapobj.capabilities @@ -535,7 +536,7 @@ class IMAPFolder(BaseFolder): content) self.ui.debug('imap', 'savemessage: header is: %s: %s' %\ (headername, headervalue)) - content = self.addmessageheader(content, headername, headervalue) + content = self.addmessageheader(content, CRLF, headername, headervalue) if len(content)>200: dbg_output = "%s...%s" % (content[:150], content[-50:]) From c92c4e56a092f271d71daedf796461945e682dee Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 1 Jun 2014 22:32:57 +0400 Subject: [PATCH 5/7] Add version qualifier to differentiate releases and development ones It is always good to see which version we're talking about, so I had added explicit marker for -devel, -release, -rcX and other states of the OfflineIMAP. Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 3 +++ offlineimap/__init__.py | 6 ++++-- offlineimap/init.py | 2 +- offlineimap/ui/Curses.py | 2 +- offlineimap/ui/UIBase.py | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 4a4db93..27a31a5 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -12,6 +12,9 @@ OfflineIMAP v6.5.6.1 (YYYY-MM-DD) X-OfflineIMAP was added with preceeding '\n' instead of '\r\n' just before message was uploaded to the IMAP server. +* Add missing version bump for 6.5.6 (it was released with + 6.5.5 in setup.py and other places). + OfflineIMAP v6.5.6 (2014-05-14) =============================== diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py index 60c79a3..1df5d2c 100644 --- a/offlineimap/__init__.py +++ b/offlineimap/__init__.py @@ -1,13 +1,15 @@ __all__ = ['OfflineImap'] __productname__ = 'OfflineIMAP' -__version__ = "6.5.5" +__version__ = "6.5.6.1" +__revision__ = "-devel" +__bigversion__ = __version__ + __revision__ __copyright__ = "Copyright 2002-2013 John Goerzen & contributors" __author__ = "John Goerzen" __author_email__= "john@complete.org" __description__ = "Disconnected Universal IMAP Mail Synchronization/Reader Support" __license__ = "Licensed under the GNU GPL v2+ (v2 or any later version)" -__bigcopyright__ = """%(__productname__)s %(__version__)s +__bigcopyright__ = """%(__productname__)s %(__bigversion__)s %(__license__)s""" % locals() __homepage__ = "http://offlineimap.org" diff --git a/offlineimap/init.py b/offlineimap/init.py index 9193bd8..5bb0438 100644 --- a/offlineimap/init.py +++ b/offlineimap/init.py @@ -50,7 +50,7 @@ class OfflineImap: self.__sync(options) def __parse_cmd_options(self): - parser = OptionParser(version=offlineimap.__version__, + parser = OptionParser(version=offlineimap.__bigversion__, description="%s.\n\n%s" % (offlineimap.__copyright__, offlineimap.__license__)) diff --git a/offlineimap/ui/Curses.py b/offlineimap/ui/Curses.py index 1c378ab..4150066 100644 --- a/offlineimap/ui/Curses.py +++ b/offlineimap/ui/Curses.py @@ -576,7 +576,7 @@ class Blinkenlights(UIBase, CursesUtil): self.bannerwin.clear() # Delete old content (eg before resizes) self.bannerwin.bkgd(' ', color) # Fill background with that color string = "%s %s" % (offlineimap.__productname__, - offlineimap.__version__) + offlineimap.__bigversion__) self.bannerwin.addstr(0, 0, string, color) self.bannerwin.addstr(0, self.width -len(offlineimap.__copyright__) -1, offlineimap.__copyright__, color) diff --git a/offlineimap/ui/UIBase.py b/offlineimap/ui/UIBase.py index 4dfd092..dace655 100644 --- a/offlineimap/ui/UIBase.py +++ b/offlineimap/ui/UIBase.py @@ -95,7 +95,7 @@ class UIBase(object): # write out more verbose initial info blurb on the log file p_ver = ".".join([str(x) for x in sys.version_info[0:3]]) msg = "OfflineImap %s starting...\n Python: %s Platform: %s\n "\ - "Args: %s" % (offlineimap.__version__, p_ver, sys.platform, + "Args: %s" % (offlineimap.__bigversion__, p_ver, sys.platform, " ".join(sys.argv)) self.logger.info(msg) @@ -409,7 +409,7 @@ class UIBase(object): #TODO: Debug and make below working, it hangs Gmail #res_type, response = conn.id(( # 'name', offlineimap.__productname__, - # 'version', offlineimap.__version__)) + # 'version', offlineimap.__bigversion__)) #self._msg("Server ID: %s %s" % (res_type, response[0])) self._msg("Server welcome string: %s" % str(conn.welcome)) self._msg("Server capabilities: %s\n" % str(conn.capabilities)) From 968ffc20bd0f1b3e9bbc0a0df15eab6f2acf21f8 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 24 Jun 2014 18:48:58 +0400 Subject: [PATCH 6/7] More clearly show results of folder name translation For cases like http://article.gmane.org/gmane.mail.imap.offlineimap.general/6468 it is beneficial to see that folder name was translated and the result of this translation on a single line: having log like {{{ Folder Boring/Wreck [acc: tmarble@info9.net]: Syncing Boring/Breck: Gmail -> Maildir }}} with translated name on the "Folder" line and original one on the "Syncing" line isn't very intuitive. Signed-off-by: Eygene Ryabinkin --- offlineimap/accounts.py | 2 +- offlineimap/folder/Base.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index 2c9551d..f8a2281 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -326,7 +326,7 @@ class SyncableAccount(Account): thread = InstanceLimitedThread(\ instancename = 'FOLDER_' + self.remoterepos.getname(), target = syncfolder, - name = "Folder %s [acc: %s]" % (remotefolder, self), + name = "Folder %s [acc: %s]" % (remotefolder.getexplainedname(), self), args = (self, remotefolder, quick)) thread.start() folderthreads.append(thread) diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index bca52a2..a80a6e0 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -132,6 +132,13 @@ class BaseFolder(object): """The nametrans-transposed name of the folder's name""" return self.visiblename + def getexplainedname(self): + """ Name that shows both real and nametrans-mangled values""" + if self.name == self.visiblename: + return self.name + else: + return "%s [remote name %s]" % (self.visiblename, self.name) + def getrepository(self): """Returns the repository object that this folder is within.""" return self.repository From f282f0b0e0404fd156589e2a83e03bb67102cfb2 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Tue, 24 Jun 2014 16:32:49 +0200 Subject: [PATCH 7/7] Merge pull request #67 from danmilon/fix-docs-folderfilter-typo docs: fix folderfilter typo --- docs/doc-src/nametrans.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/doc-src/nametrans.rst b/docs/doc-src/nametrans.rst index c211072..f6b9924 100644 --- a/docs/doc-src/nametrans.rst +++ b/docs/doc-src/nametrans.rst @@ -13,7 +13,7 @@ safely skip this section. folderfilter ------------ -If you do not want to synchronize all your filters, you can specify a `folderfilter`_ function that determines which folders to include in a sync and which to exclude. Typically, you would set a folderfilter option on the remote repository only, and it would be a lambda or any other python function. +If you do not want to synchronize all your folders, you can specify a `folderfilter`_ function that determines which folders to include in a sync and which to exclude. Typically, you would set a folderfilter option on the remote repository only, and it would be a lambda or any other python function. The only parameter to that function is the folder name. If the filter function returns True, the folder will be synced, if it returns False, @@ -114,7 +114,7 @@ Reverse nametrans Since 6.4.0, OfflineImap supports the creation of folders on the remote repository and that complicates things. Previously, only one nametrans setting on the remote repository was needed and that transformed a remote to a local name. However, nametrans transformations are one-way, and OfflineImap has no way using those rules on the remote repository to back local names to remote names. -Take a remote nametrans rule `lambda f: re.sub('^INBOX/','',f)` which cuts of any existing INBOX prefix. Now, if we parse a list of local folders, finding e.g. a folder "Sent", is it supposed to map to "INBOX/Sent" or to "Sent"? We have no way of knowing. This is why **every nametrans setting on a remote repository requires an equivalent nametrans rule on the local repository that reverses the transformation**. +Take a remote nametrans rule `lambda f: re.sub('^INBOX/','',f)` which cuts off any existing INBOX prefix. Now, if we parse a list of local folders, finding e.g. a folder "Sent", is it supposed to map to "INBOX/Sent" or to "Sent"? We have no way of knowing. This is why **every nametrans setting on a remote repository requires an equivalent nametrans rule on the local repository that reverses the transformation**. Take the above examples. If your remote nametrans setting was:: @@ -122,7 +122,7 @@ Take the above examples. If your remote nametrans setting was:: then you will want to have this in your local repository, prepending "INBOX" to any local folder name:: - nametrans = lambda folder: 'INBOX' + folder + nametrans = lambda folder: 'INBOX.' + folder Failure to set the local nametrans rule will lead to weird-looking error messages of -for instance- this type::