From 5391476dfbc3ec519302063019016c18ece552c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abd=C3=B3=20Roig-Maranges?= Date: Tue, 16 Oct 2012 20:53:54 +0200 Subject: [PATCH] Add ability to trim some local mail headers When filterheaders is set to a comma-separated list of headers, OfflineIMAP removes those headers from messages before uploading them to the server. Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 2 ++ offlineimap.conf | 11 ++++++++ offlineimap/folder/IMAP.py | 55 +++++++++++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index cb60d5e..0d3dca9 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -10,6 +10,8 @@ OfflineIMAP v6.5.6 (YYYY-MM-DD) * 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 + to IMAP server (Abdó Roig-Maranges) OfflineIMAP v6.5.5 (2013-10-07) diff --git a/offlineimap.conf b/offlineimap.conf index f3cd479..333e52e 100644 --- a/offlineimap.conf +++ b/offlineimap.conf @@ -272,6 +272,17 @@ remoterepository = RemoteExample #maildir-windows-compatible = no +# OfflineIMAP can strip off some headers when your messages are propagated +# back to the IMAP server. This option carries the comma-separated list +# of headers to trim off. Header name matching is case-sensitive. +# +# This knob is respected only by IMAP-based accounts. Value of labelsheader +# for GMail-based accounts is automatically added to this list, you don't +# need to specify it explicitely. +# +#filterheaders = X-Some-Weird-Header + + [Repository LocalExample] # Each repository requires a "type" declaration. The types supported for diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index b7c0369..bab80f4 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -26,6 +26,10 @@ from offlineimap import globals from offlineimap.imaplib2 import MonthNames +# Globals +CRLF = '\r\n' + + class IMAPFolder(BaseFolder): def __init__(self, imapserver, name, repository): name = imaputil.dequote(name) @@ -38,6 +42,10 @@ class IMAPFolder(BaseFolder): self.randomgenerator = random.Random() #self.ui is set in BaseFolder + fh_conf = self.repository.account.getconf('filterheaders', '') + self.filterheaders = [h for h in re.split(r'\s*,\s*', fh_conf) if h] + + def __selectro(self, imapobj, force = False): """Select this folder when we do not need write access. @@ -248,7 +256,7 @@ class IMAPFolder(BaseFolder): # data looks now e.g. [('320 (UID 17061 BODY[] # {2565}','msgbody....')] we only asked for one message, # and that msg is in data[0]. msbody is in [0][1] - data = data[0][1].replace("\r\n", "\n") + data = data[0][1].replace(CRLF, "\n") if len(data)>200: dbg_output = "%s...%s" % (str(data)[:150], @@ -300,7 +308,7 @@ class IMAPFolder(BaseFolder): self.ui.debug('imap', '__savemessage_addheader: called to add %s: %s' % (headername, headervalue)) - insertionpoint = content.find("\r\n\r\n") + insertionpoint = content.find(CRLF + CRLF) self.ui.debug('imap', '__savemessage_addheader: insertionpoint = %d' % insertionpoint) leader = content[0:insertionpoint] self.ui.debug('imap', '__savemessage_addheader: leader = %s' % repr(leader)) @@ -308,7 +316,7 @@ class IMAPFolder(BaseFolder): newline = '' insertionpoint = 0 else: - newline = "\r\n" + newline = CRLF newline += "%s: %s" % (headername, headervalue) self.ui.debug('imap', '__savemessage_addheader: newline = ' + repr(newline)) trailer = content[insertionpoint:] @@ -316,6 +324,43 @@ class IMAPFolder(BaseFolder): return leader + newline + trailer + def __savemessage_delheaders(self, content, header_list): + """ + Deletes headers in the given list from the message content. + + Arguments: + - content: message itself + - header_list: list of headers to be deleted or just the header name + + We expect our message to have proper CRLF as line endings. + + """ + if type(header_list) != type([]): + header_list = [header_list] + self.ui.debug('imap', + '__savemessage_delheaders: called to delete %s' % (header_list)) + + if not len(header_list): return content + + eoh = content.find(CRLF + CRLF) + if eoh == -1: + eoh = len(content) + self.ui.debug('imap', '__savemessage_delheaders: end of headers = %d' % eoh) + headers = content[0:eoh] + rest = content[eoh:] + self.ui.debug('imap', '__savemessage_delheaders: headers = %s' % repr(headers)) + new_headers = [] + for h in headers.split(CRLF): + keep_it = True + for trim_h in self.filterheaders: + if len(h) > len(trim_h) and h[0:len(trim_h)+1] == (trim_h + ":"): + keep_it = False + break + if keep_it: new_headers.append(h) + + return (CRLF.join(new_headers) + rest) + + def __savemessage_searchforheader(self, imapobj, headername, headervalue): self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s' % \ (headername, headervalue)) @@ -510,11 +555,13 @@ class IMAPFolder(BaseFolder): return uid # Use proper CRLF all over the message - content = re.sub("(?