learn to not download UIDs defined by the user
Allow users to workaround offending emails that offlineimap can't download. Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
f0096391fc
commit
29e06a60f9
@ -1187,6 +1187,17 @@ remoteuser = u"username"
|
|||||||
#"cvlc --play-and-stop --play-and-exit /path/to/sound/file.mp3 > /dev/null 2>&1")
|
#"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]
|
[Repository GmailExample]
|
||||||
|
|
||||||
# A repository using Gmail's IMAP interface.
|
# A repository using Gmail's IMAP interface.
|
||||||
|
@ -45,6 +45,7 @@ class BaseFolder(object):
|
|||||||
if self.name == 'INBOX':
|
if self.name == 'INBOX':
|
||||||
self.newmail_hook = repository.newmail_hook
|
self.newmail_hook = repository.newmail_hook
|
||||||
self.have_newmail = False
|
self.have_newmail = False
|
||||||
|
self.copy_ignoreUIDs = None # List of UIDs to ignore.
|
||||||
self.repository = repository
|
self.repository = repository
|
||||||
self.visiblename = repository.nametrans(name)
|
self.visiblename = repository.nametrans(name)
|
||||||
# In case the visiblename becomes '.' or '/' (top-level) we use
|
# In case the visiblename becomes '.' or '/' (top-level) we use
|
||||||
@ -870,6 +871,13 @@ class BaseFolder(object):
|
|||||||
if not statusfolder.uidexists(uid)]
|
if not statusfolder.uidexists(uid)]
|
||||||
num_to_copy = len(copylist)
|
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:
|
if num_to_copy > 0 and self.repository.account.dryrun:
|
||||||
self.ui.info(
|
self.ui.info(
|
||||||
"[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
|
"[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
|
||||||
|
@ -58,6 +58,10 @@ class IMAPFolder(BaseFolder):
|
|||||||
fh_conf = self.repository.account.getconf('filterheaders', '')
|
fh_conf = self.repository.account.getconf('filterheaders', '')
|
||||||
self.filterheaders = [h for h in re.split(r'\s*,\s*', fh_conf) if h]
|
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):
|
def __selectro(self, imapobj, force=False):
|
||||||
"""Select this folder when we do not need write access.
|
"""Select this folder when we do not need write access.
|
||||||
|
@ -40,6 +40,8 @@ class IMAPRepository(BaseRepository):
|
|||||||
self._oauth2_request_url = None
|
self._oauth2_request_url = None
|
||||||
self.imapserver = imapserver.IMAPServer(self)
|
self.imapserver = imapserver.IMAPServer(self)
|
||||||
self.folders = None
|
self.folders = None
|
||||||
|
self.copy_ignore_eval = None
|
||||||
|
|
||||||
# Only set the newmail_hook in an IMAP repository.
|
# Only set the newmail_hook in an IMAP repository.
|
||||||
if self.config.has_option(self.getsection(), 'newmail_hook'):
|
if self.config.has_option(self.getsection(), 'newmail_hook'):
|
||||||
self.newmail_hook = self.localeval.eval(
|
self.newmail_hook = self.localeval.eval(
|
||||||
@ -75,6 +77,19 @@ class IMAPRepository(BaseRepository):
|
|||||||
def dropconnections(self):
|
def dropconnections(self):
|
||||||
self.imapserver.close()
|
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):
|
def getholdconnectionopen(self):
|
||||||
if self.getidlefolders():
|
if self.getidlefolders():
|
||||||
return 1
|
return 1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Curses-based interfaces
|
# 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
|
# 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
|
# 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')
|
self.gettf().setcolor('blue')
|
||||||
super(Blinkenlights, self).syncingmessages(*args)
|
super(Blinkenlights, self).syncingmessages(*args)
|
||||||
|
|
||||||
|
def ignorecopyingmessage(self, *args):
|
||||||
|
self.gettf().setcolor('red')
|
||||||
|
super(Blinkenlights, self).ignorecopyingmessage(*args)
|
||||||
|
|
||||||
def copyingmessage(self, *args):
|
def copyingmessage(self, *args):
|
||||||
self.gettf().setcolor('orange')
|
self.gettf().setcolor('orange')
|
||||||
super(Blinkenlights, self).copyingmessage(*args)
|
super(Blinkenlights, self).copyingmessage(*args)
|
||||||
|
@ -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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -21,10 +21,11 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from threading import currentThread
|
from threading import currentThread
|
||||||
from offlineimap.ui.UIBase import UIBase
|
|
||||||
import offlineimap
|
|
||||||
|
|
||||||
protocol = '7.0.0'
|
import offlineimap
|
||||||
|
from offlineimap.ui.UIBase import UIBase
|
||||||
|
|
||||||
|
protocol = '7.1.0'
|
||||||
|
|
||||||
class MachineLogFormatter(logging.Formatter):
|
class MachineLogFormatter(logging.Formatter):
|
||||||
"""urlencodes any outputted line, to avoid multi-line output"""
|
"""urlencodes any outputted line, to avoid multi-line output"""
|
||||||
@ -125,6 +126,11 @@ class MachineUI(UIBase):
|
|||||||
(s.getnicename(sr), sf.getname(), s.getnicename(dr),
|
(s.getnicename(sr), sf.getname(), s.getnicename(dr),
|
||||||
df.getname()))
|
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):
|
def copyingmessage(s, uid, num, num_to_copy, srcfolder, destfolder):
|
||||||
s._printData(s.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]"%
|
s._printData(s.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]"%
|
||||||
(uid, s.getnicename(srcfolder), srcfolder.getname(),
|
(uid, s.getnicename(srcfolder), srcfolder.getname(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Noninteractive UI
|
# 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
|
# 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
|
# 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
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from offlineimap.ui.UIBase import UIBase
|
|
||||||
import offlineimap
|
import offlineimap
|
||||||
|
from offlineimap.ui.UIBase import UIBase
|
||||||
|
|
||||||
class Basic(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):
|
def __init__(self, config, loglevel = logging.INFO):
|
||||||
return super(Basic, self).__init__(config, loglevel)
|
return super(Basic, self).__init__(config, loglevel)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# UI base class
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -27,8 +27,9 @@ try:
|
|||||||
except ImportError: # python3
|
except ImportError: # python3
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from offlineimap.error import OfflineImapError
|
|
||||||
import offlineimap
|
import offlineimap
|
||||||
|
from offlineimap.error import OfflineImapError
|
||||||
|
|
||||||
debugtypes = {'':'Other offlineimap related sync messages',
|
debugtypes = {'':'Other offlineimap related sync messages',
|
||||||
'imap': 'IMAP protocol debugging',
|
'imap': 'IMAP protocol debugging',
|
||||||
@ -385,6 +386,12 @@ class UIBase(object):
|
|||||||
self.getnicename(sr), srcfolder,
|
self.getnicename(sr), srcfolder,
|
||||||
self.getnicename(dr), dstfolder))
|
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):
|
def copyingmessage(self, uid, num, num_to_copy, src, destfolder):
|
||||||
"""Output a log line stating which message we copy."""
|
"""Output a log line stating which message we copy."""
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user