[PATCH 4/4] learn to not download UIDs defined by the user
Nicolas Sebrecht
nicolas.s-dev at laposte.net
Tue Jun 28 23:04:47 BST 2016
Allow users to workaround offending emails that offlineimap can't download.
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev at laposte.net>
---
offlineimap.conf | 11 +++++++++++
offlineimap/folder/Base.py | 8 ++++++++
offlineimap/folder/IMAP.py | 4 ++++
offlineimap/repository/IMAP.py | 15 +++++++++++++++
offlineimap/ui/Curses.py | 6 +++++-
offlineimap/ui/Machine.py | 12 +++++++++---
offlineimap/ui/Noninteractive.py | 8 +++++---
offlineimap/ui/UIBase.py | 13 ++++++++++---
8 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/offlineimap.conf b/offlineimap.conf
index e2e6115..14d14df 100644
--- a/offlineimap.conf
+++ b/offlineimap.conf
@@ -1187,6 +1187,17 @@ remoteuser = u"username"
#"cvlc --play-and-stop --play-and-exit /path/to/sound/file.mp3 > /dev/null 2>&1")
+# This option stands in the [Repository RemoteExample] section.
+#
+# If offlineiamp is having troubles to download some UIDS, it's possible to get
+# them ignored in a list.
+#
+# The function must return the list of UIDs to ignore, None otherwise. It is
+# passed the folder name.
+#
+#copy_ignore_eval = lambda foldername: {'INBOX': [2, 3, 4]}.get(foldername)
+
+
[Repository GmailExample]
# A repository using Gmail's IMAP interface.
diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py
index 98f78f8..e467f23 100644
--- a/offlineimap/folder/Base.py
+++ b/offlineimap/folder/Base.py
@@ -45,6 +45,7 @@ class BaseFolder(object):
if self.name == 'INBOX':
self.newmail_hook = repository.newmail_hook
self.have_newmail = False
+ self.copy_ignoreUIDs = None # List of UIDs to ignore.
self.repository = repository
self.visiblename = repository.nametrans(name)
# In case the visiblename becomes '.' or '/' (top-level) we use
@@ -870,6 +871,13 @@ class BaseFolder(object):
if not statusfolder.uidexists(uid)]
num_to_copy = len(copylist)
+ # Honor 'copy_ignore_eval' configuration option.
+ if self.copy_ignoreUIDs is not None:
+ for uid in self.copy_ignoreUIDs:
+ if uid in copylist:
+ copylist.remove(uid)
+ self.ui.ignorecopyingmessage(uid, self, dstfolder)
+
if num_to_copy > 0 and self.repository.account.dryrun:
self.ui.info(
"[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index 917c17f..a8cd1d2 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -58,6 +58,10 @@ class IMAPFolder(BaseFolder):
fh_conf = self.repository.account.getconf('filterheaders', '')
self.filterheaders = [h for h in re.split(r'\s*,\s*', fh_conf) if h]
+ # self.copy_ignoreUIDs is used by BaseFolder.
+ self.copy_ignoreUIDs = repository.get_copy_ignore_UIDs(
+ self.getvisiblename())
+
def __selectro(self, imapobj, force=False):
"""Select this folder when we do not need write access.
diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py
index fdd9955..7ed9301 100644
--- a/offlineimap/repository/IMAP.py
+++ b/offlineimap/repository/IMAP.py
@@ -40,6 +40,8 @@ class IMAPRepository(BaseRepository):
self._oauth2_request_url = None
self.imapserver = imapserver.IMAPServer(self)
self.folders = None
+ self.copy_ignore_eval = None
+
# Only set the newmail_hook in an IMAP repository.
if self.config.has_option(self.getsection(), 'newmail_hook'):
self.newmail_hook = self.localeval.eval(
@@ -75,6 +77,19 @@ class IMAPRepository(BaseRepository):
def dropconnections(self):
self.imapserver.close()
+ def get_copy_ignore_UIDs(self, foldername):
+ """Return a list of UIDs to not copy for this foldername."""
+
+ if self.copy_ignore_eval is None:
+ if self.config.has_option(self.getsection(),
+ 'copy_ignore_eval'):
+ self.copy_ignore_eval = self.localeval.eval(
+ self.getconf('copy_ignore_eval'))
+ else:
+ self.copy_ignore_eval = lambda x: None
+
+ return self.copy_ignore_eval(foldername)
+
def getholdconnectionopen(self):
if self.getidlefolders():
return 1
diff --git a/offlineimap/ui/Curses.py b/offlineimap/ui/Curses.py
index d5b148d..dfefb7d 100644
--- a/offlineimap/ui/Curses.py
+++ b/offlineimap/ui/Curses.py
@@ -1,5 +1,5 @@
# Curses-based interfaces
-# Copyright (C) 2003-2015 John Goerzen & contributors
+# Copyright (C) 2003-2016 John Goerzen & contributors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -442,6 +442,10 @@ class Blinkenlights(UIBase, CursesUtil):
self.gettf().setcolor('blue')
super(Blinkenlights, self).syncingmessages(*args)
+ def ignorecopyingmessage(self, *args):
+ self.gettf().setcolor('red')
+ super(Blinkenlights, self).ignorecopyingmessage(*args)
+
def copyingmessage(self, *args):
self.gettf().setcolor('orange')
super(Blinkenlights, self).copyingmessage(*args)
diff --git a/offlineimap/ui/Machine.py b/offlineimap/ui/Machine.py
index dc650c3..06de17b 100644
--- a/offlineimap/ui/Machine.py
+++ b/offlineimap/ui/Machine.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2015 John Goerzen & contributors
+# Copyright (C) 2007-2016 John Goerzen & contributors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,10 +21,11 @@ import sys
import time
import logging
from threading import currentThread
-from offlineimap.ui.UIBase import UIBase
+
import offlineimap
+from offlineimap.ui.UIBase import UIBase
-protocol = '7.0.0'
+protocol = '7.1.0'
class MachineLogFormatter(logging.Formatter):
"""urlencodes any outputted line, to avoid multi-line output"""
@@ -125,6 +126,11 @@ class MachineUI(UIBase):
(s.getnicename(sr), sf.getname(), s.getnicename(dr),
df.getname()))
+ def ignorecopyingmessage(s, uid, srcfolder, destfolder):
+ s._printData(s.logger.info, 'ignorecopyingmessage', "%d\n%s\n%s\n%s[%s]"%
+ (uid, s.getnicename(srcfolder), srcfolder.getname(),
+ s.getnicename(destfolder), destfolder))
+
def copyingmessage(s, uid, num, num_to_copy, srcfolder, destfolder):
s._printData(s.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]"%
(uid, s.getnicename(srcfolder), srcfolder.getname(),
diff --git a/offlineimap/ui/Noninteractive.py b/offlineimap/ui/Noninteractive.py
index 0e23a93..e7ef096 100644
--- a/offlineimap/ui/Noninteractive.py
+++ b/offlineimap/ui/Noninteractive.py
@@ -1,5 +1,5 @@
# Noninteractive UI
-# Copyright (C) 2002-2012 John Goerzen & contributors
+# Copyright (C) 2002-2016 John Goerzen & contributors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,11 +16,13 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
-from offlineimap.ui.UIBase import UIBase
+
import offlineimap
+from offlineimap.ui.UIBase import UIBase
class Basic(UIBase):
- """'Basic' simply sets log level to INFO"""
+ """'Basic' simply sets log level to INFO."""
+
def __init__(self, config, loglevel = logging.INFO):
return super(Basic, self).__init__(config, loglevel)
diff --git a/offlineimap/ui/UIBase.py b/offlineimap/ui/UIBase.py
index 769a008..2aa7ca8 100644
--- a/offlineimap/ui/UIBase.py
+++ b/offlineimap/ui/UIBase.py
@@ -1,5 +1,5 @@
# UI base class
-# Copyright (C) 2002-2016 John Goerzen & contributors
+# Copyright (C) 2002-2016 John Goerzen & contributors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,11 +24,12 @@ import traceback
import threading
try:
from Queue import Queue
-except ImportError: #python3
+except ImportError: # python3
from queue import Queue
from collections import deque
-from offlineimap.error import OfflineImapError
+
import offlineimap
+from offlineimap.error import OfflineImapError
debugtypes = {'':'Other offlineimap related sync messages',
'imap': 'IMAP protocol debugging',
@@ -385,6 +386,12 @@ class UIBase(object):
self.getnicename(sr), srcfolder,
self.getnicename(dr), dstfolder))
+ def ignorecopyingmessage(self, uid, src, destfolder):
+ """Output a log line stating which message is ignored."""
+
+ self.logger.info("IGNORED: Copy message UID %s %s:%s -> %s"% (
+ uid, src.repository, src, destfolder.repository))
+
def copyingmessage(self, uid, num, num_to_copy, src, destfolder):
"""Output a log line stating which message we copy."""
--
2.7.4
More information about the OfflineIMAP-project
mailing list