more style consistency
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
594a286888
commit
0f40ca4799
@ -385,6 +385,7 @@ def syncfolder(account, remotefolder, quick):
|
||||
"""Synchronizes given remote folder for the specified account.
|
||||
|
||||
Filtered folders on the remote side will not invoke this function."""
|
||||
|
||||
remoterepos = account.remoterepos
|
||||
localrepos = account.localrepos
|
||||
statusrepos = account.statusrepos
|
||||
|
@ -19,13 +19,12 @@ import email
|
||||
from email.Parser import Parser as MailParser
|
||||
|
||||
def get_message_date(content, header='Date'):
|
||||
"""
|
||||
Parses mail and returns resulting timestamp.
|
||||
"""Parses mail and returns resulting timestamp.
|
||||
|
||||
:param header: the header to extract date from;
|
||||
:returns: timestamp or `None` in the case of failure.
|
||||
|
||||
"""
|
||||
|
||||
message = MailParser().parsestr(content, True)
|
||||
dateheader = message.get(header)
|
||||
# parsedate_tz returns a 10-tuple that can be passed to mktime_tz
|
||||
|
@ -15,14 +15,15 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os.path
|
||||
import re
|
||||
from sys import exc_info
|
||||
|
||||
from offlineimap import threadutil, emailutil
|
||||
from offlineimap import globals
|
||||
from offlineimap.ui import getglobalui
|
||||
from offlineimap.error import OfflineImapError
|
||||
import offlineimap.accounts
|
||||
import os.path
|
||||
import re
|
||||
from sys import exc_info
|
||||
|
||||
|
||||
class BaseFolder(object):
|
||||
@ -31,6 +32,7 @@ class BaseFolder(object):
|
||||
:para name: Path & name of folder minus root or reference
|
||||
:para repository: Repository() in which the folder is.
|
||||
"""
|
||||
|
||||
self.ui = getglobalui()
|
||||
# Save original name for folderfilter operations
|
||||
self.ffilter_name = name
|
||||
@ -56,15 +58,15 @@ class BaseFolder(object):
|
||||
|
||||
# Determine if we're running static or dynamic folder filtering
|
||||
# and check filtering status
|
||||
self._dynamic_folderfilter = \
|
||||
self.config.getdefaultboolean(repo, "dynamic_folderfilter", False)
|
||||
self._dynamic_folderfilter = self.config.getdefaultboolean(
|
||||
repo, "dynamic_folderfilter", False)
|
||||
self._sync_this = repository.should_sync_folder(self.ffilter_name)
|
||||
if self._dynamic_folderfilter:
|
||||
self.ui.debug('', "Running dynamic folder filtering on '%s'[%s]" \
|
||||
% (self.ffilter_name, repository))
|
||||
self.ui.debug('', "Running dynamic folder filtering on '%s'[%s]"%
|
||||
(self.ffilter_name, repository))
|
||||
elif not self._sync_this:
|
||||
self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter" \
|
||||
% (self.ffilter_name, repository))
|
||||
self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter"%
|
||||
(self.ffilter_name, repository))
|
||||
|
||||
# Passes for syncmessagesto
|
||||
self.syncmessagesto_passes = [('copying messages' , self.__syncmessagesto_copy),
|
||||
@ -115,17 +117,20 @@ class BaseFolder(object):
|
||||
|
||||
:param statusfolder: keeps track of the last known folder state.
|
||||
"""
|
||||
|
||||
return True
|
||||
|
||||
def getcopyinstancelimit(self):
|
||||
"""For threading folders, returns the instancelimitname for
|
||||
InstanceLimitedThreads."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def storesmessages(self):
|
||||
"""Should be true for any backend that actually saves message bodies.
|
||||
(Almost all of them). False for the LocalStatus backend. Saves
|
||||
us from having to slurp up messages just for localstatus purposes."""
|
||||
|
||||
return 1
|
||||
|
||||
def getvisiblename(self):
|
||||
@ -143,14 +148,17 @@ class BaseFolder(object):
|
||||
|
||||
def getrepository(self):
|
||||
"""Returns the repository object that this folder is within."""
|
||||
|
||||
return self.repository
|
||||
|
||||
def getroot(self):
|
||||
"""Returns the root of the folder, in a folder-specific fashion."""
|
||||
|
||||
return self.root
|
||||
|
||||
def getsep(self):
|
||||
"""Returns the separator for this folder type."""
|
||||
|
||||
return self.sep
|
||||
|
||||
def getfullname(self):
|
||||
@ -160,7 +168,8 @@ class BaseFolder(object):
|
||||
return self.getname()
|
||||
|
||||
def getfolderbasename(self):
|
||||
"""Return base file name of file to store Status/UID info in"""
|
||||
"""Return base file name of file to store Status/UID info in."""
|
||||
|
||||
if not self.name:
|
||||
basename = '.'
|
||||
else: #avoid directory hierarchies and file names such as '/'
|
||||
@ -188,6 +197,7 @@ class BaseFolder(object):
|
||||
|
||||
def _getuidfilename(self):
|
||||
"""provides UIDVALIDITY cache filename for class internal purposes"""
|
||||
|
||||
return os.path.join(self.repository.getuiddir(),
|
||||
self.getfolderbasename())
|
||||
|
||||
@ -196,6 +206,7 @@ class BaseFolder(object):
|
||||
|
||||
:returns: UIDVALIDITY as (long) number or None, if None had been
|
||||
saved yet."""
|
||||
|
||||
if hasattr(self, '_base_saved_uidvalidity'):
|
||||
return self._base_saved_uidvalidity
|
||||
uidfilename = self._getuidfilename()
|
||||
@ -212,6 +223,7 @@ class BaseFolder(object):
|
||||
|
||||
This function is not threadsafe, so don't attempt to call it
|
||||
from concurrent threads."""
|
||||
|
||||
newval = self.get_uidvalidity()
|
||||
uidfilename = self._getuidfilename()
|
||||
|
||||
@ -225,45 +237,50 @@ class BaseFolder(object):
|
||||
|
||||
This function needs to be implemented by each Backend
|
||||
:returns: UIDVALIDITY as a (long) number"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def cachemessagelist(self):
|
||||
"""Reads the message list from disk or network and stores it in
|
||||
memory for later use. This list will not be re-read from disk or
|
||||
memory unless this function is called again."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def getmessagelist(self):
|
||||
"""Gets the current message list.
|
||||
You must call cachemessagelist() before calling this function!"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def msglist_item_initializer(self, uid):
|
||||
"""
|
||||
Returns value for empty messagelist element with given UID.
|
||||
"""Returns value for empty messagelist element with given UID.
|
||||
|
||||
This function must initialize all fields of messagelist item
|
||||
and must be called every time when one creates new messagelist
|
||||
entry to ensure that all fields that must be present are present.
|
||||
entry to ensure that all fields that must be present are present."""
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def uidexists(self, uid):
|
||||
"""Returns True if uid exists"""
|
||||
|
||||
return uid in self.getmessagelist()
|
||||
|
||||
def getmessageuidlist(self):
|
||||
"""Gets a list of UIDs.
|
||||
You may have to call cachemessagelist() before calling this function!"""
|
||||
|
||||
return self.getmessagelist().keys()
|
||||
|
||||
def getmessagecount(self):
|
||||
"""Gets the number of messages."""
|
||||
|
||||
return len(self.getmessagelist())
|
||||
|
||||
def getmessage(self, uid):
|
||||
"""Returns the content of the specified message."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def savemessage(self, uid, content, flags, rtime):
|
||||
@ -286,20 +303,23 @@ class BaseFolder(object):
|
||||
|
||||
Note that savemessage() does not check against dryrun settings,
|
||||
so you need to ensure that savemessage is never called in a
|
||||
dryrun mode.
|
||||
"""
|
||||
dryrun mode."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def getmessagetime(self, uid):
|
||||
"""Return the received time for the specified message."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def getmessagemtime(self, uid):
|
||||
"""Returns the message modification time of the specified message."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def getmessageflags(self, uid):
|
||||
"""Returns the flags for the specified message."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def savemessageflags(self, uid, flags):
|
||||
@ -308,6 +328,7 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def addmessageflags(self, uid, flags):
|
||||
@ -319,14 +340,15 @@ class BaseFolder(object):
|
||||
dryrun mode.
|
||||
|
||||
:param flags: A set() of flags"""
|
||||
|
||||
newflags = self.getmessageflags(uid) | flags
|
||||
self.savemessageflags(uid, newflags)
|
||||
|
||||
def addmessagesflags(self, uidlist, flags):
|
||||
"""
|
||||
Note that this function does not check against dryrun settings,
|
||||
"""Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
for uid in uidlist:
|
||||
self.addmessageflags(uid, flags)
|
||||
|
||||
@ -337,6 +359,7 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
newflags = self.getmessageflags(uid) - flags
|
||||
self.savemessageflags(uid, newflags)
|
||||
|
||||
@ -345,10 +368,10 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
for uid in uidlist:
|
||||
self.deletemessageflags(uid, flags)
|
||||
|
||||
|
||||
def getmessagelabels(self, uid):
|
||||
"""Returns the labels for the specified message."""
|
||||
raise NotImplementedError
|
||||
@ -359,6 +382,7 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def addmessagelabels(self, uid, labels):
|
||||
@ -370,14 +394,15 @@ class BaseFolder(object):
|
||||
dryrun mode.
|
||||
|
||||
:param labels: A set() of labels"""
|
||||
|
||||
newlabels = self.getmessagelabels(uid) | labels
|
||||
self.savemessagelabels(uid, newlabels)
|
||||
|
||||
def addmessageslabels(self, uidlist, labels):
|
||||
"""
|
||||
Note that this function does not check against dryrun settings,
|
||||
"""Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
for uid in uidlist:
|
||||
self.addmessagelabels(uid, labels)
|
||||
|
||||
@ -388,6 +413,7 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
newlabels = self.getmessagelabels(uid) - labels
|
||||
self.savemessagelabels(uid, newlabels)
|
||||
|
||||
@ -396,12 +422,12 @@ class BaseFolder(object):
|
||||
Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
for uid in uidlist:
|
||||
self.deletemessagelabels(uid, labels)
|
||||
|
||||
def addmessageheader(self, content, linebreak, headername, headervalue):
|
||||
"""
|
||||
Adds new header to the provided message.
|
||||
"""Adds new header to the provided message.
|
||||
|
||||
WARNING: This function is a bit tricky, and modifying it in the wrong way,
|
||||
may easily lead to data-loss.
|
||||
@ -454,9 +480,9 @@ class BaseFolder(object):
|
||||
This is the body\n
|
||||
next line\n
|
||||
"""
|
||||
self.ui.debug('',
|
||||
'addmessageheader: called to add %s: %s' % (headername,
|
||||
headervalue))
|
||||
|
||||
self.ui.debug('', 'addmessageheader: called to add %s: %s'%
|
||||
(headername, headervalue))
|
||||
|
||||
insertionpoint = content.find(linebreak * 2)
|
||||
if insertionpoint == -1:
|
||||
@ -499,15 +525,14 @@ class BaseFolder(object):
|
||||
|
||||
|
||||
def __find_eoh(self, content):
|
||||
"""
|
||||
Searches for the point where mail headers end.
|
||||
""" Searches for the point where mail headers end.
|
||||
Either double '\n', or end of string.
|
||||
|
||||
Arguments:
|
||||
- content: contents of the message to search in
|
||||
Returns: position of the first non-header byte.
|
||||
|
||||
"""
|
||||
|
||||
eoh_cr = content.find('\n\n')
|
||||
if eoh_cr == -1:
|
||||
eoh_cr = len(content)
|
||||
@ -516,8 +541,7 @@ class BaseFolder(object):
|
||||
|
||||
|
||||
def getmessageheader(self, content, name):
|
||||
"""
|
||||
Searches for the first occurence of the given header and returns
|
||||
"""Searches for the first occurence of the given header and returns
|
||||
its value. Header name is case-insensitive.
|
||||
|
||||
Arguments:
|
||||
@ -525,8 +549,8 @@ class BaseFolder(object):
|
||||
- name: name of the header to be searched
|
||||
|
||||
Returns: header value or None if no such header was found
|
||||
|
||||
"""
|
||||
|
||||
self.ui.debug('', 'getmessageheader: called to get %s'% name)
|
||||
eoh = self.__find_eoh(content)
|
||||
self.ui.debug('', 'getmessageheader: eoh = %d'% eoh)
|
||||
@ -541,8 +565,7 @@ class BaseFolder(object):
|
||||
|
||||
|
||||
def getmessageheaderlist(self, content, name):
|
||||
"""
|
||||
Searches for the given header and returns a list of values for
|
||||
"""Searches for the given header and returns a list of values for
|
||||
that header.
|
||||
|
||||
Arguments:
|
||||
@ -550,8 +573,8 @@ class BaseFolder(object):
|
||||
- name: name of the header to be searched
|
||||
|
||||
Returns: list of header values or emptylist if no such header was found
|
||||
|
||||
"""
|
||||
|
||||
self.ui.debug('', 'getmessageheaderlist: called to get %s' % name)
|
||||
eoh = self.__find_eoh(content)
|
||||
self.ui.debug('', 'getmessageheaderlist: eoh = %d' % eoh)
|
||||
@ -562,16 +585,15 @@ class BaseFolder(object):
|
||||
|
||||
|
||||
def deletemessageheaders(self, content, header_list):
|
||||
"""
|
||||
Deletes headers in the given list from the message content.
|
||||
"""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 '\n' as line endings.
|
||||
|
||||
"""
|
||||
|
||||
if type(header_list) != type([]):
|
||||
header_list = [header_list]
|
||||
self.ui.debug('', 'deletemessageheaders: called to delete %s'% (header_list))
|
||||
@ -609,16 +631,14 @@ class BaseFolder(object):
|
||||
raise NotImplementedError
|
||||
|
||||
def deletemessage(self, uid):
|
||||
"""
|
||||
Note that this function does not check against dryrun settings,
|
||||
"""Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def deletemessages(self, uidlist):
|
||||
"""
|
||||
Note that this function does not check against dryrun settings,
|
||||
"""Note that this function does not check against dryrun settings,
|
||||
so you need to ensure that it is never called in a
|
||||
dryrun mode."""
|
||||
|
||||
@ -686,9 +706,8 @@ class BaseFolder(object):
|
||||
self.deletemessage(uid)
|
||||
else:
|
||||
raise OfflineImapError("Trying to save msg (uid %d) on folder "
|
||||
"%s returned invalid uid %d" % (uid,
|
||||
dstfolder.getvisiblename(), new_uid),
|
||||
OfflineImapError.ERROR.MESSAGE)
|
||||
"%s returned invalid uid %d"% (uid, dstfolder.getvisiblename(),
|
||||
new_uid), OfflineImapError.ERROR.MESSAGE)
|
||||
except (KeyboardInterrupt): # bubble up CTRL-C
|
||||
raise
|
||||
except OfflineImapError as e:
|
||||
@ -697,8 +716,7 @@ class BaseFolder(object):
|
||||
self.ui.error(e, exc_info()[2])
|
||||
except Exception as e:
|
||||
self.ui.error(e, exc_info()[2],
|
||||
msg="Copying message %s [acc: %s]" %\
|
||||
(uid, self.accountname))
|
||||
msg = "Copying message %s [acc: %s]"% (uid, self.accountname))
|
||||
raise #raise on unknown errors, so we can fix those
|
||||
|
||||
def __syncmessagesto_copy(self, dstfolder, statusfolder):
|
||||
@ -714,6 +732,7 @@ class BaseFolder(object):
|
||||
|
||||
This function checks and protects us from action in ryrun mode.
|
||||
"""
|
||||
|
||||
threads = []
|
||||
|
||||
copylist = filter(lambda uid: not \
|
||||
@ -754,6 +773,7 @@ class BaseFolder(object):
|
||||
|
||||
This function checks and protects us from action in ryrun mode.
|
||||
"""
|
||||
|
||||
deletelist = filter(lambda uid: uid>=0 \
|
||||
and not self.uidexists(uid),
|
||||
statusfolder.getmessageuidlist())
|
||||
@ -776,6 +796,7 @@ class BaseFolder(object):
|
||||
|
||||
This function checks and protects us from action in ryrun mode.
|
||||
"""
|
||||
|
||||
# For each flag, we store a list of uids to which it should be
|
||||
# added. Then, we can call addmessagesflags() to apply them in
|
||||
# bulk, rather than one call per message.
|
||||
@ -854,8 +875,8 @@ class BaseFolder(object):
|
||||
|
||||
:param dstfolder: Folderinstance to sync the msgs to.
|
||||
:param statusfolder: LocalStatus instance to sync against.
|
||||
|
||||
"""
|
||||
|
||||
for (passdesc, action) in self.syncmessagesto_passes:
|
||||
# bail out on CTRL-C or SIGTERM
|
||||
if offlineimap.accounts.Account.abort_NOW_signal.is_set():
|
||||
@ -883,6 +904,7 @@ class BaseFolder(object):
|
||||
MailDirFolder('foo') == IMAPFolder('foo') --> False
|
||||
MailDirFolder('foo') == MaildirFolder('foo') --> False
|
||||
"""
|
||||
|
||||
if isinstance(other, basestring):
|
||||
return other == self.name
|
||||
return id(self) == id(other)
|
||||
|
@ -20,6 +20,7 @@ import binascii
|
||||
import re
|
||||
import time
|
||||
from sys import exc_info
|
||||
|
||||
from .Base import BaseFolder
|
||||
from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError
|
||||
from offlineimap import globals
|
||||
@ -86,6 +87,7 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
UIDVALIDITY value will be cached on the first call.
|
||||
:returns: The UIDVALIDITY as (long) number."""
|
||||
|
||||
if hasattr(self, '_uidvalidity'):
|
||||
# use cached value if existing
|
||||
return self._uidvalidity
|
||||
@ -141,8 +143,7 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
|
||||
def _msgs_to_fetch(self, imapobj):
|
||||
"""
|
||||
Determines sequence numbers of messages to be fetched.
|
||||
"""Determines sequence numbers of messages to be fetched.
|
||||
|
||||
Message sequence numbers (MSNs) are more easily compacted
|
||||
into ranges which makes transactions slightly faster.
|
||||
@ -151,9 +152,8 @@ class IMAPFolder(BaseFolder):
|
||||
- imapobj: instance of IMAPlib
|
||||
|
||||
Returns: range(s) for messages or None if no messages
|
||||
are to be fetched.
|
||||
are to be fetched."""
|
||||
|
||||
"""
|
||||
res_type, imapdata = imapobj.select(self.getfullname(), True, True)
|
||||
if imapdata == [None] or imapdata[0] == '0':
|
||||
# Empty folder, no need to populate message list
|
||||
@ -219,8 +219,8 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
# Get the flags and UIDs for these. single-quotes prevent
|
||||
# imaplib2 from quoting the sequence.
|
||||
res_type, response = imapobj.fetch("'%s'" % msgsToFetch,
|
||||
'(FLAGS UID)')
|
||||
res_type, response = imapobj.fetch("'%s'"%
|
||||
msgsToFetch, '(FLAGS UID)')
|
||||
if res_type != 'OK':
|
||||
raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "
|
||||
"Server responded '[%s] %s'"% (
|
||||
@ -255,16 +255,15 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
# Interface from BaseFolder
|
||||
def getmessage(self, uid):
|
||||
"""
|
||||
Retrieve message with UID from the IMAP server (incl body)
|
||||
"""Retrieve message with UID from the IMAP server (incl body)
|
||||
|
||||
After this function all CRLFs will be transformed to '\n'.
|
||||
|
||||
:returns: the message body or throws and OfflineImapError
|
||||
(probably severity MESSAGE) if e.g. no message with
|
||||
this UID could be found.
|
||||
|
||||
"""
|
||||
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
try:
|
||||
data = self._fetch_from_imap(imapobj, str(uid), 2)
|
||||
@ -307,6 +306,7 @@ class IMAPFolder(BaseFolder):
|
||||
headername == 'X-OfflineIMAP' and headervalue will be a
|
||||
random string
|
||||
"""
|
||||
|
||||
headername = 'X-OfflineIMAP'
|
||||
# We need a random component too. If we ever upload the same
|
||||
# mail twice (e.g. in different folders), we would still need to
|
||||
@ -368,8 +368,8 @@ class IMAPFolder(BaseFolder):
|
||||
We need to locate the UID just after mail headers containing our
|
||||
X-OfflineIMAP line.
|
||||
|
||||
Returns UID when found, 0 when not found.
|
||||
"""
|
||||
Returns UID when found, 0 when not found."""
|
||||
|
||||
self.ui.debug('imap', '__savemessage_fetchheaders called for %s: %s'% \
|
||||
(headername, headervalue))
|
||||
|
||||
@ -467,8 +467,8 @@ class IMAPFolder(BaseFolder):
|
||||
# or something. Argh. It seems that Time2Internaldate
|
||||
# will rause a ValueError if the year is 0102 but not 1902,
|
||||
# but some IMAP servers nonetheless choke on 1902.
|
||||
self.ui.debug('imap', "Message with invalid date %s. Server will use local time." \
|
||||
% datetuple)
|
||||
self.ui.debug('imap', "Message with invalid date %s. "
|
||||
"Server will use local time."% datetuple)
|
||||
return None
|
||||
|
||||
#produce a string representation of datetuple that works as
|
||||
@ -507,6 +507,7 @@ class IMAPFolder(BaseFolder):
|
||||
message is saved, but it's UID can not be found, it will
|
||||
return 0. If the message can't be written (folder is
|
||||
read-only for example) it will return -1."""
|
||||
|
||||
self.ui.savemessage('imap', uid, flags, self)
|
||||
|
||||
# already have it, just save modified flags
|
||||
@ -544,7 +545,7 @@ class IMAPFolder(BaseFolder):
|
||||
# insert a random unique header that we can fetch later
|
||||
(headername, headervalue) = self.__generate_randomheader(
|
||||
content)
|
||||
self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
|
||||
self.ui.debug('imap', 'savemessage: header is: %s: %s'%
|
||||
(headername, headervalue))
|
||||
content = self.addmessageheader(content, CRLF, headername, headervalue)
|
||||
|
||||
@ -566,9 +567,8 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
#Do the APPEND
|
||||
try:
|
||||
(typ, dat) = imapobj.append(self.getfullname(),
|
||||
imaputil.flagsmaildir2imap(flags),
|
||||
date, content)
|
||||
(typ, dat) = imapobj.append(fullname,
|
||||
imaputil.flagsmaildir2imap(flags), date, content)
|
||||
# This should only catch 'NO' responses since append()
|
||||
# will raise an exception for 'BAD' responses:
|
||||
if typ != 'OK':
|
||||
@ -654,16 +654,15 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
|
||||
def _fetch_from_imap(self, imapobj, uids, retry_num=1):
|
||||
"""
|
||||
Fetches data from IMAP server.
|
||||
"""Fetches data from IMAP server.
|
||||
|
||||
Arguments:
|
||||
- imapobj: IMAPlib object
|
||||
- uids: message UIDS
|
||||
- retry_num: number of retries to make
|
||||
|
||||
Returns: data obtained by this query.
|
||||
"""
|
||||
Returns: data obtained by this query."""
|
||||
|
||||
query = "(%s)"% (" ".join(self.imap_query))
|
||||
fails_left = retry_num # retry on dropped connection
|
||||
while fails_left:
|
||||
@ -695,23 +694,21 @@ class IMAPFolder(BaseFolder):
|
||||
|
||||
|
||||
def _store_to_imap(self, imapobj, uid, field, data):
|
||||
"""
|
||||
Stores data to IMAP server
|
||||
"""Stores data to IMAP server
|
||||
|
||||
Arguments:
|
||||
- imapobj: instance of IMAPlib to use
|
||||
- uid: message UID
|
||||
- field: field name to be stored/updated
|
||||
- data: field contents
|
||||
|
||||
"""
|
||||
imapobj.select(self.getfullname())
|
||||
res_type, retdata = imapobj.uid('store', uid, field, data)
|
||||
if res_type != 'OK':
|
||||
severity = OfflineImapError.ERROR.MESSAGE
|
||||
reason = "IMAP server '%s' failed to store %s for message UID '%d'."\
|
||||
"Server responded: %s %s" % (self.getrepository(), field, uid,
|
||||
res_type, retdata)
|
||||
"Server responded: %s %s"% (
|
||||
self.getrepository(), field, uid, res_type, retdata)
|
||||
raise OfflineImapError(reason, severity)
|
||||
return retdata[0]
|
||||
|
||||
@ -724,12 +721,11 @@ class IMAPFolder(BaseFolder):
|
||||
dryrun mode."""
|
||||
imapobj = self.imapserver.acquireconnection()
|
||||
try:
|
||||
result = self._store_to_imap(imapobj, str(uid), 'FLAGS', imaputil.flagsmaildir2imap(flags))
|
||||
|
||||
result = self._store_to_imap(imapobj, str(uid), 'FLAGS',
|
||||
imaputil.flagsmaildir2imap(flags))
|
||||
except imapobj.readonly:
|
||||
self.ui.flagstoreadonly(self, [uid], flags)
|
||||
return
|
||||
|
||||
finally:
|
||||
self.imapserver.releaseconnection(imapobj)
|
||||
|
||||
@ -751,6 +747,7 @@ class IMAPFolder(BaseFolder):
|
||||
"""This is here for the sake of UIDMaps.py -- deletemessages must
|
||||
add flags and get a converted UID, and if we don't have noconvert,
|
||||
then UIDMaps will try to convert it twice."""
|
||||
|
||||
self.__addmessagesflags_noconvert(uidlist, flags)
|
||||
|
||||
# Interface from BaseFolder
|
||||
@ -770,8 +767,7 @@ class IMAPFolder(BaseFolder):
|
||||
self.ui.flagstoreadonly(self, uidlist, flags)
|
||||
return
|
||||
r = imapobj.uid('store',
|
||||
imaputil.uid_sequence(uidlist),
|
||||
operation + 'FLAGS',
|
||||
imaputil.uid_sequence(uidlist), operation + 'FLAGS',
|
||||
imaputil.flagsmaildir2imap(flags))
|
||||
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
|
||||
r = r[1]
|
||||
@ -818,9 +814,9 @@ class IMAPFolder(BaseFolder):
|
||||
"""Change the message from existing uid to new_uid
|
||||
|
||||
If the backend supports it. IMAP does not and will throw errors."""
|
||||
|
||||
raise OfflineImapError('IMAP backend cannot change a messages UID from '
|
||||
'%d to %d' % (uid, new_uid),
|
||||
OfflineImapError.ERROR.MESSAGE)
|
||||
'%d to %d'% (uid, new_uid), OfflineImapError.ERROR.MESSAGE)
|
||||
|
||||
# Interface from BaseFolder
|
||||
def deletemessage(self, uid):
|
||||
|
@ -118,6 +118,7 @@ class IMAPServer:
|
||||
def getroot(self):
|
||||
"""Returns this server's folder root. Can only be called after one
|
||||
or more calls to acquireconnection."""
|
||||
|
||||
return self.root
|
||||
|
||||
|
||||
@ -126,6 +127,7 @@ class IMAPServer:
|
||||
|
||||
:param drop_conn: If True, the connection will be released and
|
||||
not be reused. This can be used to indicate broken connections."""
|
||||
|
||||
if connection is None: return #noop on bad connection
|
||||
self.connectionlock.acquire()
|
||||
self.assignedconnections.remove(connection)
|
||||
@ -147,17 +149,16 @@ class IMAPServer:
|
||||
return retval
|
||||
|
||||
def __loginauth(self, imapobj):
|
||||
""" Basic authentication via LOGIN command """
|
||||
""" Basic authentication via LOGIN command."""
|
||||
|
||||
self.ui.debug('imap', 'Attempting IMAP LOGIN authentication')
|
||||
imapobj.login(self.username, self.__getpassword())
|
||||
|
||||
|
||||
def __plainhandler(self, response):
|
||||
"""
|
||||
Implements SASL PLAIN authentication, RFC 4616,
|
||||
http://tools.ietf.org/html/rfc4616
|
||||
"""Implements SASL PLAIN authentication, RFC 4616,
|
||||
http://tools.ietf.org/html/rfc4616"""
|
||||
|
||||
"""
|
||||
authc = self.username
|
||||
passwd = self.__getpassword()
|
||||
authz = ''
|
||||
@ -175,8 +176,8 @@ class IMAPServer:
|
||||
try:
|
||||
if self.gss_step == self.GSS_STATE_STEP:
|
||||
if not self.gss_vc:
|
||||
rc, self.gss_vc = kerberos.authGSSClientInit('imap@' +
|
||||
self.hostname)
|
||||
rc, self.gss_vc = kerberos.authGSSClientInit(
|
||||
'imap@' + self.hostname)
|
||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||
rc = kerberos.authGSSClientStep(self.gss_vc, data)
|
||||
if rc != kerberos.AUTH_GSS_CONTINUE:
|
||||
@ -184,8 +185,8 @@ class IMAPServer:
|
||||
elif self.gss_step == self.GSS_STATE_WRAP:
|
||||
rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
|
||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||
rc = kerberos.authGSSClientWrap(self.gss_vc, response,
|
||||
self.username)
|
||||
rc = kerberos.authGSSClientWrap(
|
||||
self.gss_vc, response, self.username)
|
||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||
except kerberos.GSSError as err:
|
||||
# Kerberos errored out on us, respond with None to cancel the
|
||||
@ -266,8 +267,7 @@ class IMAPServer:
|
||||
|
||||
|
||||
def __authn_helper(self, imapobj):
|
||||
"""
|
||||
Authentication machinery for self.acquireconnection().
|
||||
"""Authentication machinery for self.acquireconnection().
|
||||
|
||||
Raises OfflineImapError() of type ERROR.REPO when
|
||||
there are either fatal problems or no authentications
|
||||
@ -275,9 +275,7 @@ class IMAPServer:
|
||||
|
||||
If any authentication method succeeds, routine should exit:
|
||||
warnings for failed methods are to be produced in the
|
||||
respective except blocks.
|
||||
|
||||
"""
|
||||
respective except blocks."""
|
||||
|
||||
# Authentication routines, hash keyed by method name
|
||||
# with value that is a tuple with
|
||||
@ -321,7 +319,7 @@ class IMAPServer:
|
||||
continue
|
||||
|
||||
tried_to_authn = True
|
||||
self.ui.debug('imap', 'Attempting '
|
||||
self.ui.debug('imap', u'Attempting '
|
||||
'%s authentication'% m)
|
||||
try:
|
||||
if func(imapobj):
|
||||
@ -343,7 +341,7 @@ class IMAPServer:
|
||||
lambda x: x[5:], filter(lambda x: x[0:5] == "AUTH=",
|
||||
imapobj.capabilities)
|
||||
))
|
||||
raise OfflineImapError("Repository %s: no supported "
|
||||
raise OfflineImapError(u"Repository %s: no supported "
|
||||
"authentication mechanisms found; configured %s, "
|
||||
"server advertises %s"% (self.repos,
|
||||
", ".join(self.authmechs), methods),
|
||||
@ -383,9 +381,8 @@ class IMAPServer:
|
||||
|
||||
self.connectionlock.release() # Release until need to modify data
|
||||
|
||||
""" Must be careful here that if we fail we should bail out gracefully
|
||||
and release locks / threads so that the next attempt can try...
|
||||
"""
|
||||
# Must be careful here that if we fail we should bail out gracefully
|
||||
# and release locks / threads so that the next attempt can try...
|
||||
success = 0
|
||||
try:
|
||||
while not success:
|
||||
@ -458,6 +455,7 @@ class IMAPServer:
|
||||
"""If we are here then we did not succeed in getting a
|
||||
connection - we should clean up and then re-raise the
|
||||
error..."""
|
||||
|
||||
self.semaphore.release()
|
||||
|
||||
severity = OfflineImapError.ERROR.REPO
|
||||
@ -503,13 +501,15 @@ class IMAPServer:
|
||||
raise
|
||||
|
||||
def connectionwait(self):
|
||||
"""Waits until there is a connection available. Note that between
|
||||
the time that a connection becomes available and the time it is
|
||||
requested, another thread may have grabbed it. This function is
|
||||
mainly present as a way to avoid spawning thousands of threads
|
||||
to copy messages, then have them all wait for 3 available connections.
|
||||
It's OK if we have maxconnections + 1 or 2 threads, which is what
|
||||
this will help us do."""
|
||||
"""Waits until there is a connection available.
|
||||
|
||||
Note that between the time that a connection becomes available and the
|
||||
time it is requested, another thread may have grabbed it. This function
|
||||
is mainly present as a way to avoid spawning thousands of threads to
|
||||
copy messages, then have them all wait for 3 available connections.
|
||||
It's OK if we have maxconnections + 1 or 2 threads, which is what this
|
||||
will help us do."""
|
||||
|
||||
self.semaphore.acquire()
|
||||
self.semaphore.release()
|
||||
|
||||
@ -533,11 +533,13 @@ class IMAPServer:
|
||||
self.gssapi = False
|
||||
|
||||
def keepalive(self, timeout, event):
|
||||
"""Sends a NOOP to each connection recorded. It will wait a maximum
|
||||
of timeout seconds between doing this, and will continue to do so
|
||||
until the Event object as passed is true. This method is expected
|
||||
to be invoked in a separate thread, which should be join()'d after
|
||||
the event is set."""
|
||||
"""Sends a NOOP to each connection recorded.
|
||||
|
||||
It will wait a maximum of timeout seconds between doing this, and will
|
||||
continue to do so until the Event object as passed is true. This method
|
||||
is expected to be invoked in a separate thread, which should be join()'d
|
||||
after the event is set."""
|
||||
|
||||
self.ui.debug('imap', 'keepalive thread started')
|
||||
while not event.isSet():
|
||||
self.connectionlock.acquire()
|
||||
@ -570,11 +572,11 @@ class IMAPServer:
|
||||
return
|
||||
|
||||
def __verifycert(self, cert, hostname):
|
||||
'''Verify that cert (in socket.getpeercert() format) matches hostname.
|
||||
CRLs are not handled.
|
||||
"""Verify that cert (in socket.getpeercert() format) matches hostname.
|
||||
|
||||
CRLs are not handled.
|
||||
Returns error message if any problems are found and None on success."""
|
||||
|
||||
Returns error message if any problems are found and None on success.
|
||||
'''
|
||||
errstr = "CA Cert verifying failed: "
|
||||
if not cert:
|
||||
return ('%s no certificate received'% errstr)
|
||||
@ -614,6 +616,7 @@ class IdleThread(object):
|
||||
"""If invoked without 'folder', perform a NOOP and wait for
|
||||
self.stop() to be called. If invoked with folder, switch to IDLE
|
||||
mode and synchronize once we have a new message"""
|
||||
|
||||
self.parent = parent
|
||||
self.folder = folder
|
||||
self.stop_sig = Event()
|
||||
@ -644,7 +647,7 @@ class IdleThread(object):
|
||||
try:
|
||||
imapobj.noop()
|
||||
except imapobj.abort:
|
||||
self.ui.warn('Attempting NOOP on dropped connection %s' % \
|
||||
self.ui.warn('Attempting NOOP on dropped connection %s'%
|
||||
imapobj.identifier)
|
||||
self.parent.releaseconnection(imapobj, True)
|
||||
imapobj = None
|
||||
|
@ -121,8 +121,7 @@ def imapsplit(imapstring):
|
||||
arg = arg.replace('\\', '\\\\')
|
||||
arg = arg.replace('"', '\\"')
|
||||
arg = '"%s"' % arg
|
||||
__debug("imapsplit() non-string [%d]: Appending %s" %\
|
||||
(i, arg))
|
||||
__debug("imapsplit() non-string [%d]: Appending %s"% (i, arg))
|
||||
retval.append(arg)
|
||||
else:
|
||||
# Even -- we have a string that ends with a literal
|
||||
|
@ -23,6 +23,7 @@ import signal
|
||||
import socket
|
||||
import logging
|
||||
from optparse import OptionParser
|
||||
|
||||
import offlineimap
|
||||
from offlineimap import accounts, threadutil, syncmaster
|
||||
from offlineimap import globals
|
||||
@ -335,8 +336,8 @@ class OfflineImap:
|
||||
" accounts are defined!"% account
|
||||
else:
|
||||
errormsg = "The account '%s' does not exist. Valid ac" \
|
||||
"counts are: " % account
|
||||
errormsg += ", ".join(allaccounts.keys())
|
||||
"counts are: %s"% \
|
||||
(account, ", ".join(allaccounts.keys()))
|
||||
self.ui.terminate(1, errormsg=errormsg)
|
||||
if account not in syncaccounts:
|
||||
syncaccounts.append(account)
|
||||
|
@ -23,9 +23,7 @@ except:
|
||||
pass
|
||||
|
||||
class LocalEval:
|
||||
"""Here is a powerfull but very dangerous option, of course.
|
||||
|
||||
Assume source file to be ASCII encoded."""
|
||||
"""Here is a powerfull but very dangerous option, of course."""
|
||||
|
||||
def __init__(self, path=None):
|
||||
self.namespace = {}
|
||||
|
@ -167,6 +167,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
|
||||
that forward and backward nametrans actually match up!
|
||||
Configuring nametrans on BOTH repositories therefore could lead
|
||||
to infinite folder creation cycles."""
|
||||
|
||||
if not self.get_create_folders() and not dst_repo.get_create_folders():
|
||||
# quick exit if no folder creation is enabled on either side.
|
||||
return
|
||||
|
@ -53,8 +53,8 @@ class LocalStatusRepository(BaseRepository):
|
||||
self.LocalStatusFolderClass = self.backends[backend]['class']
|
||||
|
||||
else:
|
||||
raise SyntaxWarning("Unknown status_backend '%s' for account '%s'" \
|
||||
% (backend, self.account.name))
|
||||
raise SyntaxWarning("Unknown status_backend '%s' for account '%s'"%
|
||||
(backend, self.account.name))
|
||||
|
||||
def import_other_backend(self, folder):
|
||||
for bk, dic in self.backends.items():
|
||||
@ -68,8 +68,9 @@ class LocalStatusRepository(BaseRepository):
|
||||
|
||||
# if backend contains data, import it to folder.
|
||||
if not folderbk.isnewfolder():
|
||||
self.ui._msg('Migrating LocalStatus cache from %s to %s ' % (bk, self._backend) + \
|
||||
'status folder for %s:%s' % (self.name, folder.name))
|
||||
self.ui._msg('Migrating LocalStatus cache from %s to %s " \
|
||||
"status folder for %s:%s'%
|
||||
(bk, self._backend, self.name, folder.name))
|
||||
|
||||
folderbk.cachemessagelist()
|
||||
folder.messagelist = folderbk.messagelist
|
||||
|
@ -32,7 +32,7 @@ class MaildirRepository(BaseRepository):
|
||||
self.root = self.getlocalroot()
|
||||
self.folders = None
|
||||
self.ui = getglobalui()
|
||||
self.debug("MaildirRepository initialized, sep is " + repr(self.getsep()))
|
||||
self.debug("MaildirRepository initialized, sep is %s"% repr(self.getsep()))
|
||||
self.folder_atimes = []
|
||||
|
||||
# Create the top-level folder if it doesn't exist
|
||||
@ -128,6 +128,7 @@ class MaildirRepository(BaseRepository):
|
||||
If necessary, scan and cache all foldernames to make sure that
|
||||
we only return existing folders and that 2 calls with the same
|
||||
name will return the same object."""
|
||||
|
||||
# getfolders() will scan and cache the values *if* necessary
|
||||
folders = self.getfolders()
|
||||
for f in folders:
|
||||
@ -142,8 +143,9 @@ class MaildirRepository(BaseRepository):
|
||||
|
||||
:param root: (absolute) path to Maildir root
|
||||
:param extension: (relative) subfolder to examine within root"""
|
||||
self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s" \
|
||||
% (root, extension))
|
||||
|
||||
self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s"%
|
||||
(root, extension))
|
||||
retval = []
|
||||
|
||||
# Configure the full path to this repository -- "toppath"
|
||||
|
@ -23,7 +23,7 @@ from offlineimap import banner
|
||||
from offlineimap.ui.UIBase import UIBase
|
||||
|
||||
class TTYFormatter(logging.Formatter):
|
||||
"""Specific Formatter that adds thread information to the log output"""
|
||||
"""Specific Formatter that adds thread information to the log output."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
#super() doesn't work in py2.6 as 'logging' uses old-style class
|
||||
@ -31,7 +31,8 @@ class TTYFormatter(logging.Formatter):
|
||||
self._last_log_thread = None
|
||||
|
||||
def format(self, record):
|
||||
"""Override format to add thread information"""
|
||||
"""Override format to add thread information."""
|
||||
|
||||
#super() doesn't work in py2.6 as 'logging' uses old-style class
|
||||
log_str = logging.Formatter.format(self, record)
|
||||
# If msg comes from a different thread than our last, prepend
|
||||
@ -69,12 +70,12 @@ class TTYUI(UIBase):
|
||||
return ch
|
||||
|
||||
def isusable(self):
|
||||
"""TTYUI is reported as usable when invoked on a terminal"""
|
||||
"""TTYUI is reported as usable when invoked on a terminal."""
|
||||
|
||||
return sys.stdout.isatty() and sys.stdin.isatty()
|
||||
|
||||
def getpass(self, accountname, config, errmsg=None):
|
||||
"""TTYUI backend is capable of querying the password"""
|
||||
"""TTYUI backend is capable of querying the password."""
|
||||
|
||||
if errmsg:
|
||||
self.warn("%s: %s"% (accountname, errmsg))
|
||||
@ -100,8 +101,7 @@ class TTYUI(UIBase):
|
||||
|
||||
This implementation in UIBase does not support this, but some
|
||||
implementations return 0 for successful sleep and 1 for an
|
||||
'abort', ie a request to sync immediately.
|
||||
"""
|
||||
'abort', ie a request to sync immediately."""
|
||||
|
||||
if sleepsecs > 0:
|
||||
if remainingsecs//60 != (remainingsecs-sleepsecs)//60:
|
||||
|
@ -28,7 +28,8 @@ class DebuggingLock:
|
||||
def acquire(self, blocking = 1):
|
||||
self.print_tb("Acquire lock")
|
||||
self.lock.acquire(blocking)
|
||||
self.logmsg("===== %s: Thread %s acquired lock\n" % (self.name, currentThread().getName()))
|
||||
self.logmsg("===== %s: Thread %s acquired lock\n"%
|
||||
(self.name, currentThread().getName()))
|
||||
|
||||
def release(self):
|
||||
self.print_tb("Release lock")
|
||||
|
Loading…
Reference in New Issue
Block a user