more style consistency
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
594a286888
commit
0f40ca4799
@ -321,18 +321,18 @@ class SyncableAccount(Account):
|
|||||||
|
|
||||||
if not remotefolder.sync_this:
|
if not remotefolder.sync_this:
|
||||||
self.ui.debug('', "Not syncing filtered folder '%s'"
|
self.ui.debug('', "Not syncing filtered folder '%s'"
|
||||||
"[%s]" % (remotefolder, remoterepos))
|
"[%s]"% (remotefolder, remoterepos))
|
||||||
continue # Ignore filtered folder
|
continue # Ignore filtered folder
|
||||||
localfolder = self.get_local_folder(remotefolder)
|
localfolder = self.get_local_folder(remotefolder)
|
||||||
if not localfolder.sync_this:
|
if not localfolder.sync_this:
|
||||||
self.ui.debug('', "Not syncing filtered folder '%s'"
|
self.ui.debug('', "Not syncing filtered folder '%s'"
|
||||||
"[%s]" % (localfolder, localfolder.repository))
|
"[%s]"% (localfolder, localfolder.repository))
|
||||||
continue # Ignore filtered folder
|
continue # Ignore filtered folder
|
||||||
if not globals.options.singlethreading:
|
if not globals.options.singlethreading:
|
||||||
thread = InstanceLimitedThread(\
|
thread = InstanceLimitedThread(\
|
||||||
instancename = 'FOLDER_' + self.remoterepos.getname(),
|
instancename = 'FOLDER_' + self.remoterepos.getname(),
|
||||||
target = syncfolder,
|
target = syncfolder,
|
||||||
name = "Folder %s [acc: %s]" % (remotefolder.getexplainedname(), self),
|
name = "Folder %s [acc: %s]"% (remotefolder.getexplainedname(), self),
|
||||||
args = (self, remotefolder, quick))
|
args = (self, remotefolder, quick))
|
||||||
thread.start()
|
thread.start()
|
||||||
folderthreads.append(thread)
|
folderthreads.append(thread)
|
||||||
@ -385,6 +385,7 @@ def syncfolder(account, remotefolder, quick):
|
|||||||
"""Synchronizes given remote folder for the specified account.
|
"""Synchronizes given remote folder for the specified account.
|
||||||
|
|
||||||
Filtered folders on the remote side will not invoke this function."""
|
Filtered folders on the remote side will not invoke this function."""
|
||||||
|
|
||||||
remoterepos = account.remoterepos
|
remoterepos = account.remoterepos
|
||||||
localrepos = account.localrepos
|
localrepos = account.localrepos
|
||||||
statusrepos = account.statusrepos
|
statusrepos = account.statusrepos
|
||||||
|
@ -19,13 +19,12 @@ import email
|
|||||||
from email.Parser import Parser as MailParser
|
from email.Parser import Parser as MailParser
|
||||||
|
|
||||||
def get_message_date(content, header='Date'):
|
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;
|
:param header: the header to extract date from;
|
||||||
:returns: timestamp or `None` in the case of failure.
|
:returns: timestamp or `None` in the case of failure.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
message = MailParser().parsestr(content, True)
|
message = MailParser().parsestr(content, True)
|
||||||
dateheader = message.get(header)
|
dateheader = message.get(header)
|
||||||
# parsedate_tz returns a 10-tuple that can be passed to mktime_tz
|
# 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
|
# along with this program; if not, write to the Free Software
|
||||||
# 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 os.path
|
||||||
|
import re
|
||||||
|
from sys import exc_info
|
||||||
|
|
||||||
from offlineimap import threadutil, emailutil
|
from offlineimap import threadutil, emailutil
|
||||||
from offlineimap import globals
|
from offlineimap import globals
|
||||||
from offlineimap.ui import getglobalui
|
from offlineimap.ui import getglobalui
|
||||||
from offlineimap.error import OfflineImapError
|
from offlineimap.error import OfflineImapError
|
||||||
import offlineimap.accounts
|
import offlineimap.accounts
|
||||||
import os.path
|
|
||||||
import re
|
|
||||||
from sys import exc_info
|
|
||||||
|
|
||||||
|
|
||||||
class BaseFolder(object):
|
class BaseFolder(object):
|
||||||
@ -31,6 +32,7 @@ class BaseFolder(object):
|
|||||||
:para name: Path & name of folder minus root or reference
|
:para name: Path & name of folder minus root or reference
|
||||||
:para repository: Repository() in which the folder is.
|
:para repository: Repository() in which the folder is.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.ui = getglobalui()
|
self.ui = getglobalui()
|
||||||
# Save original name for folderfilter operations
|
# Save original name for folderfilter operations
|
||||||
self.ffilter_name = name
|
self.ffilter_name = name
|
||||||
@ -56,15 +58,15 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
# Determine if we're running static or dynamic folder filtering
|
# Determine if we're running static or dynamic folder filtering
|
||||||
# and check filtering status
|
# and check filtering status
|
||||||
self._dynamic_folderfilter = \
|
self._dynamic_folderfilter = self.config.getdefaultboolean(
|
||||||
self.config.getdefaultboolean(repo, "dynamic_folderfilter", False)
|
repo, "dynamic_folderfilter", False)
|
||||||
self._sync_this = repository.should_sync_folder(self.ffilter_name)
|
self._sync_this = repository.should_sync_folder(self.ffilter_name)
|
||||||
if self._dynamic_folderfilter:
|
if self._dynamic_folderfilter:
|
||||||
self.ui.debug('', "Running dynamic folder filtering on '%s'[%s]" \
|
self.ui.debug('', "Running dynamic folder filtering on '%s'[%s]"%
|
||||||
% (self.ffilter_name, repository))
|
(self.ffilter_name, repository))
|
||||||
elif not self._sync_this:
|
elif not self._sync_this:
|
||||||
self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter" \
|
self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter"%
|
||||||
% (self.ffilter_name, repository))
|
(self.ffilter_name, repository))
|
||||||
|
|
||||||
# Passes for syncmessagesto
|
# Passes for syncmessagesto
|
||||||
self.syncmessagesto_passes = [('copying messages' , self.__syncmessagesto_copy),
|
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.
|
:param statusfolder: keeps track of the last known folder state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getcopyinstancelimit(self):
|
def getcopyinstancelimit(self):
|
||||||
"""For threading folders, returns the instancelimitname for
|
"""For threading folders, returns the instancelimitname for
|
||||||
InstanceLimitedThreads."""
|
InstanceLimitedThreads."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def storesmessages(self):
|
def storesmessages(self):
|
||||||
"""Should be true for any backend that actually saves message bodies.
|
"""Should be true for any backend that actually saves message bodies.
|
||||||
(Almost all of them). False for the LocalStatus backend. Saves
|
(Almost all of them). False for the LocalStatus backend. Saves
|
||||||
us from having to slurp up messages just for localstatus purposes."""
|
us from having to slurp up messages just for localstatus purposes."""
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def getvisiblename(self):
|
def getvisiblename(self):
|
||||||
@ -143,14 +148,17 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
def getrepository(self):
|
def getrepository(self):
|
||||||
"""Returns the repository object that this folder is within."""
|
"""Returns the repository object that this folder is within."""
|
||||||
|
|
||||||
return self.repository
|
return self.repository
|
||||||
|
|
||||||
def getroot(self):
|
def getroot(self):
|
||||||
"""Returns the root of the folder, in a folder-specific fashion."""
|
"""Returns the root of the folder, in a folder-specific fashion."""
|
||||||
|
|
||||||
return self.root
|
return self.root
|
||||||
|
|
||||||
def getsep(self):
|
def getsep(self):
|
||||||
"""Returns the separator for this folder type."""
|
"""Returns the separator for this folder type."""
|
||||||
|
|
||||||
return self.sep
|
return self.sep
|
||||||
|
|
||||||
def getfullname(self):
|
def getfullname(self):
|
||||||
@ -160,7 +168,8 @@ class BaseFolder(object):
|
|||||||
return self.getname()
|
return self.getname()
|
||||||
|
|
||||||
def getfolderbasename(self):
|
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:
|
if not self.name:
|
||||||
basename = '.'
|
basename = '.'
|
||||||
else: #avoid directory hierarchies and file names such as '/'
|
else: #avoid directory hierarchies and file names such as '/'
|
||||||
@ -188,6 +197,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
def _getuidfilename(self):
|
def _getuidfilename(self):
|
||||||
"""provides UIDVALIDITY cache filename for class internal purposes"""
|
"""provides UIDVALIDITY cache filename for class internal purposes"""
|
||||||
|
|
||||||
return os.path.join(self.repository.getuiddir(),
|
return os.path.join(self.repository.getuiddir(),
|
||||||
self.getfolderbasename())
|
self.getfolderbasename())
|
||||||
|
|
||||||
@ -196,6 +206,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
:returns: UIDVALIDITY as (long) number or None, if None had been
|
:returns: UIDVALIDITY as (long) number or None, if None had been
|
||||||
saved yet."""
|
saved yet."""
|
||||||
|
|
||||||
if hasattr(self, '_base_saved_uidvalidity'):
|
if hasattr(self, '_base_saved_uidvalidity'):
|
||||||
return self._base_saved_uidvalidity
|
return self._base_saved_uidvalidity
|
||||||
uidfilename = self._getuidfilename()
|
uidfilename = self._getuidfilename()
|
||||||
@ -212,6 +223,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
This function is not threadsafe, so don't attempt to call it
|
This function is not threadsafe, so don't attempt to call it
|
||||||
from concurrent threads."""
|
from concurrent threads."""
|
||||||
|
|
||||||
newval = self.get_uidvalidity()
|
newval = self.get_uidvalidity()
|
||||||
uidfilename = self._getuidfilename()
|
uidfilename = self._getuidfilename()
|
||||||
|
|
||||||
@ -225,45 +237,50 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
This function needs to be implemented by each Backend
|
This function needs to be implemented by each Backend
|
||||||
:returns: UIDVALIDITY as a (long) number"""
|
:returns: UIDVALIDITY as a (long) number"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
"""Reads the message list from disk or network and stores it in
|
"""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 for later use. This list will not be re-read from disk or
|
||||||
memory unless this function is called again."""
|
memory unless this function is called again."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def getmessagelist(self):
|
def getmessagelist(self):
|
||||||
"""Gets the current message list.
|
"""Gets the current message list.
|
||||||
You must call cachemessagelist() before calling this function!"""
|
You must call cachemessagelist() before calling this function!"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def msglist_item_initializer(self, uid):
|
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
|
This function must initialize all fields of messagelist item
|
||||||
and must be called every time when one creates new messagelist
|
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
|
raise NotImplementedError
|
||||||
|
|
||||||
def uidexists(self, uid):
|
def uidexists(self, uid):
|
||||||
"""Returns True if uid exists"""
|
"""Returns True if uid exists"""
|
||||||
|
|
||||||
return uid in self.getmessagelist()
|
return uid in self.getmessagelist()
|
||||||
|
|
||||||
def getmessageuidlist(self):
|
def getmessageuidlist(self):
|
||||||
"""Gets a list of UIDs.
|
"""Gets a list of UIDs.
|
||||||
You may have to call cachemessagelist() before calling this function!"""
|
You may have to call cachemessagelist() before calling this function!"""
|
||||||
|
|
||||||
return self.getmessagelist().keys()
|
return self.getmessagelist().keys()
|
||||||
|
|
||||||
def getmessagecount(self):
|
def getmessagecount(self):
|
||||||
"""Gets the number of messages."""
|
"""Gets the number of messages."""
|
||||||
|
|
||||||
return len(self.getmessagelist())
|
return len(self.getmessagelist())
|
||||||
|
|
||||||
def getmessage(self, uid):
|
def getmessage(self, uid):
|
||||||
"""Returns the content of the specified message."""
|
"""Returns the content of the specified message."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
@ -286,20 +303,23 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
Note that savemessage() does not check against dryrun settings,
|
Note that savemessage() does not check against dryrun settings,
|
||||||
so you need to ensure that savemessage is never called in a
|
so you need to ensure that savemessage is never called in a
|
||||||
dryrun mode.
|
dryrun mode."""
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def getmessagetime(self, uid):
|
def getmessagetime(self, uid):
|
||||||
"""Return the received time for the specified message."""
|
"""Return the received time for the specified message."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def getmessagemtime(self, uid):
|
def getmessagemtime(self, uid):
|
||||||
"""Returns the message modification time of the specified message."""
|
"""Returns the message modification time of the specified message."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def getmessageflags(self, uid):
|
def getmessageflags(self, uid):
|
||||||
"""Returns the flags for the specified message."""
|
"""Returns the flags for the specified message."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
@ -308,6 +328,7 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def addmessageflags(self, uid, flags):
|
def addmessageflags(self, uid, flags):
|
||||||
@ -319,14 +340,15 @@ class BaseFolder(object):
|
|||||||
dryrun mode.
|
dryrun mode.
|
||||||
|
|
||||||
:param flags: A set() of flags"""
|
:param flags: A set() of flags"""
|
||||||
|
|
||||||
newflags = self.getmessageflags(uid) | flags
|
newflags = self.getmessageflags(uid) | flags
|
||||||
self.savemessageflags(uid, newflags)
|
self.savemessageflags(uid, newflags)
|
||||||
|
|
||||||
def addmessagesflags(self, uidlist, flags):
|
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
self.addmessageflags(uid, flags)
|
self.addmessageflags(uid, flags)
|
||||||
|
|
||||||
@ -337,6 +359,7 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
newflags = self.getmessageflags(uid) - flags
|
newflags = self.getmessageflags(uid) - flags
|
||||||
self.savemessageflags(uid, newflags)
|
self.savemessageflags(uid, newflags)
|
||||||
|
|
||||||
@ -345,10 +368,10 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
self.deletemessageflags(uid, flags)
|
self.deletemessageflags(uid, flags)
|
||||||
|
|
||||||
|
|
||||||
def getmessagelabels(self, uid):
|
def getmessagelabels(self, uid):
|
||||||
"""Returns the labels for the specified message."""
|
"""Returns the labels for the specified message."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -359,6 +382,7 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def addmessagelabels(self, uid, labels):
|
def addmessagelabels(self, uid, labels):
|
||||||
@ -370,14 +394,15 @@ class BaseFolder(object):
|
|||||||
dryrun mode.
|
dryrun mode.
|
||||||
|
|
||||||
:param labels: A set() of labels"""
|
:param labels: A set() of labels"""
|
||||||
|
|
||||||
newlabels = self.getmessagelabels(uid) | labels
|
newlabels = self.getmessagelabels(uid) | labels
|
||||||
self.savemessagelabels(uid, newlabels)
|
self.savemessagelabels(uid, newlabels)
|
||||||
|
|
||||||
def addmessageslabels(self, uidlist, labels):
|
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
self.addmessagelabels(uid, labels)
|
self.addmessagelabels(uid, labels)
|
||||||
|
|
||||||
@ -388,6 +413,7 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
newlabels = self.getmessagelabels(uid) - labels
|
newlabels = self.getmessagelabels(uid) - labels
|
||||||
self.savemessagelabels(uid, newlabels)
|
self.savemessagelabels(uid, newlabels)
|
||||||
|
|
||||||
@ -396,12 +422,12 @@ class BaseFolder(object):
|
|||||||
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
self.deletemessagelabels(uid, labels)
|
self.deletemessagelabels(uid, labels)
|
||||||
|
|
||||||
def addmessageheader(self, content, linebreak, headername, headervalue):
|
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,
|
WARNING: This function is a bit tricky, and modifying it in the wrong way,
|
||||||
may easily lead to data-loss.
|
may easily lead to data-loss.
|
||||||
@ -454,9 +480,9 @@ class BaseFolder(object):
|
|||||||
This is the body\n
|
This is the body\n
|
||||||
next line\n
|
next line\n
|
||||||
"""
|
"""
|
||||||
self.ui.debug('',
|
|
||||||
'addmessageheader: called to add %s: %s' % (headername,
|
self.ui.debug('', 'addmessageheader: called to add %s: %s'%
|
||||||
headervalue))
|
(headername, headervalue))
|
||||||
|
|
||||||
insertionpoint = content.find(linebreak * 2)
|
insertionpoint = content.find(linebreak * 2)
|
||||||
if insertionpoint == -1:
|
if insertionpoint == -1:
|
||||||
@ -490,24 +516,23 @@ class BaseFolder(object):
|
|||||||
if content[0:len(linebreak)] != linebreak:
|
if content[0:len(linebreak)] != linebreak:
|
||||||
suffix = suffix + linebreak
|
suffix = suffix + linebreak
|
||||||
|
|
||||||
self.ui.debug('', 'addmessageheader: insertionpoint = %d' % insertionpoint)
|
self.ui.debug('', 'addmessageheader: insertionpoint = %d'% insertionpoint)
|
||||||
headers = content[0:insertionpoint]
|
headers = content[0:insertionpoint]
|
||||||
self.ui.debug('', 'addmessageheader: headers = %s' % repr(headers))
|
self.ui.debug('', 'addmessageheader: headers = %s'% repr(headers))
|
||||||
new_header = prefix + ("%s: %s" % (headername, headervalue)) + suffix
|
new_header = prefix + ("%s: %s" % (headername, headervalue)) + suffix
|
||||||
self.ui.debug('', 'addmessageheader: new_header = ' + repr(new_header))
|
self.ui.debug('', 'addmessageheader: new_header = ' + repr(new_header))
|
||||||
return headers + new_header + content[insertionpoint:]
|
return headers + new_header + content[insertionpoint:]
|
||||||
|
|
||||||
|
|
||||||
def __find_eoh(self, content):
|
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.
|
Either double '\n', or end of string.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- content: contents of the message to search in
|
- content: contents of the message to search in
|
||||||
Returns: position of the first non-header byte.
|
Returns: position of the first non-header byte.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
eoh_cr = content.find('\n\n')
|
eoh_cr = content.find('\n\n')
|
||||||
if eoh_cr == -1:
|
if eoh_cr == -1:
|
||||||
eoh_cr = len(content)
|
eoh_cr = len(content)
|
||||||
@ -516,8 +541,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
|
|
||||||
def getmessageheader(self, content, name):
|
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.
|
its value. Header name is case-insensitive.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -525,13 +549,13 @@ class BaseFolder(object):
|
|||||||
- name: name of the header to be searched
|
- name: name of the header to be searched
|
||||||
|
|
||||||
Returns: header value or None if no such header was found
|
Returns: header value or None if no such header was found
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.ui.debug('', 'getmessageheader: called to get %s' % name)
|
|
||||||
|
self.ui.debug('', 'getmessageheader: called to get %s'% name)
|
||||||
eoh = self.__find_eoh(content)
|
eoh = self.__find_eoh(content)
|
||||||
self.ui.debug('', 'getmessageheader: eoh = %d' % eoh)
|
self.ui.debug('', 'getmessageheader: eoh = %d'% eoh)
|
||||||
headers = content[0:eoh]
|
headers = content[0:eoh]
|
||||||
self.ui.debug('', 'getmessageheader: headers = %s' % repr(headers))
|
self.ui.debug('', 'getmessageheader: headers = %s'% repr(headers))
|
||||||
|
|
||||||
m = re.search('^%s:(.*)$' % name, headers, flags = re.MULTILINE | re.IGNORECASE)
|
m = re.search('^%s:(.*)$' % name, headers, flags = re.MULTILINE | re.IGNORECASE)
|
||||||
if m:
|
if m:
|
||||||
@ -541,8 +565,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
|
|
||||||
def getmessageheaderlist(self, content, name):
|
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.
|
that header.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -550,8 +573,8 @@ class BaseFolder(object):
|
|||||||
- name: name of the header to be searched
|
- name: name of the header to be searched
|
||||||
|
|
||||||
Returns: list of header values or emptylist if no such header was found
|
Returns: list of header values or emptylist if no such header was found
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.ui.debug('', 'getmessageheaderlist: called to get %s' % name)
|
self.ui.debug('', 'getmessageheaderlist: called to get %s' % name)
|
||||||
eoh = self.__find_eoh(content)
|
eoh = self.__find_eoh(content)
|
||||||
self.ui.debug('', 'getmessageheaderlist: eoh = %d' % eoh)
|
self.ui.debug('', 'getmessageheaderlist: eoh = %d' % eoh)
|
||||||
@ -562,27 +585,26 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
|
|
||||||
def deletemessageheaders(self, content, header_list):
|
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:
|
Arguments:
|
||||||
- content: message itself
|
- content: message itself
|
||||||
- header_list: list of headers to be deleted or just the header name
|
- header_list: list of headers to be deleted or just the header name
|
||||||
|
|
||||||
We expect our message to have '\n' as line endings.
|
We expect our message to have '\n' as line endings.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if type(header_list) != type([]):
|
if type(header_list) != type([]):
|
||||||
header_list = [header_list]
|
header_list = [header_list]
|
||||||
self.ui.debug('', 'deletemessageheaders: called to delete %s' % (header_list))
|
self.ui.debug('', 'deletemessageheaders: called to delete %s'% (header_list))
|
||||||
|
|
||||||
if not len(header_list): return content
|
if not len(header_list): return content
|
||||||
|
|
||||||
eoh = self.__find_eoh(content)
|
eoh = self.__find_eoh(content)
|
||||||
self.ui.debug('', 'deletemessageheaders: end of headers = %d' % eoh)
|
self.ui.debug('', 'deletemessageheaders: end of headers = %d'% eoh)
|
||||||
headers = content[0:eoh]
|
headers = content[0:eoh]
|
||||||
rest = content[eoh:]
|
rest = content[eoh:]
|
||||||
self.ui.debug('', 'deletemessageheaders: headers = %s' % repr(headers))
|
self.ui.debug('', 'deletemessageheaders: headers = %s'% repr(headers))
|
||||||
new_headers = []
|
new_headers = []
|
||||||
for h in headers.split('\n'):
|
for h in headers.split('\n'):
|
||||||
keep_it = True
|
keep_it = True
|
||||||
@ -609,16 +631,14 @@ class BaseFolder(object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def deletemessage(self, uid):
|
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def deletemessages(self, uidlist):
|
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
|
so you need to ensure that it is never called in a
|
||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
|
|
||||||
@ -686,9 +706,8 @@ class BaseFolder(object):
|
|||||||
self.deletemessage(uid)
|
self.deletemessage(uid)
|
||||||
else:
|
else:
|
||||||
raise OfflineImapError("Trying to save msg (uid %d) on folder "
|
raise OfflineImapError("Trying to save msg (uid %d) on folder "
|
||||||
"%s returned invalid uid %d" % (uid,
|
"%s returned invalid uid %d"% (uid, dstfolder.getvisiblename(),
|
||||||
dstfolder.getvisiblename(), new_uid),
|
new_uid), OfflineImapError.ERROR.MESSAGE)
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
|
||||||
except (KeyboardInterrupt): # bubble up CTRL-C
|
except (KeyboardInterrupt): # bubble up CTRL-C
|
||||||
raise
|
raise
|
||||||
except OfflineImapError as e:
|
except OfflineImapError as e:
|
||||||
@ -697,8 +716,7 @@ class BaseFolder(object):
|
|||||||
self.ui.error(e, exc_info()[2])
|
self.ui.error(e, exc_info()[2])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ui.error(e, exc_info()[2],
|
self.ui.error(e, exc_info()[2],
|
||||||
msg="Copying message %s [acc: %s]" %\
|
msg = "Copying message %s [acc: %s]"% (uid, self.accountname))
|
||||||
(uid, self.accountname))
|
|
||||||
raise #raise on unknown errors, so we can fix those
|
raise #raise on unknown errors, so we can fix those
|
||||||
|
|
||||||
def __syncmessagesto_copy(self, dstfolder, statusfolder):
|
def __syncmessagesto_copy(self, dstfolder, statusfolder):
|
||||||
@ -714,6 +732,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
This function checks and protects us from action in ryrun mode.
|
This function checks and protects us from action in ryrun mode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
threads = []
|
threads = []
|
||||||
|
|
||||||
copylist = filter(lambda uid: not \
|
copylist = filter(lambda uid: not \
|
||||||
@ -754,6 +773,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
This function checks and protects us from action in ryrun mode.
|
This function checks and protects us from action in ryrun mode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
deletelist = filter(lambda uid: uid>=0 \
|
deletelist = filter(lambda uid: uid>=0 \
|
||||||
and not self.uidexists(uid),
|
and not self.uidexists(uid),
|
||||||
statusfolder.getmessageuidlist())
|
statusfolder.getmessageuidlist())
|
||||||
@ -776,6 +796,7 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
This function checks and protects us from action in ryrun mode.
|
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
|
# For each flag, we store a list of uids to which it should be
|
||||||
# added. Then, we can call addmessagesflags() to apply them in
|
# added. Then, we can call addmessagesflags() to apply them in
|
||||||
# bulk, rather than one call per message.
|
# bulk, rather than one call per message.
|
||||||
@ -854,8 +875,8 @@ class BaseFolder(object):
|
|||||||
|
|
||||||
:param dstfolder: Folderinstance to sync the msgs to.
|
:param dstfolder: Folderinstance to sync the msgs to.
|
||||||
:param statusfolder: LocalStatus instance to sync against.
|
:param statusfolder: LocalStatus instance to sync against.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for (passdesc, action) in self.syncmessagesto_passes:
|
for (passdesc, action) in self.syncmessagesto_passes:
|
||||||
# bail out on CTRL-C or SIGTERM
|
# bail out on CTRL-C or SIGTERM
|
||||||
if offlineimap.accounts.Account.abort_NOW_signal.is_set():
|
if offlineimap.accounts.Account.abort_NOW_signal.is_set():
|
||||||
@ -883,6 +904,7 @@ class BaseFolder(object):
|
|||||||
MailDirFolder('foo') == IMAPFolder('foo') --> False
|
MailDirFolder('foo') == IMAPFolder('foo') --> False
|
||||||
MailDirFolder('foo') == MaildirFolder('foo') --> False
|
MailDirFolder('foo') == MaildirFolder('foo') --> False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(other, basestring):
|
if isinstance(other, basestring):
|
||||||
return other == self.name
|
return other == self.name
|
||||||
return id(self) == id(other)
|
return id(self) == id(other)
|
||||||
|
@ -20,6 +20,7 @@ import binascii
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
|
|
||||||
from .Base import BaseFolder
|
from .Base import BaseFolder
|
||||||
from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError
|
from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError
|
||||||
from offlineimap import globals
|
from offlineimap import globals
|
||||||
@ -54,7 +55,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
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]
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
Prefer SELECT to EXAMINE if we can, since some servers
|
Prefer SELECT to EXAMINE if we can, since some servers
|
||||||
@ -86,6 +87,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
UIDVALIDITY value will be cached on the first call.
|
UIDVALIDITY value will be cached on the first call.
|
||||||
:returns: The UIDVALIDITY as (long) number."""
|
:returns: The UIDVALIDITY as (long) number."""
|
||||||
|
|
||||||
if hasattr(self, '_uidvalidity'):
|
if hasattr(self, '_uidvalidity'):
|
||||||
# use cached value if existing
|
# use cached value if existing
|
||||||
return self._uidvalidity
|
return self._uidvalidity
|
||||||
@ -141,8 +143,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
|
|
||||||
def _msgs_to_fetch(self, imapobj):
|
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
|
Message sequence numbers (MSNs) are more easily compacted
|
||||||
into ranges which makes transactions slightly faster.
|
into ranges which makes transactions slightly faster.
|
||||||
@ -151,9 +152,8 @@ class IMAPFolder(BaseFolder):
|
|||||||
- imapobj: instance of IMAPlib
|
- imapobj: instance of IMAPlib
|
||||||
|
|
||||||
Returns: range(s) for messages or None if no messages
|
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)
|
res_type, imapdata = imapobj.select(self.getfullname(), True, True)
|
||||||
if imapdata == [None] or imapdata[0] == '0':
|
if imapdata == [None] or imapdata[0] == '0':
|
||||||
# Empty folder, no need to populate message list
|
# Empty folder, no need to populate message list
|
||||||
@ -162,9 +162,9 @@ class IMAPFolder(BaseFolder):
|
|||||||
# By default examine all messages in this folder
|
# By default examine all messages in this folder
|
||||||
msgsToFetch = '1:*'
|
msgsToFetch = '1:*'
|
||||||
|
|
||||||
maxage = self.config.getdefaultint("Account %s" % self.accountname,
|
maxage = self.config.getdefaultint("Account %s"% self.accountname,
|
||||||
"maxage", -1)
|
"maxage", -1)
|
||||||
maxsize = self.config.getdefaultint("Account %s" % self.accountname,
|
maxsize = self.config.getdefaultint("Account %s"% self.accountname,
|
||||||
"maxsize", -1)
|
"maxsize", -1)
|
||||||
|
|
||||||
# Build search condition
|
# Build search condition
|
||||||
@ -193,7 +193,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
res_type, res_data = imapobj.search(None, search_cond)
|
res_type, res_data = imapobj.search(None, search_cond)
|
||||||
if res_type != 'OK':
|
if res_type != 'OK':
|
||||||
raise OfflineImapError("SEARCH in folder [%s]%s failed. "
|
raise OfflineImapError("SEARCH in folder [%s]%s failed. "
|
||||||
"Search string was '%s'. Server responded '[%s] %s'" % (
|
"Search string was '%s'. Server responded '[%s] %s'"% (
|
||||||
self.getrepository(), self, search_cond, res_type, res_data),
|
self.getrepository(), self, search_cond, res_type, res_data),
|
||||||
OfflineImapError.ERROR.FOLDER)
|
OfflineImapError.ERROR.FOLDER)
|
||||||
|
|
||||||
@ -219,11 +219,11 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
# Get the flags and UIDs for these. single-quotes prevent
|
# Get the flags and UIDs for these. single-quotes prevent
|
||||||
# imaplib2 from quoting the sequence.
|
# imaplib2 from quoting the sequence.
|
||||||
res_type, response = imapobj.fetch("'%s'" % msgsToFetch,
|
res_type, response = imapobj.fetch("'%s'"%
|
||||||
'(FLAGS UID)')
|
msgsToFetch, '(FLAGS UID)')
|
||||||
if res_type != 'OK':
|
if res_type != 'OK':
|
||||||
raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "
|
raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "
|
||||||
"Server responded '[%s] %s'" % (
|
"Server responded '[%s] %s'"% (
|
||||||
self.getrepository(), self,
|
self.getrepository(), self,
|
||||||
res_type, response),
|
res_type, response),
|
||||||
OfflineImapError.ERROR.FOLDER)
|
OfflineImapError.ERROR.FOLDER)
|
||||||
@ -238,7 +238,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
messagestr = messagestr.split(' ', 1)[1]
|
messagestr = messagestr.split(' ', 1)[1]
|
||||||
options = imaputil.flags2hash(messagestr)
|
options = imaputil.flags2hash(messagestr)
|
||||||
if not 'UID' in options:
|
if not 'UID' in options:
|
||||||
self.ui.warn('No UID in message with options %s' %\
|
self.ui.warn('No UID in message with options %s'% \
|
||||||
str(options),
|
str(options),
|
||||||
minor = 1)
|
minor = 1)
|
||||||
else:
|
else:
|
||||||
@ -255,16 +255,15 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
# Interface from BaseFolder
|
# Interface from BaseFolder
|
||||||
def getmessage(self, uid):
|
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'.
|
After this function all CRLFs will be transformed to '\n'.
|
||||||
|
|
||||||
:returns: the message body or throws and OfflineImapError
|
:returns: the message body or throws and OfflineImapError
|
||||||
(probably severity MESSAGE) if e.g. no message with
|
(probably severity MESSAGE) if e.g. no message with
|
||||||
this UID could be found.
|
this UID could be found.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
try:
|
try:
|
||||||
data = self._fetch_from_imap(imapobj, str(uid), 2)
|
data = self._fetch_from_imap(imapobj, str(uid), 2)
|
||||||
@ -281,7 +280,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
else:
|
else:
|
||||||
dbg_output = data
|
dbg_output = data
|
||||||
|
|
||||||
self.ui.debug('imap', "Returned object from fetching %d: '%s'" %
|
self.ui.debug('imap', "Returned object from fetching %d: '%s'"%
|
||||||
(uid, dbg_output))
|
(uid, dbg_output))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -307,6 +306,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
headername == 'X-OfflineIMAP' and headervalue will be a
|
headername == 'X-OfflineIMAP' and headervalue will be a
|
||||||
random string
|
random string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
headername = 'X-OfflineIMAP'
|
headername = 'X-OfflineIMAP'
|
||||||
# We need a random component too. If we ever upload the same
|
# We need a random component too. If we ever upload the same
|
||||||
# mail twice (e.g. in different folders), we would still need to
|
# mail twice (e.g. in different folders), we would still need to
|
||||||
@ -322,20 +322,20 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
|
|
||||||
def __savemessage_searchforheader(self, imapobj, headername, headervalue):
|
def __savemessage_searchforheader(self, imapobj, headername, headervalue):
|
||||||
self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s' % \
|
self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s'% \
|
||||||
(headername, headervalue))
|
(headername, headervalue))
|
||||||
# Now find the UID it got.
|
# Now find the UID it got.
|
||||||
headervalue = imapobj._quote(headervalue)
|
headervalue = imapobj._quote(headervalue)
|
||||||
try:
|
try:
|
||||||
matchinguids = imapobj.uid('search', 'HEADER', headername, headervalue)[1][0]
|
matchinguids = imapobj.uid('search', 'HEADER', headername, headervalue)[1][0]
|
||||||
except imapobj.error as err:
|
except imapobj.error as err:
|
||||||
# IMAP server doesn't implement search or had a problem.
|
# IMAP server doesn't implement search or had a problem.
|
||||||
self.ui.debug('imap', "__savemessage_searchforheader: got IMAP error '%s' while attempting to UID SEARCH for message with header %s" % (err, headername))
|
self.ui.debug('imap', "__savemessage_searchforheader: got IMAP error '%s' while attempting to UID SEARCH for message with header %s"% (err, headername))
|
||||||
return 0
|
return 0
|
||||||
self.ui.debug('imap', '__savemessage_searchforheader got initial matchinguids: ' + repr(matchinguids))
|
self.ui.debug('imap', '__savemessage_searchforheader got initial matchinguids: ' + repr(matchinguids))
|
||||||
|
|
||||||
if matchinguids == '':
|
if matchinguids == '':
|
||||||
self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH for message with header %s yielded no results" % headername)
|
self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH for message with header %s yielded no results"% headername)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
matchinguids = matchinguids.split(' ')
|
matchinguids = matchinguids.split(' ')
|
||||||
@ -343,7 +343,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
repr(matchinguids))
|
repr(matchinguids))
|
||||||
if len(matchinguids) != 1 or matchinguids[0] == None:
|
if len(matchinguids) != 1 or matchinguids[0] == None:
|
||||||
raise ValueError("While attempting to find UID for message with "
|
raise ValueError("While attempting to find UID for message with "
|
||||||
"header %s, got wrong-sized matchinguids of %s" %\
|
"header %s, got wrong-sized matchinguids of %s"%\
|
||||||
(headername, str(matchinguids)))
|
(headername, str(matchinguids)))
|
||||||
return long(matchinguids[0])
|
return long(matchinguids[0])
|
||||||
|
|
||||||
@ -368,9 +368,9 @@ class IMAPFolder(BaseFolder):
|
|||||||
We need to locate the UID just after mail headers containing our
|
We need to locate the UID just after mail headers containing our
|
||||||
X-OfflineIMAP line.
|
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' % \
|
self.ui.debug('imap', '__savemessage_fetchheaders called for %s: %s'% \
|
||||||
(headername, headervalue))
|
(headername, headervalue))
|
||||||
|
|
||||||
# run "fetch X:* rfc822.header"
|
# run "fetch X:* rfc822.header"
|
||||||
@ -381,7 +381,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
# ascending.
|
# ascending.
|
||||||
|
|
||||||
if self.getmessagelist():
|
if self.getmessagelist():
|
||||||
start = 1+max(self.getmessagelist().keys())
|
start = 1 + max(self.getmessagelist().keys())
|
||||||
else:
|
else:
|
||||||
# Folder was empty - start from 1
|
# Folder was empty - start from 1
|
||||||
start = 1
|
start = 1
|
||||||
@ -390,7 +390,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
# with the range X:*. So we use bytearray to stop imaplib from getting
|
# with the range X:*. So we use bytearray to stop imaplib from getting
|
||||||
# in our way
|
# in our way
|
||||||
|
|
||||||
result = imapobj.uid('FETCH', bytearray('%d:*' % start), 'rfc822.header')
|
result = imapobj.uid('FETCH', bytearray('%d:*'% start), 'rfc822.header')
|
||||||
if result[0] != 'OK':
|
if result[0] != 'OK':
|
||||||
raise OfflineImapError('Error fetching mail headers: ' + '. '.join(result[1]),
|
raise OfflineImapError('Error fetching mail headers: ' + '. '.join(result[1]),
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
OfflineImapError.ERROR.MESSAGE)
|
||||||
@ -401,7 +401,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
for item in result:
|
for item in result:
|
||||||
if found == 0 and type(item) == type( () ):
|
if found == 0 and type(item) == type( () ):
|
||||||
# Walk just tuples
|
# Walk just tuples
|
||||||
if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)" % (headername, headervalue),
|
if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)"% (headername, headervalue),
|
||||||
item[1], flags=re.IGNORECASE):
|
item[1], flags=re.IGNORECASE):
|
||||||
found = 1
|
found = 1
|
||||||
elif found == 1:
|
elif found == 1:
|
||||||
@ -467,8 +467,8 @@ class IMAPFolder(BaseFolder):
|
|||||||
# or something. Argh. It seems that Time2Internaldate
|
# or something. Argh. It seems that Time2Internaldate
|
||||||
# will rause a ValueError if the year is 0102 but not 1902,
|
# will rause a ValueError if the year is 0102 but not 1902,
|
||||||
# but some IMAP servers nonetheless choke on 1902.
|
# but some IMAP servers nonetheless choke on 1902.
|
||||||
self.ui.debug('imap', "Message with invalid date %s. Server will use local time." \
|
self.ui.debug('imap', "Message with invalid date %s. "
|
||||||
% datetuple)
|
"Server will use local time."% datetuple)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
#produce a string representation of datetuple that works as
|
#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
|
message is saved, but it's UID can not be found, it will
|
||||||
return 0. If the message can't be written (folder is
|
return 0. If the message can't be written (folder is
|
||||||
read-only for example) it will return -1."""
|
read-only for example) it will return -1."""
|
||||||
|
|
||||||
self.ui.savemessage('imap', uid, flags, self)
|
self.ui.savemessage('imap', uid, flags, self)
|
||||||
|
|
||||||
# already have it, just save modified flags
|
# already have it, just save modified flags
|
||||||
@ -543,17 +544,17 @@ class IMAPFolder(BaseFolder):
|
|||||||
if not use_uidplus:
|
if not use_uidplus:
|
||||||
# insert a random unique header that we can fetch later
|
# insert a random unique header that we can fetch later
|
||||||
(headername, headervalue) = self.__generate_randomheader(
|
(headername, headervalue) = self.__generate_randomheader(
|
||||||
content)
|
content)
|
||||||
self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
|
self.ui.debug('imap', 'savemessage: header is: %s: %s'%
|
||||||
(headername, headervalue))
|
(headername, headervalue))
|
||||||
content = self.addmessageheader(content, CRLF, headername, headervalue)
|
content = self.addmessageheader(content, CRLF, headername, headervalue)
|
||||||
|
|
||||||
if len(content)>200:
|
if len(content)>200:
|
||||||
dbg_output = "%s...%s" % (content[:150], content[-50:])
|
dbg_output = "%s...%s" % (content[:150], content[-50:])
|
||||||
else:
|
else:
|
||||||
dbg_output = content
|
dbg_output = content
|
||||||
self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
|
self.ui.debug('imap', "savemessage: date: %s, content: '%s'"%
|
||||||
(date, dbg_output))
|
(date, dbg_output))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Select folder for append and make the box READ-WRITE
|
# Select folder for append and make the box READ-WRITE
|
||||||
@ -566,9 +567,8 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
#Do the APPEND
|
#Do the APPEND
|
||||||
try:
|
try:
|
||||||
(typ, dat) = imapobj.append(self.getfullname(),
|
(typ, dat) = imapobj.append(fullname,
|
||||||
imaputil.flagsmaildir2imap(flags),
|
imaputil.flagsmaildir2imap(flags), date, content)
|
||||||
date, content)
|
|
||||||
# This should only catch 'NO' responses since append()
|
# This should only catch 'NO' responses since append()
|
||||||
# will raise an exception for 'BAD' responses:
|
# will raise an exception for 'BAD' responses:
|
||||||
if typ != 'OK':
|
if typ != 'OK':
|
||||||
@ -580,7 +580,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
# and continue with the next account.
|
# and continue with the next account.
|
||||||
msg = \
|
msg = \
|
||||||
"Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
|
"Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
|
||||||
"Server responded: %s %s\n" % \
|
"Server responded: %s %s\n"% \
|
||||||
(msg_id, self, self.getrepository(), typ, dat)
|
(msg_id, self, self.getrepository(), typ, dat)
|
||||||
raise OfflineImapError(msg, OfflineImapError.ERROR.REPO)
|
raise OfflineImapError(msg, OfflineImapError.ERROR.REPO)
|
||||||
retry_left = 0 # Mark as success
|
retry_left = 0 # Mark as success
|
||||||
@ -592,7 +592,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
if not retry_left:
|
if not retry_left:
|
||||||
raise OfflineImapError("Saving msg (%s) in folder '%s', "
|
raise OfflineImapError("Saving msg (%s) in folder '%s', "
|
||||||
"repository '%s' failed (abort). Server responded: %s\n"
|
"repository '%s' failed (abort). Server responded: %s\n"
|
||||||
"Message content was: %s" %
|
"Message content was: %s"%
|
||||||
(msg_id, self, self.getrepository(), str(e), dbg_output),
|
(msg_id, self, self.getrepository(), str(e), dbg_output),
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
OfflineImapError.ERROR.MESSAGE)
|
||||||
self.ui.error(e, exc_info()[2])
|
self.ui.error(e, exc_info()[2])
|
||||||
@ -604,8 +604,8 @@ class IMAPFolder(BaseFolder):
|
|||||||
imapobj = None
|
imapobj = None
|
||||||
raise OfflineImapError("Saving msg (%s) folder '%s', repo '%s'"
|
raise OfflineImapError("Saving msg (%s) folder '%s', repo '%s'"
|
||||||
"failed (error). Server responded: %s\nMessage content was: "
|
"failed (error). Server responded: %s\nMessage content was: "
|
||||||
"%s" % (msg_id, self, self.getrepository(), str(e), dbg_output),
|
"%s"% (msg_id, self, self.getrepository(), str(e), dbg_output),
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
OfflineImapError.ERROR.MESSAGE)
|
||||||
# Checkpoint. Let it write out stuff, etc. Eg searches for
|
# Checkpoint. Let it write out stuff, etc. Eg searches for
|
||||||
# just uploaded messages won't work if we don't do this.
|
# just uploaded messages won't work if we don't do this.
|
||||||
(typ,dat) = imapobj.check()
|
(typ,dat) = imapobj.check()
|
||||||
@ -622,24 +622,24 @@ class IMAPFolder(BaseFolder):
|
|||||||
resp = imapobj._get_untagged_response('APPENDUID')
|
resp = imapobj._get_untagged_response('APPENDUID')
|
||||||
if resp == [None] or resp is None:
|
if resp == [None] or resp is None:
|
||||||
self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
|
self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
|
||||||
"appending a message.")
|
"appending a message.")
|
||||||
return 0
|
return 0
|
||||||
uid = long(resp[-1].split(' ')[1])
|
uid = long(resp[-1].split(' ')[1])
|
||||||
if uid == 0:
|
if uid == 0:
|
||||||
self.ui.warn("savemessage: Server supports UIDPLUS, but"
|
self.ui.warn("savemessage: Server supports UIDPLUS, but"
|
||||||
" we got no usable uid back. APPENDUID reponse was "
|
" we got no usable uid back. APPENDUID reponse was "
|
||||||
"'%s'" % str(resp))
|
"'%s'"% str(resp))
|
||||||
else:
|
else:
|
||||||
# we don't support UIDPLUS
|
# we don't support UIDPLUS
|
||||||
uid = self.__savemessage_searchforheader(imapobj, headername,
|
uid = self.__savemessage_searchforheader(imapobj, headername,
|
||||||
headervalue)
|
headervalue)
|
||||||
# See docs for savemessage in Base.py for explanation
|
# See docs for savemessage in Base.py for explanation
|
||||||
# of this and other return values
|
# of this and other return values
|
||||||
if uid == 0:
|
if uid == 0:
|
||||||
self.ui.debug('imap', 'savemessage: attempt to get new UID '
|
self.ui.debug('imap', 'savemessage: attempt to get new UID '
|
||||||
'UID failed. Search headers manually.')
|
'UID failed. Search headers manually.')
|
||||||
uid = self.__savemessage_fetchheaders(imapobj, headername,
|
uid = self.__savemessage_fetchheaders(imapobj, headername,
|
||||||
headervalue)
|
headervalue)
|
||||||
self.ui.warn('imap', "savemessage: Searching mails for new "
|
self.ui.warn('imap', "savemessage: Searching mails for new "
|
||||||
"Message-ID failed. Could not determine new UID.")
|
"Message-ID failed. Could not determine new UID.")
|
||||||
finally:
|
finally:
|
||||||
@ -649,22 +649,21 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.messagelist[uid] = self.msglist_item_initializer(uid)
|
self.messagelist[uid] = self.msglist_item_initializer(uid)
|
||||||
self.messagelist[uid]['flags'] = flags
|
self.messagelist[uid]['flags'] = flags
|
||||||
|
|
||||||
self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
|
self.ui.debug('imap', 'savemessage: returning new UID %d'% uid)
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
|
||||||
def _fetch_from_imap(self, imapobj, uids, retry_num=1):
|
def _fetch_from_imap(self, imapobj, uids, retry_num=1):
|
||||||
"""
|
"""Fetches data from IMAP server.
|
||||||
Fetches data from IMAP server.
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- imapobj: IMAPlib object
|
- imapobj: IMAPlib object
|
||||||
- uids: message UIDS
|
- uids: message UIDS
|
||||||
- retry_num: number of retries to make
|
- 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))
|
query = "(%s)"% (" ".join(self.imap_query))
|
||||||
fails_left = retry_num # retry on dropped connection
|
fails_left = retry_num # retry on dropped connection
|
||||||
while fails_left:
|
while fails_left:
|
||||||
try:
|
try:
|
||||||
@ -683,7 +682,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
#IMAP server says bad request or UID does not exist
|
#IMAP server says bad request or UID does not exist
|
||||||
severity = OfflineImapError.ERROR.MESSAGE
|
severity = OfflineImapError.ERROR.MESSAGE
|
||||||
reason = "IMAP server '%s' failed to fetch messages UID '%s'."\
|
reason = "IMAP server '%s' failed to fetch messages UID '%s'."\
|
||||||
"Server responded: %s %s" % (self.getrepository(), uids,
|
"Server responded: %s %s"% (self.getrepository(), uids,
|
||||||
res_type, data)
|
res_type, data)
|
||||||
if data == [None]:
|
if data == [None]:
|
||||||
#IMAP server did not find a message with this UID
|
#IMAP server did not find a message with this UID
|
||||||
@ -695,23 +694,21 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
|
|
||||||
def _store_to_imap(self, imapobj, uid, field, data):
|
def _store_to_imap(self, imapobj, uid, field, data):
|
||||||
"""
|
"""Stores data to IMAP server
|
||||||
Stores data to IMAP server
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- imapobj: instance of IMAPlib to use
|
- imapobj: instance of IMAPlib to use
|
||||||
- uid: message UID
|
- uid: message UID
|
||||||
- field: field name to be stored/updated
|
- field: field name to be stored/updated
|
||||||
- data: field contents
|
- data: field contents
|
||||||
|
|
||||||
"""
|
"""
|
||||||
imapobj.select(self.getfullname())
|
imapobj.select(self.getfullname())
|
||||||
res_type, retdata = imapobj.uid('store', uid, field, data)
|
res_type, retdata = imapobj.uid('store', uid, field, data)
|
||||||
if res_type != 'OK':
|
if res_type != 'OK':
|
||||||
severity = OfflineImapError.ERROR.MESSAGE
|
severity = OfflineImapError.ERROR.MESSAGE
|
||||||
reason = "IMAP server '%s' failed to store %s for message UID '%d'."\
|
reason = "IMAP server '%s' failed to store %s for message UID '%d'."\
|
||||||
"Server responded: %s %s" % (self.getrepository(), field, uid,
|
"Server responded: %s %s"% (
|
||||||
res_type, retdata)
|
self.getrepository(), field, uid, res_type, retdata)
|
||||||
raise OfflineImapError(reason, severity)
|
raise OfflineImapError(reason, severity)
|
||||||
return retdata[0]
|
return retdata[0]
|
||||||
|
|
||||||
@ -724,12 +721,11 @@ class IMAPFolder(BaseFolder):
|
|||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
try:
|
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:
|
except imapobj.readonly:
|
||||||
self.ui.flagstoreadonly(self, [uid], flags)
|
self.ui.flagstoreadonly(self, [uid], flags)
|
||||||
return
|
return
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.imapserver.releaseconnection(imapobj)
|
self.imapserver.releaseconnection(imapobj)
|
||||||
|
|
||||||
@ -751,6 +747,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
"""This is here for the sake of UIDMaps.py -- deletemessages must
|
"""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,
|
add flags and get a converted UID, and if we don't have noconvert,
|
||||||
then UIDMaps will try to convert it twice."""
|
then UIDMaps will try to convert it twice."""
|
||||||
|
|
||||||
self.__addmessagesflags_noconvert(uidlist, flags)
|
self.__addmessagesflags_noconvert(uidlist, flags)
|
||||||
|
|
||||||
# Interface from BaseFolder
|
# Interface from BaseFolder
|
||||||
@ -770,9 +767,8 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.ui.flagstoreadonly(self, uidlist, flags)
|
self.ui.flagstoreadonly(self, uidlist, flags)
|
||||||
return
|
return
|
||||||
r = imapobj.uid('store',
|
r = imapobj.uid('store',
|
||||||
imaputil.uid_sequence(uidlist),
|
imaputil.uid_sequence(uidlist), operation + 'FLAGS',
|
||||||
operation + 'FLAGS',
|
imaputil.flagsmaildir2imap(flags))
|
||||||
imaputil.flagsmaildir2imap(flags))
|
|
||||||
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
|
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
|
||||||
r = r[1]
|
r = r[1]
|
||||||
finally:
|
finally:
|
||||||
@ -818,9 +814,9 @@ class IMAPFolder(BaseFolder):
|
|||||||
"""Change the message from existing uid to new_uid
|
"""Change the message from existing uid to new_uid
|
||||||
|
|
||||||
If the backend supports it. IMAP does not and will throw errors."""
|
If the backend supports it. IMAP does not and will throw errors."""
|
||||||
|
|
||||||
raise OfflineImapError('IMAP backend cannot change a messages UID from '
|
raise OfflineImapError('IMAP backend cannot change a messages UID from '
|
||||||
'%d to %d' % (uid, new_uid),
|
'%d to %d'% (uid, new_uid), OfflineImapError.ERROR.MESSAGE)
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
|
||||||
|
|
||||||
# Interface from BaseFolder
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
|
@ -56,7 +56,7 @@ class IMAPServer:
|
|||||||
self.preauth_tunnel = repos.getpreauthtunnel()
|
self.preauth_tunnel = repos.getpreauthtunnel()
|
||||||
self.transport_tunnel = repos.gettransporttunnel()
|
self.transport_tunnel = repos.gettransporttunnel()
|
||||||
if self.preauth_tunnel and self.transport_tunnel:
|
if self.preauth_tunnel and self.transport_tunnel:
|
||||||
raise OfflineImapError('%s: ' % repos + \
|
raise OfflineImapError('%s: '% repos + \
|
||||||
'you must enable precisely one '
|
'you must enable precisely one '
|
||||||
'type of tunnel (preauth or transport), '
|
'type of tunnel (preauth or transport), '
|
||||||
'not both', OfflineImapError.ERROR.REPO)
|
'not both', OfflineImapError.ERROR.REPO)
|
||||||
@ -116,8 +116,9 @@ class IMAPServer:
|
|||||||
|
|
||||||
# XXX: is this function used anywhere?
|
# XXX: is this function used anywhere?
|
||||||
def getroot(self):
|
def getroot(self):
|
||||||
"""Returns this server's folder root. Can only be called after one
|
"""Returns this server's folder root. Can only be called after one
|
||||||
or more calls to acquireconnection."""
|
or more calls to acquireconnection."""
|
||||||
|
|
||||||
return self.root
|
return self.root
|
||||||
|
|
||||||
|
|
||||||
@ -126,6 +127,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
:param drop_conn: If True, the connection will be released and
|
:param drop_conn: If True, the connection will be released and
|
||||||
not be reused. This can be used to indicate broken connections."""
|
not be reused. This can be used to indicate broken connections."""
|
||||||
|
|
||||||
if connection is None: return #noop on bad connection
|
if connection is None: return #noop on bad connection
|
||||||
self.connectionlock.acquire()
|
self.connectionlock.acquire()
|
||||||
self.assignedconnections.remove(connection)
|
self.assignedconnections.remove(connection)
|
||||||
@ -139,25 +141,24 @@ class IMAPServer:
|
|||||||
|
|
||||||
def __md5handler(self, response):
|
def __md5handler(self, response):
|
||||||
challenge = response.strip()
|
challenge = response.strip()
|
||||||
self.ui.debug('imap', '__md5handler: got challenge %s' % challenge)
|
self.ui.debug('imap', '__md5handler: got challenge %s'% challenge)
|
||||||
|
|
||||||
passwd = self.__getpassword()
|
passwd = self.__getpassword()
|
||||||
retval = self.username + ' ' + hmac.new(passwd, challenge).hexdigest()
|
retval = self.username + ' ' + hmac.new(passwd, challenge).hexdigest()
|
||||||
self.ui.debug('imap', '__md5handler: returning %s' % retval)
|
self.ui.debug('imap', '__md5handler: returning %s'% retval)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def __loginauth(self, imapobj):
|
def __loginauth(self, imapobj):
|
||||||
""" Basic authentication via LOGIN command """
|
""" Basic authentication via LOGIN command."""
|
||||||
|
|
||||||
self.ui.debug('imap', 'Attempting IMAP LOGIN authentication')
|
self.ui.debug('imap', 'Attempting IMAP LOGIN authentication')
|
||||||
imapobj.login(self.username, self.__getpassword())
|
imapobj.login(self.username, self.__getpassword())
|
||||||
|
|
||||||
|
|
||||||
def __plainhandler(self, response):
|
def __plainhandler(self, response):
|
||||||
"""
|
"""Implements SASL PLAIN authentication, RFC 4616,
|
||||||
Implements SASL PLAIN authentication, RFC 4616,
|
http://tools.ietf.org/html/rfc4616"""
|
||||||
http://tools.ietf.org/html/rfc4616
|
|
||||||
|
|
||||||
"""
|
|
||||||
authc = self.username
|
authc = self.username
|
||||||
passwd = self.__getpassword()
|
passwd = self.__getpassword()
|
||||||
authz = ''
|
authz = ''
|
||||||
@ -175,8 +176,8 @@ class IMAPServer:
|
|||||||
try:
|
try:
|
||||||
if self.gss_step == self.GSS_STATE_STEP:
|
if self.gss_step == self.GSS_STATE_STEP:
|
||||||
if not self.gss_vc:
|
if not self.gss_vc:
|
||||||
rc, self.gss_vc = kerberos.authGSSClientInit('imap@' +
|
rc, self.gss_vc = kerberos.authGSSClientInit(
|
||||||
self.hostname)
|
'imap@' + self.hostname)
|
||||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||||
rc = kerberos.authGSSClientStep(self.gss_vc, data)
|
rc = kerberos.authGSSClientStep(self.gss_vc, data)
|
||||||
if rc != kerberos.AUTH_GSS_CONTINUE:
|
if rc != kerberos.AUTH_GSS_CONTINUE:
|
||||||
@ -184,13 +185,13 @@ class IMAPServer:
|
|||||||
elif self.gss_step == self.GSS_STATE_WRAP:
|
elif self.gss_step == self.GSS_STATE_WRAP:
|
||||||
rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
|
rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
|
||||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||||
rc = kerberos.authGSSClientWrap(self.gss_vc, response,
|
rc = kerberos.authGSSClientWrap(
|
||||||
self.username)
|
self.gss_vc, response, self.username)
|
||||||
response = kerberos.authGSSClientResponse(self.gss_vc)
|
response = kerberos.authGSSClientResponse(self.gss_vc)
|
||||||
except kerberos.GSSError as err:
|
except kerberos.GSSError as err:
|
||||||
# Kerberos errored out on us, respond with None to cancel the
|
# Kerberos errored out on us, respond with None to cancel the
|
||||||
# authentication
|
# authentication
|
||||||
self.ui.debug('imap', '%s: %s' % (err[0][0], err[1][0]))
|
self.ui.debug('imap', '%s: %s'% (err[0][0], err[1][0]))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
@ -205,7 +206,7 @@ class IMAPServer:
|
|||||||
imapobj.starttls()
|
imapobj.starttls()
|
||||||
except imapobj.error as e:
|
except imapobj.error as e:
|
||||||
raise OfflineImapError("Failed to start "
|
raise OfflineImapError("Failed to start "
|
||||||
"TLS connection: %s" % str(e),
|
"TLS connection: %s"% str(e),
|
||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
|
|
||||||
@ -266,8 +267,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
|
|
||||||
def __authn_helper(self, imapobj):
|
def __authn_helper(self, imapobj):
|
||||||
"""
|
"""Authentication machinery for self.acquireconnection().
|
||||||
Authentication machinery for self.acquireconnection().
|
|
||||||
|
|
||||||
Raises OfflineImapError() of type ERROR.REPO when
|
Raises OfflineImapError() of type ERROR.REPO when
|
||||||
there are either fatal problems or no authentications
|
there are either fatal problems or no authentications
|
||||||
@ -275,9 +275,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
If any authentication method succeeds, routine should exit:
|
If any authentication method succeeds, routine should exit:
|
||||||
warnings for failed methods are to be produced in the
|
warnings for failed methods are to be produced in the
|
||||||
respective except blocks.
|
respective except blocks."""
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Authentication routines, hash keyed by method name
|
# Authentication routines, hash keyed by method name
|
||||||
# with value that is a tuple with
|
# with value that is a tuple with
|
||||||
@ -321,13 +319,13 @@ class IMAPServer:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
tried_to_authn = True
|
tried_to_authn = True
|
||||||
self.ui.debug('imap', 'Attempting '
|
self.ui.debug('imap', u'Attempting '
|
||||||
'%s authentication' % m)
|
'%s authentication'% m)
|
||||||
try:
|
try:
|
||||||
if func(imapobj):
|
if func(imapobj):
|
||||||
return
|
return
|
||||||
except (imapobj.error, OfflineImapError) as e:
|
except (imapobj.error, OfflineImapError) as e:
|
||||||
self.ui.warn('%s authentication failed: %s' % (m, e))
|
self.ui.warn('%s authentication failed: %s'% (m, e))
|
||||||
exc_stack.append((m, e))
|
exc_stack.append((m, e))
|
||||||
|
|
||||||
if len(exc_stack):
|
if len(exc_stack):
|
||||||
@ -343,9 +341,9 @@ class IMAPServer:
|
|||||||
lambda x: x[5:], filter(lambda x: x[0:5] == "AUTH=",
|
lambda x: x[5:], filter(lambda x: x[0:5] == "AUTH=",
|
||||||
imapobj.capabilities)
|
imapobj.capabilities)
|
||||||
))
|
))
|
||||||
raise OfflineImapError("Repository %s: no supported "
|
raise OfflineImapError(u"Repository %s: no supported "
|
||||||
"authentication mechanisms found; configured %s, "
|
"authentication mechanisms found; configured %s, "
|
||||||
"server advertises %s" % (self.repos,
|
"server advertises %s"% (self.repos,
|
||||||
", ".join(self.authmechs), methods),
|
", ".join(self.authmechs), methods),
|
||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
@ -383,9 +381,8 @@ class IMAPServer:
|
|||||||
|
|
||||||
self.connectionlock.release() # Release until need to modify data
|
self.connectionlock.release() # Release until need to modify data
|
||||||
|
|
||||||
""" Must be careful here that if we fail we should bail out gracefully
|
# Must be careful here that if we fail we should bail out gracefully
|
||||||
and release locks / threads so that the next attempt can try...
|
# and release locks / threads so that the next attempt can try...
|
||||||
"""
|
|
||||||
success = 0
|
success = 0
|
||||||
try:
|
try:
|
||||||
while not success:
|
while not success:
|
||||||
@ -441,7 +438,7 @@ class IMAPServer:
|
|||||||
# No Folders were returned. This occurs, e.g. if the
|
# No Folders were returned. This occurs, e.g. if the
|
||||||
# 'reference' prefix does not exist on the mail
|
# 'reference' prefix does not exist on the mail
|
||||||
# server. Raise exception.
|
# server. Raise exception.
|
||||||
err = "Server '%s' returned no folders in '%s'" % \
|
err = "Server '%s' returned no folders in '%s'"% \
|
||||||
(self.repos.getname(), self.reference)
|
(self.repos.getname(), self.reference)
|
||||||
self.ui.warn(err)
|
self.ui.warn(err)
|
||||||
raise Exception(err)
|
raise Exception(err)
|
||||||
@ -458,6 +455,7 @@ class IMAPServer:
|
|||||||
"""If we are here then we did not succeed in getting a
|
"""If we are here then we did not succeed in getting a
|
||||||
connection - we should clean up and then re-raise the
|
connection - we should clean up and then re-raise the
|
||||||
error..."""
|
error..."""
|
||||||
|
|
||||||
self.semaphore.release()
|
self.semaphore.release()
|
||||||
|
|
||||||
severity = OfflineImapError.ERROR.REPO
|
severity = OfflineImapError.ERROR.REPO
|
||||||
@ -489,7 +487,7 @@ class IMAPServer:
|
|||||||
reason = "Connection to host '%s:%d' for repository '%s' was "\
|
reason = "Connection to host '%s:%d' for repository '%s' was "\
|
||||||
"refused. Make sure you have the right host and port "\
|
"refused. Make sure you have the right host and port "\
|
||||||
"configured and that you are actually able to access the "\
|
"configured and that you are actually able to access the "\
|
||||||
"network." % (self.hostname, self.port, self.repos)
|
"network."% (self.hostname, self.port, self.repos)
|
||||||
raise OfflineImapError(reason, severity)
|
raise OfflineImapError(reason, severity)
|
||||||
# Could not acquire connection to the remote;
|
# Could not acquire connection to the remote;
|
||||||
# socket.error(last_error) raised
|
# socket.error(last_error) raised
|
||||||
@ -503,13 +501,15 @@ class IMAPServer:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def connectionwait(self):
|
def connectionwait(self):
|
||||||
"""Waits until there is a connection available. Note that between
|
"""Waits until there is a connection available.
|
||||||
the time that a connection becomes available and the time it is
|
|
||||||
requested, another thread may have grabbed it. This function is
|
Note that between the time that a connection becomes available and the
|
||||||
mainly present as a way to avoid spawning thousands of threads
|
time it is requested, another thread may have grabbed it. This function
|
||||||
to copy messages, then have them all wait for 3 available connections.
|
is mainly present as a way to avoid spawning thousands of threads to
|
||||||
It's OK if we have maxconnections + 1 or 2 threads, which is what
|
copy messages, then have them all wait for 3 available connections.
|
||||||
this will help us do."""
|
It's OK if we have maxconnections + 1 or 2 threads, which is what this
|
||||||
|
will help us do."""
|
||||||
|
|
||||||
self.semaphore.acquire()
|
self.semaphore.acquire()
|
||||||
self.semaphore.release()
|
self.semaphore.release()
|
||||||
|
|
||||||
@ -533,11 +533,13 @@ class IMAPServer:
|
|||||||
self.gssapi = False
|
self.gssapi = False
|
||||||
|
|
||||||
def keepalive(self, timeout, event):
|
def keepalive(self, timeout, event):
|
||||||
"""Sends a NOOP to each connection recorded. It will wait a maximum
|
"""Sends a NOOP to each connection recorded.
|
||||||
of timeout seconds between doing this, and will continue to do so
|
|
||||||
until the Event object as passed is true. This method is expected
|
It will wait a maximum of timeout seconds between doing this, and will
|
||||||
to be invoked in a separate thread, which should be join()'d after
|
continue to do so until the Event object as passed is true. This method
|
||||||
the event is set."""
|
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')
|
self.ui.debug('imap', 'keepalive thread started')
|
||||||
while not event.isSet():
|
while not event.isSet():
|
||||||
self.connectionlock.acquire()
|
self.connectionlock.acquire()
|
||||||
@ -547,7 +549,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
threads = []
|
threads = []
|
||||||
for i in range(numconnections):
|
for i in range(numconnections):
|
||||||
self.ui.debug('imap', 'keepalive: processing connection %d of %d' % (i, numconnections))
|
self.ui.debug('imap', 'keepalive: processing connection %d of %d'% (i, numconnections))
|
||||||
if len(self.idlefolders) > i:
|
if len(self.idlefolders) > i:
|
||||||
# IDLE thread
|
# IDLE thread
|
||||||
idler = IdleThread(self, self.idlefolders[i])
|
idler = IdleThread(self, self.idlefolders[i])
|
||||||
@ -570,14 +572,14 @@ class IMAPServer:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def __verifycert(self, cert, hostname):
|
def __verifycert(self, cert, hostname):
|
||||||
'''Verify that cert (in socket.getpeercert() format) matches hostname.
|
"""Verify that cert (in socket.getpeercert() format) matches hostname.
|
||||||
CRLs are not handled.
|
|
||||||
|
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: "
|
errstr = "CA Cert verifying failed: "
|
||||||
if not cert:
|
if not cert:
|
||||||
return ('%s no certificate received' % errstr)
|
return ('%s no certificate received'% errstr)
|
||||||
dnsname = hostname.lower()
|
dnsname = hostname.lower()
|
||||||
certnames = []
|
certnames = []
|
||||||
|
|
||||||
@ -585,7 +587,7 @@ class IMAPServer:
|
|||||||
notafter = cert.get('notAfter')
|
notafter = cert.get('notAfter')
|
||||||
if notafter:
|
if notafter:
|
||||||
if time.time() >= cert_time_to_seconds(notafter):
|
if time.time() >= cert_time_to_seconds(notafter):
|
||||||
return '%s certificate expired %s' % (errstr, notafter)
|
return '%s certificate expired %s'% (errstr, notafter)
|
||||||
|
|
||||||
# First read commonName
|
# First read commonName
|
||||||
for s in cert.get('subject', []):
|
for s in cert.get('subject', []):
|
||||||
@ -593,7 +595,7 @@ class IMAPServer:
|
|||||||
if key == 'commonName':
|
if key == 'commonName':
|
||||||
certnames.append(value.lower())
|
certnames.append(value.lower())
|
||||||
if len(certnames) == 0:
|
if len(certnames) == 0:
|
||||||
return ('%s no commonName found in certificate' % errstr)
|
return ('%s no commonName found in certificate'% errstr)
|
||||||
|
|
||||||
# Then read subjectAltName
|
# Then read subjectAltName
|
||||||
for key, value in cert.get('subjectAltName', []):
|
for key, value in cert.get('subjectAltName', []):
|
||||||
@ -606,7 +608,7 @@ class IMAPServer:
|
|||||||
'.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
|
'.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return ('%s no matching domain name found in certificate' % errstr)
|
return ('%s no matching domain name found in certificate'% errstr)
|
||||||
|
|
||||||
|
|
||||||
class IdleThread(object):
|
class IdleThread(object):
|
||||||
@ -614,6 +616,7 @@ class IdleThread(object):
|
|||||||
"""If invoked without 'folder', perform a NOOP and wait for
|
"""If invoked without 'folder', perform a NOOP and wait for
|
||||||
self.stop() to be called. If invoked with folder, switch to IDLE
|
self.stop() to be called. If invoked with folder, switch to IDLE
|
||||||
mode and synchronize once we have a new message"""
|
mode and synchronize once we have a new message"""
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.folder = folder
|
self.folder = folder
|
||||||
self.stop_sig = Event()
|
self.stop_sig = Event()
|
||||||
@ -634,18 +637,18 @@ class IdleThread(object):
|
|||||||
self.thread.join()
|
self.thread.join()
|
||||||
|
|
||||||
def noop(self):
|
def noop(self):
|
||||||
#TODO: AFAIK this is not optimal, we will send a NOOP on one
|
# TODO: AFAIK this is not optimal, we will send a NOOP on one
|
||||||
#random connection (ie not enough to keep all connections
|
# random connection (ie not enough to keep all connections
|
||||||
#open). In case we do the noop multiple times, we can well use
|
# open). In case we do the noop multiple times, we can well use
|
||||||
#the same connection every time, as we get a random one. This
|
# the same connection every time, as we get a random one. This
|
||||||
#function should IMHO send a noop on ALL available connections
|
# function should IMHO send a noop on ALL available connections
|
||||||
#to the server.
|
# to the server.
|
||||||
imapobj = self.parent.acquireconnection()
|
imapobj = self.parent.acquireconnection()
|
||||||
try:
|
try:
|
||||||
imapobj.noop()
|
imapobj.noop()
|
||||||
except imapobj.abort:
|
except imapobj.abort:
|
||||||
self.ui.warn('Attempting NOOP on dropped connection %s' % \
|
self.ui.warn('Attempting NOOP on dropped connection %s'%
|
||||||
imapobj.identifier)
|
imapobj.identifier)
|
||||||
self.parent.releaseconnection(imapobj, True)
|
self.parent.releaseconnection(imapobj, True)
|
||||||
imapobj = None
|
imapobj = None
|
||||||
finally:
|
finally:
|
||||||
|
@ -121,8 +121,7 @@ def imapsplit(imapstring):
|
|||||||
arg = arg.replace('\\', '\\\\')
|
arg = arg.replace('\\', '\\\\')
|
||||||
arg = arg.replace('"', '\\"')
|
arg = arg.replace('"', '\\"')
|
||||||
arg = '"%s"' % arg
|
arg = '"%s"' % arg
|
||||||
__debug("imapsplit() non-string [%d]: Appending %s" %\
|
__debug("imapsplit() non-string [%d]: Appending %s"% (i, arg))
|
||||||
(i, arg))
|
|
||||||
retval.append(arg)
|
retval.append(arg)
|
||||||
else:
|
else:
|
||||||
# Even -- we have a string that ends with a literal
|
# Even -- we have a string that ends with a literal
|
||||||
@ -131,8 +130,8 @@ def imapsplit(imapstring):
|
|||||||
# Recursion to the rescue.
|
# Recursion to the rescue.
|
||||||
arg = imapstring[i]
|
arg = imapstring[i]
|
||||||
arg = re.sub('\{\d+\}$', '', arg)
|
arg = re.sub('\{\d+\}$', '', arg)
|
||||||
__debug("imapsplit() non-string [%d]: Feeding %s to recursion" %\
|
__debug("imapsplit() non-string [%d]: Feeding %s to recursion"%\
|
||||||
(i, arg))
|
(i, arg))
|
||||||
retval.extend(imapsplit(arg))
|
retval.extend(imapsplit(arg))
|
||||||
__debug("imapsplit() non-string: returning %s" % str(retval))
|
__debug("imapsplit() non-string: returning %s" % str(retval))
|
||||||
return retval
|
return retval
|
||||||
|
@ -23,6 +23,7 @@ import signal
|
|||||||
import socket
|
import socket
|
||||||
import logging
|
import logging
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
import offlineimap
|
import offlineimap
|
||||||
from offlineimap import accounts, threadutil, syncmaster
|
from offlineimap import accounts, threadutil, syncmaster
|
||||||
from offlineimap import globals
|
from offlineimap import globals
|
||||||
@ -180,7 +181,7 @@ class OfflineImap:
|
|||||||
config = CustomConfigParser()
|
config = CustomConfigParser()
|
||||||
if not os.path.exists(configfilename):
|
if not os.path.exists(configfilename):
|
||||||
# TODO, initialize and make use of chosen ui for logging
|
# TODO, initialize and make use of chosen ui for logging
|
||||||
logging.error(" *** Config file '%s' does not exist; aborting!" %
|
logging.error(" *** Config file '%s' does not exist; aborting!"%
|
||||||
configfilename)
|
configfilename)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
config.read(configfilename)
|
config.read(configfilename)
|
||||||
@ -193,14 +194,14 @@ class OfflineImap:
|
|||||||
options.singlethreading = True
|
options.singlethreading = True
|
||||||
if os.path.exists(options.profiledir):
|
if os.path.exists(options.profiledir):
|
||||||
# TODO, make use of chosen ui for logging
|
# TODO, make use of chosen ui for logging
|
||||||
logging.warn("Profile mode: Directory '%s' already exists!" %
|
logging.warn("Profile mode: Directory '%s' already exists!"%
|
||||||
options.profiledir)
|
options.profiledir)
|
||||||
else:
|
else:
|
||||||
os.mkdir(options.profiledir)
|
os.mkdir(options.profiledir)
|
||||||
threadutil.ExitNotifyThread.set_profiledir(options.profiledir)
|
threadutil.ExitNotifyThread.set_profiledir(options.profiledir)
|
||||||
# TODO, make use of chosen ui for logging
|
# TODO, make use of chosen ui for logging
|
||||||
logging.warn("Profile mode: Potentially large data will be "
|
logging.warn("Profile mode: Potentially large data will be "
|
||||||
"created in '%s'" % options.profiledir)
|
"created in '%s'"% options.profiledir)
|
||||||
|
|
||||||
#override a config value
|
#override a config value
|
||||||
if options.configoverride:
|
if options.configoverride:
|
||||||
@ -234,8 +235,8 @@ class OfflineImap:
|
|||||||
# create the ui class
|
# create the ui class
|
||||||
self.ui = UI_LIST[ui_type.lower()](config)
|
self.ui = UI_LIST[ui_type.lower()](config)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logging.error("UI '%s' does not exist, choose one of: %s" % \
|
logging.error("UI '%s' does not exist, choose one of: %s"% \
|
||||||
(ui_type,', '.join(UI_LIST.keys())))
|
(ui_type, ', '.join(UI_LIST.keys())))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
setglobalui(self.ui)
|
setglobalui(self.ui)
|
||||||
|
|
||||||
@ -331,13 +332,13 @@ class OfflineImap:
|
|||||||
for account in activeaccounts:
|
for account in activeaccounts:
|
||||||
if account not in allaccounts:
|
if account not in allaccounts:
|
||||||
if len(allaccounts) == 0:
|
if len(allaccounts) == 0:
|
||||||
errormsg = "The account '%s' does not exist because no"\
|
errormsg = "The account '%s' does not exist because no" \
|
||||||
" accounts are defined!" % account
|
" accounts are defined!"% account
|
||||||
else:
|
else:
|
||||||
errormsg = "The account '%s' does not exist. Valid ac"\
|
errormsg = "The account '%s' does not exist. Valid ac" \
|
||||||
"counts are: " % account
|
"counts are: %s"% \
|
||||||
errormsg += ", ".join(allaccounts.keys())
|
(account, ", ".join(allaccounts.keys()))
|
||||||
self.ui.terminate(1, errormsg = errormsg)
|
self.ui.terminate(1, errormsg=errormsg)
|
||||||
if account not in syncaccounts:
|
if account not in syncaccounts:
|
||||||
syncaccounts.append(account)
|
syncaccounts.append(account)
|
||||||
|
|
||||||
|
@ -23,9 +23,7 @@ except:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class LocalEval:
|
class LocalEval:
|
||||||
"""Here is a powerfull but very dangerous option, of course.
|
"""Here is a powerfull but very dangerous option, of course."""
|
||||||
|
|
||||||
Assume source file to be ASCII encoded."""
|
|
||||||
|
|
||||||
def __init__(self, path=None):
|
def __init__(self, path=None):
|
||||||
self.namespace = {}
|
self.namespace = {}
|
||||||
|
@ -167,6 +167,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
|
|||||||
that forward and backward nametrans actually match up!
|
that forward and backward nametrans actually match up!
|
||||||
Configuring nametrans on BOTH repositories therefore could lead
|
Configuring nametrans on BOTH repositories therefore could lead
|
||||||
to infinite folder creation cycles."""
|
to infinite folder creation cycles."""
|
||||||
|
|
||||||
if not self.get_create_folders() and not dst_repo.get_create_folders():
|
if not self.get_create_folders() and not dst_repo.get_create_folders():
|
||||||
# quick exit if no folder creation is enabled on either side.
|
# quick exit if no folder creation is enabled on either side.
|
||||||
return
|
return
|
||||||
|
@ -53,8 +53,8 @@ class LocalStatusRepository(BaseRepository):
|
|||||||
self.LocalStatusFolderClass = self.backends[backend]['class']
|
self.LocalStatusFolderClass = self.backends[backend]['class']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise SyntaxWarning("Unknown status_backend '%s' for account '%s'" \
|
raise SyntaxWarning("Unknown status_backend '%s' for account '%s'"%
|
||||||
% (backend, self.account.name))
|
(backend, self.account.name))
|
||||||
|
|
||||||
def import_other_backend(self, folder):
|
def import_other_backend(self, folder):
|
||||||
for bk, dic in self.backends.items():
|
for bk, dic in self.backends.items():
|
||||||
@ -68,8 +68,9 @@ class LocalStatusRepository(BaseRepository):
|
|||||||
|
|
||||||
# if backend contains data, import it to folder.
|
# if backend contains data, import it to folder.
|
||||||
if not folderbk.isnewfolder():
|
if not folderbk.isnewfolder():
|
||||||
self.ui._msg('Migrating LocalStatus cache from %s to %s ' % (bk, self._backend) + \
|
self.ui._msg('Migrating LocalStatus cache from %s to %s " \
|
||||||
'status folder for %s:%s' % (self.name, folder.name))
|
"status folder for %s:%s'%
|
||||||
|
(bk, self._backend, self.name, folder.name))
|
||||||
|
|
||||||
folderbk.cachemessagelist()
|
folderbk.cachemessagelist()
|
||||||
folder.messagelist = folderbk.messagelist
|
folder.messagelist = folderbk.messagelist
|
||||||
|
@ -32,7 +32,7 @@ class MaildirRepository(BaseRepository):
|
|||||||
self.root = self.getlocalroot()
|
self.root = self.getlocalroot()
|
||||||
self.folders = None
|
self.folders = None
|
||||||
self.ui = getglobalui()
|
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 = []
|
self.folder_atimes = []
|
||||||
|
|
||||||
# Create the top-level folder if it doesn't exist
|
# Create the top-level folder if it doesn't exist
|
||||||
@ -101,12 +101,12 @@ class MaildirRepository(BaseRepository):
|
|||||||
|
|
||||||
# If we're using hierarchical folders, it's possible that
|
# If we're using hierarchical folders, it's possible that
|
||||||
# sub-folders may be created before higher-up ones.
|
# sub-folders may be created before higher-up ones.
|
||||||
self.debug("makefolder: calling makedirs '%s'" % full_path)
|
self.debug("makefolder: calling makedirs '%s'"% full_path)
|
||||||
try:
|
try:
|
||||||
os.makedirs(full_path, 0o700)
|
os.makedirs(full_path, 0o700)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == 17 and os.path.isdir(full_path):
|
if e.errno == 17 and os.path.isdir(full_path):
|
||||||
self.debug("makefolder: '%s' already a directory" % foldername)
|
self.debug("makefolder: '%s' already a directory"% foldername)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
for subdir in ['cur', 'new', 'tmp']:
|
for subdir in ['cur', 'new', 'tmp']:
|
||||||
@ -114,13 +114,13 @@ class MaildirRepository(BaseRepository):
|
|||||||
os.mkdir(os.path.join(full_path, subdir), 0o700)
|
os.mkdir(os.path.join(full_path, subdir), 0o700)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == 17 and os.path.isdir(full_path):
|
if e.errno == 17 and os.path.isdir(full_path):
|
||||||
self.debug("makefolder: '%s' already has subdir %s" %
|
self.debug("makefolder: '%s' already has subdir %s"%
|
||||||
(foldername, subdir))
|
(foldername, subdir))
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def deletefolder(self, foldername):
|
def deletefolder(self, foldername):
|
||||||
self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s" % foldername)
|
self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s"% foldername)
|
||||||
|
|
||||||
def getfolder(self, foldername):
|
def getfolder(self, foldername):
|
||||||
"""Return a Folder instance of this Maildir
|
"""Return a Folder instance of this Maildir
|
||||||
@ -128,6 +128,7 @@ class MaildirRepository(BaseRepository):
|
|||||||
If necessary, scan and cache all foldernames to make sure that
|
If necessary, scan and cache all foldernames to make sure that
|
||||||
we only return existing folders and that 2 calls with the same
|
we only return existing folders and that 2 calls with the same
|
||||||
name will return the same object."""
|
name will return the same object."""
|
||||||
|
|
||||||
# getfolders() will scan and cache the values *if* necessary
|
# getfolders() will scan and cache the values *if* necessary
|
||||||
folders = self.getfolders()
|
folders = self.getfolders()
|
||||||
for f in folders:
|
for f in folders:
|
||||||
@ -142,8 +143,9 @@ class MaildirRepository(BaseRepository):
|
|||||||
|
|
||||||
:param root: (absolute) path to Maildir root
|
:param root: (absolute) path to Maildir root
|
||||||
:param extension: (relative) subfolder to examine within 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 = []
|
retval = []
|
||||||
|
|
||||||
# Configure the full path to this repository -- "toppath"
|
# Configure the full path to this repository -- "toppath"
|
||||||
@ -151,11 +153,11 @@ class MaildirRepository(BaseRepository):
|
|||||||
toppath = os.path.join(root, extension)
|
toppath = os.path.join(root, extension)
|
||||||
else:
|
else:
|
||||||
toppath = root
|
toppath = root
|
||||||
self.debug(" toppath = %s" % toppath)
|
self.debug(" toppath = %s"% toppath)
|
||||||
|
|
||||||
# Iterate over directories in top & top itself.
|
# Iterate over directories in top & top itself.
|
||||||
for dirname in os.listdir(toppath) + ['']:
|
for dirname in os.listdir(toppath) + ['']:
|
||||||
self.debug(" dirname = %s" % dirname)
|
self.debug(" dirname = %s"% dirname)
|
||||||
if dirname == '' and extension is not None:
|
if dirname == '' and extension is not None:
|
||||||
self.debug(' skip this entry (already scanned)')
|
self.debug(' skip this entry (already scanned)')
|
||||||
continue
|
continue
|
||||||
@ -178,7 +180,7 @@ class MaildirRepository(BaseRepository):
|
|||||||
os.path.isdir(os.path.join(fullname, 'new')) and
|
os.path.isdir(os.path.join(fullname, 'new')) and
|
||||||
os.path.isdir(os.path.join(fullname, 'tmp'))):
|
os.path.isdir(os.path.join(fullname, 'tmp'))):
|
||||||
# This directory has maildir stuff -- process
|
# This directory has maildir stuff -- process
|
||||||
self.debug(" This is maildir folder '%s'." % foldername)
|
self.debug(" This is maildir folder '%s'."% foldername)
|
||||||
if self.getconfboolean('restoreatime', False):
|
if self.getconfboolean('restoreatime', False):
|
||||||
self._append_folder_atimes(foldername)
|
self._append_folder_atimes(foldername)
|
||||||
fd = self.getfoldertype()(self.root, foldername,
|
fd = self.getfoldertype()(self.root, foldername,
|
||||||
@ -188,7 +190,7 @@ class MaildirRepository(BaseRepository):
|
|||||||
if self.getsep() == '/' and dirname != '':
|
if self.getsep() == '/' and dirname != '':
|
||||||
# Recursively check sub-directories for folders too.
|
# Recursively check sub-directories for folders too.
|
||||||
retval.extend(self._getfolders_scandir(root, foldername))
|
retval.extend(self._getfolders_scandir(root, foldername))
|
||||||
self.debug("_GETFOLDERS_SCANDIR RETURNING %s" % \
|
self.debug("_GETFOLDERS_SCANDIR RETURNING %s"% \
|
||||||
repr([x.getname() for x in retval]))
|
repr([x.getname() for x in retval]))
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from offlineimap import banner
|
|||||||
from offlineimap.ui.UIBase import UIBase
|
from offlineimap.ui.UIBase import UIBase
|
||||||
|
|
||||||
class TTYFormatter(logging.Formatter):
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
#super() doesn't work in py2.6 as 'logging' uses old-style class
|
#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
|
self._last_log_thread = None
|
||||||
|
|
||||||
def format(self, record):
|
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
|
#super() doesn't work in py2.6 as 'logging' uses old-style class
|
||||||
log_str = logging.Formatter.format(self, record)
|
log_str = logging.Formatter.format(self, record)
|
||||||
# If msg comes from a different thread than our last, prepend
|
# If msg comes from a different thread than our last, prepend
|
||||||
@ -44,7 +45,7 @@ class TTYFormatter(logging.Formatter):
|
|||||||
self._last_log_thread = t_name
|
self._last_log_thread = t_name
|
||||||
log_str = "%s:\n %s" % (t_name, log_str)
|
log_str = "%s:\n %s" % (t_name, log_str)
|
||||||
else:
|
else:
|
||||||
log_str = " %s" % log_str
|
log_str = " %s"% log_str
|
||||||
return log_str
|
return log_str
|
||||||
|
|
||||||
|
|
||||||
@ -69,15 +70,15 @@ class TTYUI(UIBase):
|
|||||||
return ch
|
return ch
|
||||||
|
|
||||||
def isusable(self):
|
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()
|
return sys.stdout.isatty() and sys.stdin.isatty()
|
||||||
|
|
||||||
def getpass(self, accountname, config, errmsg=None):
|
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:
|
if errmsg:
|
||||||
self.warn("%s: %s" % (accountname, errmsg))
|
self.warn("%s: %s"% (accountname, errmsg))
|
||||||
self._log_con_handler.acquire() # lock the console output
|
self._log_con_handler.acquire() # lock the console output
|
||||||
try:
|
try:
|
||||||
return getpass("Enter password for account '%s': " % accountname)
|
return getpass("Enter password for account '%s': " % accountname)
|
||||||
@ -87,7 +88,7 @@ class TTYUI(UIBase):
|
|||||||
def mainException(self):
|
def mainException(self):
|
||||||
if isinstance(sys.exc_info()[1], KeyboardInterrupt):
|
if isinstance(sys.exc_info()[1], KeyboardInterrupt):
|
||||||
self.logger.warn("Timer interrupted at user request; program "
|
self.logger.warn("Timer interrupted at user request; program "
|
||||||
"terminating.\n")
|
"terminating.\n")
|
||||||
self.terminate()
|
self.terminate()
|
||||||
else:
|
else:
|
||||||
UIBase.mainException(self)
|
UIBase.mainException(self)
|
||||||
@ -100,12 +101,11 @@ class TTYUI(UIBase):
|
|||||||
|
|
||||||
This implementation in UIBase does not support this, but some
|
This implementation in UIBase does not support this, but some
|
||||||
implementations return 0 for successful sleep and 1 for an
|
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 sleepsecs > 0:
|
||||||
if remainingsecs//60 != (remainingsecs-sleepsecs)//60:
|
if remainingsecs//60 != (remainingsecs-sleepsecs)//60:
|
||||||
self.logger.info("Next refresh in %.1f minutes" % (
|
self.logger.info("Next refresh in %.1f minutes" % (
|
||||||
remainingsecs/60.0))
|
remainingsecs/60.0))
|
||||||
time.sleep(sleepsecs)
|
time.sleep(sleepsecs)
|
||||||
return 0
|
return 0
|
||||||
|
@ -393,11 +393,11 @@ class UIBase(object):
|
|||||||
uid, dest, num, num_to_set, ", ".join(labels)))
|
uid, dest, num, num_to_set, ", ".join(labels)))
|
||||||
|
|
||||||
def collectingdata(self, uidlist, source):
|
def collectingdata(self, uidlist, source):
|
||||||
if uidlist:
|
if uidlist:
|
||||||
self.logger.info("Collecting data from %d messages on %s" % (
|
self.logger.info("Collecting data from %d messages on %s"% (
|
||||||
len(uidlist), source))
|
len(uidlist), source))
|
||||||
else:
|
else:
|
||||||
self.logger.info("Collecting data from messages on %s" % source)
|
self.logger.info("Collecting data from messages on %s"% source)
|
||||||
|
|
||||||
def serverdiagnostics(self, repository, type):
|
def serverdiagnostics(self, repository, type):
|
||||||
"""Connect to repository and output useful information for debugging."""
|
"""Connect to repository and output useful information for debugging."""
|
||||||
|
@ -28,7 +28,8 @@ class DebuggingLock:
|
|||||||
def acquire(self, blocking = 1):
|
def acquire(self, blocking = 1):
|
||||||
self.print_tb("Acquire lock")
|
self.print_tb("Acquire lock")
|
||||||
self.lock.acquire(blocking)
|
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):
|
def release(self):
|
||||||
self.print_tb("Release lock")
|
self.print_tb("Release lock")
|
||||||
@ -41,7 +42,7 @@ class DebuggingLock:
|
|||||||
loglock.release()
|
loglock.release()
|
||||||
|
|
||||||
def print_tb(self, msg):
|
def print_tb(self, msg):
|
||||||
self.logmsg(".... %s: Thread %s attempting to %s\n" % \
|
self.logmsg(".... %s: Thread %s attempting to %s\n"% \
|
||||||
(self.name, currentThread().getName(), msg) + \
|
(self.name, currentThread().getName(), msg) + \
|
||||||
"\n".join(traceback.format_list(traceback.extract_stack())))
|
"\n".join(traceback.format_list(traceback.extract_stack())))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user