Refactoring: make functions to be private if we can
Make external API of class/module to be smaller, explicitely mark all internal functions. Also annotate methods that are implemented as the part of the parent class interface. Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
This commit is contained in:
parent
af2d6dc5e1
commit
6cbd2498ae
@ -30,7 +30,7 @@ be merged into the main documentation.
|
|||||||
|
|
||||||
.. automethod:: run
|
.. automethod:: run
|
||||||
|
|
||||||
.. automethod:: parse_cmd_options
|
.. automethod:: __parse_cmd_options
|
||||||
|
|
||||||
.. .. autoattribute:: ui
|
.. .. autoattribute:: ui
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ class Account(CustomConfig.ConfigHelperMixin):
|
|||||||
return skipsleep or Account.abort_soon_signal.is_set() or \
|
return skipsleep or Account.abort_soon_signal.is_set() or \
|
||||||
Account.abort_NOW_signal.is_set()
|
Account.abort_NOW_signal.is_set()
|
||||||
|
|
||||||
def sleeper(self):
|
def _sleeper(self):
|
||||||
"""Sleep if the account is set to autorefresh
|
"""Sleep if the account is set to autorefresh
|
||||||
|
|
||||||
:returns: 0:timeout expired, 1: canceled the timer,
|
:returns: 0:timeout expired, 1: canceled the timer,
|
||||||
@ -192,7 +192,7 @@ class SyncableAccount(Account):
|
|||||||
self._lockfilepath = os.path.join(self.config.getmetadatadir(),
|
self._lockfilepath = os.path.join(self.config.getmetadatadir(),
|
||||||
"%s.lock" % self)
|
"%s.lock" % self)
|
||||||
|
|
||||||
def lock(self):
|
def __lock(self):
|
||||||
"""Lock the account, throwing an exception if it is locked already"""
|
"""Lock the account, throwing an exception if it is locked already"""
|
||||||
self._lockfd = open(self._lockfilepath, 'w')
|
self._lockfd = open(self._lockfilepath, 'w')
|
||||||
try:
|
try:
|
||||||
@ -206,7 +206,7 @@ class SyncableAccount(Account):
|
|||||||
"instance using this account?" % self,
|
"instance using this account?" % self,
|
||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
def unlock(self):
|
def _unlock(self):
|
||||||
"""Unlock the account, deleting the lock file"""
|
"""Unlock the account, deleting the lock file"""
|
||||||
#If we own the lock file, delete it
|
#If we own the lock file, delete it
|
||||||
if self._lockfd and not self._lockfd.closed:
|
if self._lockfd and not self._lockfd.closed:
|
||||||
@ -237,8 +237,8 @@ class SyncableAccount(Account):
|
|||||||
while looping:
|
while looping:
|
||||||
self.ui.acct(self)
|
self.ui.acct(self)
|
||||||
try:
|
try:
|
||||||
self.lock()
|
self.__lock()
|
||||||
self.sync()
|
self.__sync()
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except OfflineImapError as e:
|
except OfflineImapError as e:
|
||||||
@ -258,8 +258,8 @@ class SyncableAccount(Account):
|
|||||||
looping = 3
|
looping = 3
|
||||||
finally:
|
finally:
|
||||||
self.ui.acctdone(self)
|
self.ui.acctdone(self)
|
||||||
self.unlock()
|
self._unlock()
|
||||||
if looping and self.sleeper() >= 2:
|
if looping and self._sleeper() >= 2:
|
||||||
looping = 0
|
looping = 0
|
||||||
|
|
||||||
def get_local_folder(self, remotefolder):
|
def get_local_folder(self, remotefolder):
|
||||||
@ -268,7 +268,7 @@ class SyncableAccount(Account):
|
|||||||
remotefolder.getvisiblename().
|
remotefolder.getvisiblename().
|
||||||
replace(self.remoterepos.getsep(), self.localrepos.getsep()))
|
replace(self.remoterepos.getsep(), self.localrepos.getsep()))
|
||||||
|
|
||||||
def sync(self):
|
def __sync(self):
|
||||||
"""Synchronize the account once, then return
|
"""Synchronize the account once, then return
|
||||||
|
|
||||||
Assumes that `self.remoterepos`, `self.localrepos`, and
|
Assumes that `self.remoterepos`, `self.localrepos`, and
|
||||||
|
@ -96,6 +96,22 @@ class BaseFolder(object):
|
|||||||
false otherwise. Probably only IMAP will return true."""
|
false otherwise. Probably only IMAP will return true."""
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def waitforthread(self):
|
||||||
|
"""Implements method that waits for thread to be usable.
|
||||||
|
Should be implemented only for folders that suggest threads."""
|
||||||
|
raise NotImplementedException
|
||||||
|
|
||||||
|
# XXX: we may need someting like supports_quickstatus() to check
|
||||||
|
# XXX: if user specifies 'quick' flag for folder that doesn't
|
||||||
|
# XXX: support quick status queries, so one believes that quick
|
||||||
|
# XXX: status checks will be done, but it won't really be so.
|
||||||
|
def quickchanged(self, statusfolder):
|
||||||
|
""" Runs quick check for folder changes and returns changed
|
||||||
|
status: True -- changed, False -- not changed.
|
||||||
|
:param statusfolder: keeps track of the last known folder state.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def getcopyinstancelimit(self):
|
def getcopyinstancelimit(self):
|
||||||
"""For threading folders, returns the instancelimitname for
|
"""For threading folders, returns the instancelimitname for
|
||||||
InstanceLimitedThreads."""
|
InstanceLimitedThreads."""
|
||||||
@ -328,7 +344,7 @@ class BaseFolder(object):
|
|||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
self.deletemessage(uid)
|
self.deletemessage(uid)
|
||||||
|
|
||||||
def copymessageto(self, uid, dstfolder, statusfolder, register = 1):
|
def __copymessageto(self, uid, dstfolder, statusfolder, register = 1):
|
||||||
"""Copies a message from self to dst if needed, updating the status
|
"""Copies a message from self to dst if needed, updating the status
|
||||||
|
|
||||||
Note that this function does not check against dryrun settings,
|
Note that this function does not check against dryrun settings,
|
||||||
@ -403,14 +419,14 @@ class BaseFolder(object):
|
|||||||
(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):
|
||||||
"""Pass1: Copy locally existing messages not on the other side
|
"""Pass1: Copy locally existing messages not on the other side
|
||||||
|
|
||||||
This will copy messages to dstfolder that exist locally but are
|
This will copy messages to dstfolder that exist locally but are
|
||||||
not in the statusfolder yet. The strategy is:
|
not in the statusfolder yet. The strategy is:
|
||||||
|
|
||||||
1) Look for messages present in self but not in statusfolder.
|
1) Look for messages present in self but not in statusfolder.
|
||||||
2) invoke copymessageto() on those which:
|
2) invoke __copymessageto() on those which:
|
||||||
- If dstfolder doesn't have it yet, add them to dstfolder.
|
- If dstfolder doesn't have it yet, add them to dstfolder.
|
||||||
- Update statusfolder
|
- Update statusfolder
|
||||||
|
|
||||||
@ -431,23 +447,23 @@ class BaseFolder(object):
|
|||||||
if offlineimap.accounts.Account.abort_NOW_signal.is_set():
|
if offlineimap.accounts.Account.abort_NOW_signal.is_set():
|
||||||
break
|
break
|
||||||
self.ui.copyingmessage(uid, num+1, num_to_copy, self, dstfolder)
|
self.ui.copyingmessage(uid, num+1, num_to_copy, self, dstfolder)
|
||||||
# exceptions are caught in copymessageto()
|
# exceptions are caught in __copymessageto()
|
||||||
if self.suggeststhreads() and not globals.options.singlethreading:
|
if self.suggeststhreads() and not globals.options.singlethreading:
|
||||||
self.waitforthread()
|
self.waitforthread()
|
||||||
thread = threadutil.InstanceLimitedThread(\
|
thread = threadutil.InstanceLimitedThread(\
|
||||||
self.getcopyinstancelimit(),
|
self.getcopyinstancelimit(),
|
||||||
target = self.copymessageto,
|
target = self.__copymessageto,
|
||||||
name = "Copy message from %s:%s" % (self.repository, self),
|
name = "Copy message from %s:%s" % (self.repository, self),
|
||||||
args = (uid, dstfolder, statusfolder))
|
args = (uid, dstfolder, statusfolder))
|
||||||
thread.start()
|
thread.start()
|
||||||
threads.append(thread)
|
threads.append(thread)
|
||||||
else:
|
else:
|
||||||
self.copymessageto(uid, dstfolder, statusfolder,
|
self.__copymessageto(uid, dstfolder, statusfolder,
|
||||||
register = 0)
|
register = 0)
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
thread.join()
|
thread.join()
|
||||||
|
|
||||||
def syncmessagesto_delete(self, dstfolder, statusfolder):
|
def __syncmessagesto_delete(self, dstfolder, statusfolder):
|
||||||
"""Pass 2: Remove locally deleted messages on dst
|
"""Pass 2: Remove locally deleted messages on dst
|
||||||
|
|
||||||
Get all UIDS in statusfolder but not self. These are messages
|
Get all UIDS in statusfolder but not self. These are messages
|
||||||
@ -468,7 +484,7 @@ class BaseFolder(object):
|
|||||||
for folder in [statusfolder, dstfolder]:
|
for folder in [statusfolder, dstfolder]:
|
||||||
folder.deletemessages(deletelist)
|
folder.deletemessages(deletelist)
|
||||||
|
|
||||||
def syncmessagesto_flags(self, dstfolder, statusfolder):
|
def __syncmessagesto_flags(self, dstfolder, statusfolder):
|
||||||
"""Pass 3: Flag synchronization
|
"""Pass 3: Flag synchronization
|
||||||
|
|
||||||
Compare flag mismatches in self with those in statusfolder. If
|
Compare flag mismatches in self with those in statusfolder. If
|
||||||
@ -551,9 +567,9 @@ 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.
|
||||||
"""
|
"""
|
||||||
passes = [('copying messages' , self.syncmessagesto_copy),
|
passes = [('copying messages' , self.__syncmessagesto_copy),
|
||||||
('deleting messages' , self.syncmessagesto_delete),
|
('deleting messages' , self.__syncmessagesto_delete),
|
||||||
('syncing flags' , self.syncmessagesto_flags)]
|
('syncing flags' , self.__syncmessagesto_flags)]
|
||||||
|
|
||||||
for (passdesc, action) in passes:
|
for (passdesc, action) in passes:
|
||||||
# bail out on CTRL-C or SIGTERM
|
# bail out on CTRL-C or SIGTERM
|
||||||
|
@ -38,7 +38,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.randomgenerator = random.Random()
|
self.randomgenerator = random.Random()
|
||||||
#self.ui is set in BaseFolder
|
#self.ui is set in BaseFolder
|
||||||
|
|
||||||
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
|
||||||
@ -52,15 +52,19 @@ class IMAPFolder(BaseFolder):
|
|||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
imapobj.select(self.getfullname(), readonly = True, force = force)
|
imapobj.select(self.getfullname(), readonly = True, force = force)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def suggeststhreads(self):
|
def suggeststhreads(self):
|
||||||
return not globals.options.singlethreading
|
return not globals.options.singlethreading
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def waitforthread(self):
|
def waitforthread(self):
|
||||||
self.imapserver.connectionwait()
|
self.imapserver.connectionwait()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getcopyinstancelimit(self):
|
def getcopyinstancelimit(self):
|
||||||
return 'MSGCOPY_' + self.repository.getname()
|
return 'MSGCOPY_' + self.repository.getname()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def get_uidvalidity(self):
|
def get_uidvalidity(self):
|
||||||
"""Retrieve the current connections UIDVALIDITY value
|
"""Retrieve the current connections UIDVALIDITY value
|
||||||
|
|
||||||
@ -72,7 +76,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
try:
|
try:
|
||||||
# SELECT (if not already done) and get current UIDVALIDITY
|
# SELECT (if not already done) and get current UIDVALIDITY
|
||||||
self.selectro(imapobj)
|
self.__selectro(imapobj)
|
||||||
typ, uidval = imapobj.response('UIDVALIDITY')
|
typ, uidval = imapobj.response('UIDVALIDITY')
|
||||||
assert uidval != [None] and uidval != None, \
|
assert uidval != [None] and uidval != None, \
|
||||||
"response('UIDVALIDITY') returned [None]!"
|
"response('UIDVALIDITY') returned [None]!"
|
||||||
@ -81,6 +85,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
finally:
|
finally:
|
||||||
self.imapserver.releaseconnection(imapobj)
|
self.imapserver.releaseconnection(imapobj)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def quickchanged(self, statusfolder):
|
def quickchanged(self, statusfolder):
|
||||||
# An IMAP folder has definitely changed if the number of
|
# An IMAP folder has definitely changed if the number of
|
||||||
# messages or the UID of the last message have changed. Otherwise
|
# messages or the UID of the last message have changed. Otherwise
|
||||||
@ -118,6 +123,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
maxage = self.config.getdefaultint("Account %s" % self.accountname,
|
maxage = self.config.getdefaultint("Account %s" % self.accountname,
|
||||||
"maxage", -1)
|
"maxage", -1)
|
||||||
@ -199,9 +205,11 @@ class IMAPFolder(BaseFolder):
|
|||||||
rtime = imaplibutil.Internaldate2epoch(messagestr)
|
rtime = imaplibutil.Internaldate2epoch(messagestr)
|
||||||
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
|
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagelist(self):
|
def getmessagelist(self):
|
||||||
return self.messagelist
|
return self.messagelist
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
@ -253,13 +261,15 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.imapserver.releaseconnection(imapobj)
|
self.imapserver.releaseconnection(imapobj)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagetime(self, uid):
|
def getmessagetime(self, uid):
|
||||||
return self.messagelist[uid]['time']
|
return self.messagelist[uid]['time']
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessageflags(self, uid):
|
def getmessageflags(self, uid):
|
||||||
return self.messagelist[uid]['flags']
|
return self.messagelist[uid]['flags']
|
||||||
|
|
||||||
def generate_randomheader(self, content):
|
def __generate_randomheader(self, content):
|
||||||
"""Returns a unique X-OfflineIMAP header
|
"""Returns a unique X-OfflineIMAP header
|
||||||
|
|
||||||
Generate an 'X-OfflineIMAP' mail header which contains a random
|
Generate an 'X-OfflineIMAP' mail header which contains a random
|
||||||
@ -286,28 +296,28 @@ class IMAPFolder(BaseFolder):
|
|||||||
return (headername, headervalue)
|
return (headername, headervalue)
|
||||||
|
|
||||||
|
|
||||||
def savemessage_addheader(self, content, headername, headervalue):
|
def __savemessage_addheader(self, content, headername, headervalue):
|
||||||
self.ui.debug('imap',
|
self.ui.debug('imap',
|
||||||
'savemessage_addheader: called to add %s: %s' % (headername,
|
'__savemessage_addheader: called to add %s: %s' % (headername,
|
||||||
headervalue))
|
headervalue))
|
||||||
insertionpoint = content.find("\r\n\r\n")
|
insertionpoint = content.find("\r\n\r\n")
|
||||||
self.ui.debug('imap', 'savemessage_addheader: insertionpoint = %d' % insertionpoint)
|
self.ui.debug('imap', '__savemessage_addheader: insertionpoint = %d' % insertionpoint)
|
||||||
leader = content[0:insertionpoint]
|
leader = content[0:insertionpoint]
|
||||||
self.ui.debug('imap', 'savemessage_addheader: leader = %s' % repr(leader))
|
self.ui.debug('imap', '__savemessage_addheader: leader = %s' % repr(leader))
|
||||||
if insertionpoint == 0 or insertionpoint == -1:
|
if insertionpoint == 0 or insertionpoint == -1:
|
||||||
newline = ''
|
newline = ''
|
||||||
insertionpoint = 0
|
insertionpoint = 0
|
||||||
else:
|
else:
|
||||||
newline = "\r\n"
|
newline = "\r\n"
|
||||||
newline += "%s: %s" % (headername, headervalue)
|
newline += "%s: %s" % (headername, headervalue)
|
||||||
self.ui.debug('imap', 'savemessage_addheader: newline = ' + repr(newline))
|
self.ui.debug('imap', '__savemessage_addheader: newline = ' + repr(newline))
|
||||||
trailer = content[insertionpoint:]
|
trailer = content[insertionpoint:]
|
||||||
self.ui.debug('imap', 'savemessage_addheader: trailer = ' + repr(trailer))
|
self.ui.debug('imap', '__savemessage_addheader: trailer = ' + repr(trailer))
|
||||||
return leader + newline + trailer
|
return leader + newline + trailer
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@ -315,16 +325,16 @@ class IMAPFolder(BaseFolder):
|
|||||||
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(' ')
|
||||||
self.ui.debug('imap', 'savemessage_searchforheader: matchinguids now ' + \
|
self.ui.debug('imap', '__savemessage_searchforheader: matchinguids now ' + \
|
||||||
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 "
|
||||||
@ -332,7 +342,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
(headername, str(matchinguids)))
|
(headername, str(matchinguids)))
|
||||||
return long(matchinguids[0])
|
return long(matchinguids[0])
|
||||||
|
|
||||||
def savemessage_fetchheaders(self, imapobj, headername, headervalue):
|
def __savemessage_fetchheaders(self, imapobj, headername, headervalue):
|
||||||
""" We fetch all new mail headers and search for the right
|
""" We fetch all new mail headers and search for the right
|
||||||
X-OfflineImap line by hand. The response from the server has form:
|
X-OfflineImap line by hand. The response from the server has form:
|
||||||
(
|
(
|
||||||
@ -355,7 +365,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
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"
|
||||||
@ -401,7 +411,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def getmessageinternaldate(self, content, rtime=None):
|
def __getmessageinternaldate(self, content, rtime=None):
|
||||||
"""Parses mail and returns an INTERNALDATE string
|
"""Parses mail and returns an INTERNALDATE string
|
||||||
|
|
||||||
It will use information in the following order, falling back as an attempt fails:
|
It will use information in the following order, falling back as an attempt fails:
|
||||||
@ -474,6 +484,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
return internaldate
|
return internaldate
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
"""Save the message on the Server
|
"""Save the message on the Server
|
||||||
|
|
||||||
@ -511,16 +522,16 @@ class IMAPFolder(BaseFolder):
|
|||||||
use_uidplus = 'UIDPLUS' in imapobj.capabilities
|
use_uidplus = 'UIDPLUS' in imapobj.capabilities
|
||||||
|
|
||||||
# get the date of the message, so we can pass it to the server.
|
# get the date of the message, so we can pass it to the server.
|
||||||
date = self.getmessageinternaldate(content, rtime)
|
date = self.__getmessageinternaldate(content, rtime)
|
||||||
content = re.sub("(?<!\r)\n", "\r\n", content)
|
content = re.sub("(?<!\r)\n", "\r\n", content)
|
||||||
|
|
||||||
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.savemessage_addheader(content, headername,
|
content = self.__savemessage_addheader(content, headername,
|
||||||
headervalue)
|
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:])
|
||||||
@ -605,14 +616,14 @@ class IMAPFolder(BaseFolder):
|
|||||||
"'%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.")
|
||||||
@ -625,6 +636,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
|
self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
"""Change a message's flags to `flags`.
|
"""Change a message's flags to `flags`.
|
||||||
|
|
||||||
@ -650,29 +662,34 @@ class IMAPFolder(BaseFolder):
|
|||||||
flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
|
flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
|
||||||
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
|
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def addmessageflags(self, uid, flags):
|
def addmessageflags(self, uid, flags):
|
||||||
self.addmessagesflags([uid], flags)
|
self.addmessagesflags([uid], flags)
|
||||||
|
|
||||||
def addmessagesflags_noconvert(self, uidlist, flags):
|
def __addmessagesflags_noconvert(self, uidlist, flags):
|
||||||
self.processmessagesflags('+', uidlist, flags)
|
self.__processmessagesflags('+', uidlist, flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def addmessagesflags(self, uidlist, flags):
|
def addmessagesflags(self, uidlist, flags):
|
||||||
"""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
|
||||||
def deletemessageflags(self, uid, flags):
|
def deletemessageflags(self, uid, flags):
|
||||||
self.deletemessagesflags([uid], flags)
|
self.deletemessagesflags([uid], flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessagesflags(self, uidlist, flags):
|
def deletemessagesflags(self, uidlist, flags):
|
||||||
self.processmessagesflags('-', uidlist, flags)
|
self.__processmessagesflags('-', uidlist, flags)
|
||||||
|
|
||||||
def processmessagesflags(self, operation, uidlist, flags):
|
def __processmessagesflags(self, operation, uidlist, flags):
|
||||||
|
# XXX: should really iterate over batches of 100 UIDs
|
||||||
if len(uidlist) > 101:
|
if len(uidlist) > 101:
|
||||||
# Hack for those IMAP ervers with a limited line length
|
# Hack for those IMAP servers with a limited line length
|
||||||
self.processmessagesflags(operation, uidlist[:100], flags)
|
self.__processmessagesflags(operation, uidlist[:100], flags)
|
||||||
self.processmessagesflags(operation, uidlist[100:], flags)
|
self.__processmessagesflags(operation, uidlist[100:], flags)
|
||||||
return
|
return
|
||||||
|
|
||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
@ -716,6 +733,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
elif operation == '-':
|
elif operation == '-':
|
||||||
self.messagelist[uid]['flags'] -= flags
|
self.messagelist[uid]['flags'] -= flags
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def change_message_uid(self, uid, new_uid):
|
def change_message_uid(self, uid, new_uid):
|
||||||
"""Change the message from existing uid to new_uid
|
"""Change the message from existing uid to new_uid
|
||||||
|
|
||||||
@ -724,19 +742,21 @@ class IMAPFolder(BaseFolder):
|
|||||||
'%d to %d' % (uid, new_uid),
|
'%d to %d' % (uid, new_uid),
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
OfflineImapError.ERROR.MESSAGE)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
self.deletemessages_noconvert([uid])
|
self.__deletemessages_noconvert([uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessages(self, uidlist):
|
def deletemessages(self, uidlist):
|
||||||
self.deletemessages_noconvert(uidlist)
|
self.__deletemessages_noconvert(uidlist)
|
||||||
|
|
||||||
def deletemessages_noconvert(self, uidlist):
|
def __deletemessages_noconvert(self, uidlist):
|
||||||
# Weed out ones not in self.messagelist
|
# Weed out ones not in self.messagelist
|
||||||
uidlist = [uid for uid in uidlist if self.uidexists(uid)]
|
uidlist = [uid for uid in uidlist if self.uidexists(uid)]
|
||||||
if not len(uidlist):
|
if not len(uidlist):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.addmessagesflags_noconvert(uidlist, set('T'))
|
self.__addmessagesflags_noconvert(uidlist, set('T'))
|
||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@ -750,5 +770,3 @@ class IMAPFolder(BaseFolder):
|
|||||||
self.imapserver.releaseconnection(imapobj)
|
self.imapserver.releaseconnection(imapobj)
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
del self.messagelist[uid]
|
del self.messagelist[uid]
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,21 +33,26 @@ class LocalStatusFolder(BaseFolder):
|
|||||||
False)
|
False)
|
||||||
"""Should we perform fsyncs as often as possible?"""
|
"""Should we perform fsyncs as often as possible?"""
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def storesmessages(self):
|
def storesmessages(self):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def isnewfolder(self):
|
def isnewfolder(self):
|
||||||
return not os.path.exists(self.filename)
|
return not os.path.exists(self.filename)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getname(self):
|
def getname(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getroot(self):
|
def getroot(self):
|
||||||
return self.repository.root
|
return self.repository.root
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getsep(self):
|
def getsep(self):
|
||||||
return self.sep
|
return self.sep
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getfullname(self):
|
def getfullname(self):
|
||||||
return self.filename
|
return self.filename
|
||||||
|
|
||||||
@ -55,6 +60,7 @@ class LocalStatusFolder(BaseFolder):
|
|||||||
if not self.isnewfolder():
|
if not self.isnewfolder():
|
||||||
os.unlink(self.filename)
|
os.unlink(self.filename)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
if self.isnewfolder():
|
if self.isnewfolder():
|
||||||
self.messagelist = {}
|
self.messagelist = {}
|
||||||
@ -103,9 +109,11 @@ class LocalStatusFolder(BaseFolder):
|
|||||||
os.fsync(fd)
|
os.fsync(fd)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagelist(self):
|
def getmessagelist(self):
|
||||||
return self.messagelist
|
return self.messagelist
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
"""Writes a new message, with the specified uid.
|
"""Writes a new message, with the specified uid.
|
||||||
|
|
||||||
@ -124,19 +132,24 @@ class LocalStatusFolder(BaseFolder):
|
|||||||
self.save()
|
self.save()
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessageflags(self, uid):
|
def getmessageflags(self, uid):
|
||||||
return self.messagelist[uid]['flags']
|
return self.messagelist[uid]['flags']
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagetime(self, uid):
|
def getmessagetime(self, uid):
|
||||||
return self.messagelist[uid]['time']
|
return self.messagelist[uid]['time']
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
self.messagelist[uid]['flags'] = flags
|
self.messagelist[uid]['flags'] = flags
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
self.deletemessages([uid])
|
self.deletemessages([uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessages(self, uidlist):
|
def deletemessages(self, uidlist):
|
||||||
# Weed out ones not in self.messagelist
|
# Weed out ones not in self.messagelist
|
||||||
uidlist = [uid for uid in uidlist if uid in self.messagelist]
|
uidlist = [uid for uid in uidlist if uid in self.messagelist]
|
||||||
|
@ -62,14 +62,14 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
cursor = self.connection.execute("SELECT value from metadata WHERE key='db_version'")
|
cursor = self.connection.execute("SELECT value from metadata WHERE key='db_version'")
|
||||||
except sqlite.DatabaseError:
|
except sqlite.DatabaseError:
|
||||||
#db file missing or corrupt, recreate it.
|
#db file missing or corrupt, recreate it.
|
||||||
self.upgrade_db(0)
|
self.__upgrade_db(0)
|
||||||
else:
|
else:
|
||||||
# fetch db version and upgrade if needed
|
# fetch db version and upgrade if needed
|
||||||
version = int(cursor.fetchone()[0])
|
version = int(cursor.fetchone()[0])
|
||||||
if version < LocalStatusSQLiteFolder.cur_version:
|
if version < LocalStatusSQLiteFolder.cur_version:
|
||||||
self.upgrade_db(version)
|
self.__upgrade_db(version)
|
||||||
|
|
||||||
def sql_write(self, sql, vars=None, executemany=False):
|
def __sql_write(self, sql, vars=None, executemany=False):
|
||||||
"""Execute some SQL, retrying if the db was locked.
|
"""Execute some SQL, retrying if the db was locked.
|
||||||
|
|
||||||
:param sql: the SQL string passed to execute()
|
:param sql: the SQL string passed to execute()
|
||||||
@ -106,7 +106,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
self._dblock.release()
|
self._dblock.release()
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def upgrade_db(self, from_ver):
|
def __upgrade_db(self, from_ver):
|
||||||
"""Upgrade the sqlite format from version 'from_ver' to current"""
|
"""Upgrade the sqlite format from version 'from_ver' to current"""
|
||||||
|
|
||||||
if hasattr(self, 'connection'):
|
if hasattr(self, 'connection'):
|
||||||
@ -116,7 +116,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
|
|
||||||
if from_ver == 0:
|
if from_ver == 0:
|
||||||
# from_ver==0: no db existent: plain text migration?
|
# from_ver==0: no db existent: plain text migration?
|
||||||
self.create_db()
|
self.__create_db()
|
||||||
# below was derived from repository.getfolderfilename() logic
|
# below was derived from repository.getfolderfilename() logic
|
||||||
plaintextfilename = os.path.join(
|
plaintextfilename = os.path.join(
|
||||||
self.repository.account.getaccountmeta(),
|
self.repository.account.getaccountmeta(),
|
||||||
@ -144,7 +144,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
# if from_ver <= 1: ... #upgrade from 1 to 2
|
# if from_ver <= 1: ... #upgrade from 1 to 2
|
||||||
# if from_ver <= 2: ... #upgrade from 2 to 3
|
# if from_ver <= 2: ... #upgrade from 2 to 3
|
||||||
|
|
||||||
def create_db(self):
|
def __create_db(self):
|
||||||
"""Create a new db file"""
|
"""Create a new db file"""
|
||||||
self.ui._msg('Creating new Local Status db for %s:%s' \
|
self.ui._msg('Creating new Local Status db for %s:%s' \
|
||||||
% (self.repository, self))
|
% (self.repository, self))
|
||||||
@ -158,16 +158,19 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
""")
|
""")
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
|
|
||||||
|
# Interface from LocalStatusFolder
|
||||||
def isnewfolder(self):
|
def isnewfolder(self):
|
||||||
# testing the existence of the db file won't work. It is created
|
# testing the existence of the db file won't work. It is created
|
||||||
# as soon as this class instance was intitiated. So say it is a
|
# as soon as this class instance was intitiated. So say it is a
|
||||||
# new folder when there are no messages at all recorded in it.
|
# new folder when there are no messages at all recorded in it.
|
||||||
return self.getmessagecount() > 0
|
return self.getmessagecount() > 0
|
||||||
|
|
||||||
|
# Interface from LocalStatusFolder
|
||||||
def deletemessagelist(self):
|
def deletemessagelist(self):
|
||||||
"""delete all messages in the db"""
|
"""delete all messages in the db"""
|
||||||
self.sql_write('DELETE FROM status')
|
self.__sql_write('DELETE FROM status')
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
self.messagelist = {}
|
self.messagelist = {}
|
||||||
cursor = self.connection.execute('SELECT id,flags from status')
|
cursor = self.connection.execute('SELECT id,flags from status')
|
||||||
@ -175,6 +178,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
flags = set(row[1])
|
flags = set(row[1])
|
||||||
self.messagelist[row[0]] = {'uid': row[0], 'flags': flags}
|
self.messagelist[row[0]] = {'uid': row[0], 'flags': flags}
|
||||||
|
|
||||||
|
# Interface from LocalStatusFolder
|
||||||
def save(self):
|
def save(self):
|
||||||
#Noop in this backend
|
#Noop in this backend
|
||||||
pass
|
pass
|
||||||
@ -215,6 +219,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
# return flags
|
# return flags
|
||||||
# assert False,"getmessageflags() called on non-existing message"
|
# assert False,"getmessageflags() called on non-existing message"
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
"""Writes a new message, with the specified uid.
|
"""Writes a new message, with the specified uid.
|
||||||
|
|
||||||
@ -231,21 +236,24 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
|
|
||||||
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
|
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
|
||||||
flags = ''.join(sorted(flags))
|
flags = ''.join(sorted(flags))
|
||||||
self.sql_write('INSERT INTO status (id,flags) VALUES (?,?)',
|
self.__sql_write('INSERT INTO status (id,flags) VALUES (?,?)',
|
||||||
(uid,flags))
|
(uid,flags))
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
self.messagelist[uid] = {'uid': uid, 'flags': flags}
|
self.messagelist[uid] = {'uid': uid, 'flags': flags}
|
||||||
flags = ''.join(sorted(flags))
|
flags = ''.join(sorted(flags))
|
||||||
self.sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid))
|
self.__sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid))
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
if not uid in self.messagelist:
|
if not uid in self.messagelist:
|
||||||
return
|
return
|
||||||
self.sql_write('DELETE FROM status WHERE id=?', (uid, ))
|
self.__sql_write('DELETE FROM status WHERE id=?', (uid, ))
|
||||||
del(self.messagelist[uid])
|
del(self.messagelist[uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessages(self, uidlist):
|
def deletemessages(self, uidlist):
|
||||||
"""Delete list of UIDs from status cache
|
"""Delete list of UIDs from status cache
|
||||||
|
|
||||||
@ -257,6 +265,6 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
|
|||||||
if not len(uidlist):
|
if not len(uidlist):
|
||||||
return
|
return
|
||||||
# arg2 needs to be an iterable of 1-tuples [(1,),(2,),...]
|
# arg2 needs to be an iterable of 1-tuples [(1,),(2,),...]
|
||||||
self.sql_write('DELETE FROM status WHERE id=?', zip(uidlist, ), True)
|
self.__sql_write('DELETE FROM status WHERE id=?', zip(uidlist, ), True)
|
||||||
for uid in uidlist:
|
for uid in uidlist:
|
||||||
del(self.messagelist[uid])
|
del(self.messagelist[uid])
|
||||||
|
@ -43,7 +43,7 @@ timeseq = 0
|
|||||||
lasttime = 0
|
lasttime = 0
|
||||||
timelock = Lock()
|
timelock = Lock()
|
||||||
|
|
||||||
def gettimeseq():
|
def _gettimeseq():
|
||||||
global lasttime, timeseq, timelock
|
global lasttime, timeseq, timelock
|
||||||
timelock.acquire()
|
timelock.acquire()
|
||||||
try:
|
try:
|
||||||
@ -79,10 +79,12 @@ class MaildirFolder(BaseFolder):
|
|||||||
# Cache the full folder path, as we use getfullname() very often
|
# Cache the full folder path, as we use getfullname() very often
|
||||||
self._fullname = os.path.join(self.getroot(), self.getname())
|
self._fullname = os.path.join(self.getroot(), self.getname())
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getfullname(self):
|
def getfullname(self):
|
||||||
"""Return the absolute file path to the Maildir folder (sans cur|new)"""
|
"""Return the absolute file path to the Maildir folder (sans cur|new)"""
|
||||||
return self._fullname
|
return self._fullname
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def get_uidvalidity(self):
|
def get_uidvalidity(self):
|
||||||
"""Retrieve the current connections UIDVALIDITY value
|
"""Retrieve the current connections UIDVALIDITY value
|
||||||
|
|
||||||
@ -191,6 +193,7 @@ class MaildirFolder(BaseFolder):
|
|||||||
retval[uid] = {'flags': flags, 'filename': filepath}
|
retval[uid] = {'flags': flags, 'filename': filepath}
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def quickchanged(self, statusfolder):
|
def quickchanged(self, statusfolder):
|
||||||
"""Returns True if the Maildir has changed"""
|
"""Returns True if the Maildir has changed"""
|
||||||
self.cachemessagelist()
|
self.cachemessagelist()
|
||||||
@ -204,13 +207,16 @@ class MaildirFolder(BaseFolder):
|
|||||||
return True
|
return True
|
||||||
return False #Nope, nothing changed
|
return False #Nope, nothing changed
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
if self.messagelist is None:
|
if self.messagelist is None:
|
||||||
self.messagelist = self._scanfolder()
|
self.messagelist = self._scanfolder()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagelist(self):
|
def getmessagelist(self):
|
||||||
return self.messagelist
|
return self.messagelist
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessage(self, uid):
|
def getmessage(self, uid):
|
||||||
"""Return the content of the message"""
|
"""Return the content of the message"""
|
||||||
filename = self.messagelist[uid]['filename']
|
filename = self.messagelist[uid]['filename']
|
||||||
@ -222,22 +228,24 @@ class MaildirFolder(BaseFolder):
|
|||||||
# read it as text?
|
# read it as text?
|
||||||
return retval.replace("\r\n", "\n")
|
return retval.replace("\r\n", "\n")
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagetime(self, uid):
|
def getmessagetime(self, uid):
|
||||||
filename = self.messagelist[uid]['filename']
|
filename = self.messagelist[uid]['filename']
|
||||||
filepath = os.path.join(self.getfullname(), filename)
|
filepath = os.path.join(self.getfullname(), filename)
|
||||||
return os.path.getmtime(filepath)
|
return os.path.getmtime(filepath)
|
||||||
|
|
||||||
def new_message_filename(self, uid, flags=set()):
|
def __new_message_filename(self, uid, flags=set()):
|
||||||
"""Creates a new unique Maildir filename
|
"""Creates a new unique Maildir filename
|
||||||
|
|
||||||
:param uid: The UID`None`, or a set of maildir flags
|
:param uid: The UID`None`, or a set of maildir flags
|
||||||
:param flags: A set of maildir flags
|
:param flags: A set of maildir flags
|
||||||
:returns: String containing unique message filename"""
|
:returns: String containing unique message filename"""
|
||||||
timeval, timeseq = gettimeseq()
|
timeval, timeseq = _gettimeseq()
|
||||||
return '%d_%d.%d.%s,U=%d,FMD5=%s%s2,%s' % \
|
return '%d_%d.%d.%s,U=%d,FMD5=%s%s2,%s' % \
|
||||||
(timeval, timeseq, os.getpid(), socket.gethostname(),
|
(timeval, timeseq, os.getpid(), socket.gethostname(),
|
||||||
uid, self._foldermd5, self.infosep, ''.join(sorted(flags)))
|
uid, self._foldermd5, self.infosep, ''.join(sorted(flags)))
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
"""Writes a new message, with the specified uid.
|
"""Writes a new message, with the specified uid.
|
||||||
|
|
||||||
@ -259,7 +267,7 @@ class MaildirFolder(BaseFolder):
|
|||||||
# Otherwise, save the message in tmp/ and then call savemessageflags()
|
# Otherwise, save the message in tmp/ and then call savemessageflags()
|
||||||
# to give it a permanent home.
|
# to give it a permanent home.
|
||||||
tmpdir = os.path.join(self.getfullname(), 'tmp')
|
tmpdir = os.path.join(self.getfullname(), 'tmp')
|
||||||
messagename = self.new_message_filename(uid, flags)
|
messagename = self.__new_message_filename(uid, flags)
|
||||||
# open file and write it out
|
# open file and write it out
|
||||||
try:
|
try:
|
||||||
fd = os.open(os.path.join(tmpdir, messagename),
|
fd = os.open(os.path.join(tmpdir, messagename),
|
||||||
@ -291,9 +299,11 @@ class MaildirFolder(BaseFolder):
|
|||||||
self.ui.debug('maildir', 'savemessage: returning uid %d' % uid)
|
self.ui.debug('maildir', 'savemessage: returning uid %d' % uid)
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessageflags(self, uid):
|
def getmessageflags(self, uid):
|
||||||
return self.messagelist[uid]['flags']
|
return self.messagelist[uid]['flags']
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
"""Sets the specified message's flags to the given set.
|
"""Sets the specified message's flags to the given set.
|
||||||
|
|
||||||
@ -331,6 +341,7 @@ class MaildirFolder(BaseFolder):
|
|||||||
self.messagelist[uid]['flags'] = flags
|
self.messagelist[uid]['flags'] = flags
|
||||||
self.messagelist[uid]['filename'] = newfilename
|
self.messagelist[uid]['filename'] = newfilename
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def change_message_uid(self, uid, new_uid):
|
def change_message_uid(self, uid, new_uid):
|
||||||
"""Change the message from existing uid to new_uid
|
"""Change the message from existing uid to new_uid
|
||||||
|
|
||||||
@ -345,12 +356,13 @@ class MaildirFolder(BaseFolder):
|
|||||||
oldfilename = self.messagelist[uid]['filename']
|
oldfilename = self.messagelist[uid]['filename']
|
||||||
dir_prefix, filename = os.path.split(oldfilename)
|
dir_prefix, filename = os.path.split(oldfilename)
|
||||||
flags = self.getmessageflags(uid)
|
flags = self.getmessageflags(uid)
|
||||||
filename = self.new_message_filename(new_uid, flags)
|
filename = self.__new_message_filename(new_uid, flags)
|
||||||
os.rename(os.path.join(self.getfullname(), oldfilename),
|
os.rename(os.path.join(self.getfullname(), oldfilename),
|
||||||
os.path.join(self.getfullname(), dir_prefix, filename))
|
os.path.join(self.getfullname(), dir_prefix, filename))
|
||||||
self.messagelist[new_uid] = self.messagelist[uid]
|
self.messagelist[new_uid] = self.messagelist[uid]
|
||||||
del self.messagelist[uid]
|
del self.messagelist[uid]
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
"""Unlinks a message file from the Maildir.
|
"""Unlinks a message file from the Maildir.
|
||||||
|
|
||||||
@ -375,4 +387,3 @@ class MaildirFolder(BaseFolder):
|
|||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
# Yep -- return.
|
# Yep -- return.
|
||||||
del(self.messagelist[uid])
|
del(self.messagelist[uid])
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
"iling list.".format(e.args[0], self),
|
"iling list.".format(e.args[0], self),
|
||||||
OfflineImapError.ERROR.MESSAGE)
|
OfflineImapError.ERROR.MESSAGE)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def cachemessagelist(self):
|
def cachemessagelist(self):
|
||||||
self._mb.cachemessagelist()
|
self._mb.cachemessagelist()
|
||||||
reallist = self._mb.getmessagelist()
|
reallist = self._mb.getmessagelist()
|
||||||
@ -122,12 +123,14 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
finally:
|
finally:
|
||||||
self.maplock.release()
|
self.maplock.release()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def uidexists(self, ruid):
|
def uidexists(self, ruid):
|
||||||
"""Checks if the (remote) UID exists in this Folder"""
|
"""Checks if the (remote) UID exists in this Folder"""
|
||||||
# This implementation overrides the one in BaseFolder, as it is
|
# This implementation overrides the one in BaseFolder, as it is
|
||||||
# much more efficient for the mapped case.
|
# much more efficient for the mapped case.
|
||||||
return ruid in self.r2l
|
return ruid in self.r2l
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessageuidlist(self):
|
def getmessageuidlist(self):
|
||||||
"""Gets a list of (remote) UIDs.
|
"""Gets a list of (remote) UIDs.
|
||||||
You may have to call cachemessagelist() before calling this function!"""
|
You may have to call cachemessagelist() before calling this function!"""
|
||||||
@ -135,6 +138,7 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
# much more efficient for the mapped case.
|
# much more efficient for the mapped case.
|
||||||
return self.r2l.keys()
|
return self.r2l.keys()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagecount(self):
|
def getmessagecount(self):
|
||||||
"""Gets the number of messages in this folder.
|
"""Gets the number of messages in this folder.
|
||||||
You may have to call cachemessagelist() before calling this function!"""
|
You may have to call cachemessagelist() before calling this function!"""
|
||||||
@ -142,6 +146,7 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
# much more efficient for the mapped case.
|
# much more efficient for the mapped case.
|
||||||
return len(self.r2l)
|
return len(self.r2l)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagelist(self):
|
def getmessagelist(self):
|
||||||
"""Gets the current message list. This function's implementation
|
"""Gets the current message list. This function's implementation
|
||||||
is quite expensive for the mapped UID case. You must call
|
is quite expensive for the mapped UID case. You must call
|
||||||
@ -167,10 +172,12 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
finally:
|
finally:
|
||||||
self.maplock.release()
|
self.maplock.release()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessage(self, uid):
|
def getmessage(self, uid):
|
||||||
"""Returns the content of the specified message."""
|
"""Returns the content of the specified message."""
|
||||||
return self._mb.getmessage(self.r2l[uid])
|
return self._mb.getmessage(self.r2l[uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
"""Writes a new message, with the specified uid.
|
"""Writes a new message, with the specified uid.
|
||||||
|
|
||||||
@ -216,12 +223,15 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
self.maplock.release()
|
self.maplock.release()
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessageflags(self, uid):
|
def getmessageflags(self, uid):
|
||||||
return self._mb.getmessageflags(self.r2l[uid])
|
return self._mb.getmessageflags(self.r2l[uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def getmessagetime(self, uid):
|
def getmessagetime(self, uid):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def savemessageflags(self, uid, flags):
|
def savemessageflags(self, uid, flags):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -230,13 +240,16 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
dryrun mode."""
|
dryrun mode."""
|
||||||
self._mb.savemessageflags(self.r2l[uid], flags)
|
self._mb.savemessageflags(self.r2l[uid], flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def addmessageflags(self, uid, flags):
|
def addmessageflags(self, uid, flags):
|
||||||
self._mb.addmessageflags(self.r2l[uid], flags)
|
self._mb.addmessageflags(self.r2l[uid], flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def addmessagesflags(self, uidlist, flags):
|
def addmessagesflags(self, uidlist, flags):
|
||||||
self._mb.addmessagesflags(self._uidlist(self.r2l, uidlist),
|
self._mb.addmessagesflags(self._uidlist(self.r2l, uidlist),
|
||||||
flags)
|
flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def change_message_uid(self, ruid, new_ruid):
|
def change_message_uid(self, ruid, new_ruid):
|
||||||
"""Change the message from existing ruid to new_ruid
|
"""Change the message from existing ruid to new_ruid
|
||||||
|
|
||||||
@ -279,17 +292,21 @@ class MappedIMAPFolder(IMAPFolder):
|
|||||||
finally:
|
finally:
|
||||||
self.maplock.release()
|
self.maplock.release()
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessageflags(self, uid, flags):
|
def deletemessageflags(self, uid, flags):
|
||||||
self._mb.deletemessageflags(self.r2l[uid], flags)
|
self._mb.deletemessageflags(self.r2l[uid], flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessagesflags(self, uidlist, flags):
|
def deletemessagesflags(self, uidlist, flags):
|
||||||
self._mb.deletemessagesflags(self._uidlist(self.r2l, uidlist),
|
self._mb.deletemessagesflags(self._uidlist(self.r2l, uidlist),
|
||||||
flags)
|
flags)
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessage(self, uid):
|
def deletemessage(self, uid):
|
||||||
self._mb.deletemessage(self.r2l[uid])
|
self._mb.deletemessage(self.r2l[uid])
|
||||||
self._mapped_delete([uid])
|
self._mapped_delete([uid])
|
||||||
|
|
||||||
|
# Interface from BaseFolder
|
||||||
def deletemessages(self, uidlist):
|
def deletemessages(self, uidlist):
|
||||||
self._mb.deletemessages(self._uidlist(self.r2l, uidlist))
|
self._mb.deletemessages(self._uidlist(self.r2l, uidlist))
|
||||||
self._mapped_delete(uidlist)
|
self._mapped_delete(uidlist)
|
||||||
|
@ -31,7 +31,7 @@ from offlineimap.imaplib2 import IMAP4, IMAP4_SSL, zlib, IMAP4_PORT, InternalDat
|
|||||||
|
|
||||||
|
|
||||||
class UsefulIMAPMixIn(object):
|
class UsefulIMAPMixIn(object):
|
||||||
def getselectedfolder(self):
|
def __getselectedfolder(self):
|
||||||
if self.state == 'SELECTED':
|
if self.state == 'SELECTED':
|
||||||
return self.mailbox
|
return self.mailbox
|
||||||
return None
|
return None
|
||||||
@ -41,7 +41,7 @@ class UsefulIMAPMixIn(object):
|
|||||||
|
|
||||||
:returns: 'OK' on success, nothing if the folder was already
|
:returns: 'OK' on success, nothing if the folder was already
|
||||||
selected or raises an :exc:`OfflineImapError`"""
|
selected or raises an :exc:`OfflineImapError`"""
|
||||||
if self.getselectedfolder() == mailbox and self.is_readonly == readonly \
|
if self.__getselectedfolder() == mailbox and self.is_readonly == readonly \
|
||||||
and not force:
|
and not force:
|
||||||
# No change; return.
|
# No change; return.
|
||||||
return
|
return
|
||||||
@ -66,6 +66,7 @@ class UsefulIMAPMixIn(object):
|
|||||||
raise OfflineImapError(errstr, severity)
|
raise OfflineImapError(errstr, severity)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# Overrides private function from IMAP4 (@imaplib2)
|
||||||
def _mesg(self, s, tn=None, secs=None):
|
def _mesg(self, s, tn=None, secs=None):
|
||||||
new_mesg(self, s, tn, secs)
|
new_mesg(self, s, tn, secs)
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class IMAPServer:
|
|||||||
self.sslcacertfile = repos.getsslcacertfile()
|
self.sslcacertfile = repos.getsslcacertfile()
|
||||||
self.sslversion = repos.getsslversion()
|
self.sslversion = repos.getsslversion()
|
||||||
if self.sslcacertfile is None:
|
if self.sslcacertfile is None:
|
||||||
self.verifycert = None # disable cert verification
|
self.__verifycert = None # disable cert verification
|
||||||
|
|
||||||
self.delim = None
|
self.delim = None
|
||||||
self.root = None
|
self.root = None
|
||||||
@ -99,7 +99,7 @@ class IMAPServer:
|
|||||||
self.gss_vc = None
|
self.gss_vc = None
|
||||||
self.gssapi = False
|
self.gssapi = False
|
||||||
|
|
||||||
def getpassword(self):
|
def __getpassword(self):
|
||||||
"""Returns the server password or None"""
|
"""Returns the server password or None"""
|
||||||
if self.goodpassword != None: # use cached good one first
|
if self.goodpassword != None: # use cached good one first
|
||||||
return self.goodpassword
|
return self.goodpassword
|
||||||
@ -114,6 +114,7 @@ class IMAPServer:
|
|||||||
self.passworderror = None
|
self.passworderror = None
|
||||||
return self.password
|
return self.password
|
||||||
|
|
||||||
|
# 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."""
|
||||||
@ -136,39 +137,40 @@ class IMAPServer:
|
|||||||
self.connectionlock.release()
|
self.connectionlock.release()
|
||||||
self.semaphore.release()
|
self.semaphore.release()
|
||||||
|
|
||||||
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 = ''
|
||||||
if self.user_identity != None:
|
if self.user_identity != None:
|
||||||
authz = self.user_identity
|
authz = self.user_identity
|
||||||
NULL = u'\x00'
|
NULL = u'\x00'
|
||||||
retval = NULL.join((authz, authc, passwd)).encode('utf-8')
|
retval = NULL.join((authz, authc, passwd)).encode('utf-8')
|
||||||
self.ui.debug('imap', 'plainhandler: returning %s' % retval)
|
self.ui.debug('imap', '__plainhandler: returning %s' % retval)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|
||||||
def gssauth(self, response):
|
# XXX: describe function
|
||||||
|
def __gssauth(self, response):
|
||||||
data = base64.b64encode(response)
|
data = base64.b64encode(response)
|
||||||
try:
|
try:
|
||||||
if self.gss_step == self.GSS_STATE_STEP:
|
if self.gss_step == self.GSS_STATE_STEP:
|
||||||
@ -196,7 +198,7 @@ class IMAPServer:
|
|||||||
return base64.b64decode(response)
|
return base64.b64decode(response)
|
||||||
|
|
||||||
|
|
||||||
def _start_tls(self, imapobj):
|
def __start_tls(self, imapobj):
|
||||||
if 'STARTTLS' in imapobj.capabilities and not self.usessl:
|
if 'STARTTLS' in imapobj.capabilities and not self.usessl:
|
||||||
self.ui.debug('imap', 'Using STARTTLS connection')
|
self.ui.debug('imap', 'Using STARTTLS connection')
|
||||||
try:
|
try:
|
||||||
@ -207,7 +209,7 @@ class IMAPServer:
|
|||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
|
|
||||||
## All _authn_* procedures are helpers that do authentication.
|
## All __authn_* procedures are helpers that do authentication.
|
||||||
## They are class methods that take one parameter, IMAP object.
|
## They are class methods that take one parameter, IMAP object.
|
||||||
##
|
##
|
||||||
## Each function should return True if authentication was
|
## Each function should return True if authentication was
|
||||||
@ -224,13 +226,13 @@ class IMAPServer:
|
|||||||
## - OfflineImapError means that function detected some
|
## - OfflineImapError means that function detected some
|
||||||
## problem by itself.
|
## problem by itself.
|
||||||
|
|
||||||
def _authn_gssapi(self, imapobj):
|
def __authn_gssapi(self, imapobj):
|
||||||
if not have_gss:
|
if not have_gss:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.connectionlock.acquire()
|
self.connectionlock.acquire()
|
||||||
try:
|
try:
|
||||||
imapobj.authenticate('GSSAPI', self.gssauth)
|
imapobj.authenticate('GSSAPI', self.__gssauth)
|
||||||
return True
|
return True
|
||||||
except imapobj.error as e:
|
except imapobj.error as e:
|
||||||
self.gssapi = False
|
self.gssapi = False
|
||||||
@ -243,15 +245,15 @@ class IMAPServer:
|
|||||||
finally:
|
finally:
|
||||||
self.connectionlock.release()
|
self.connectionlock.release()
|
||||||
|
|
||||||
def _authn_cram_md5(self, imapobj):
|
def __authn_cram_md5(self, imapobj):
|
||||||
imapobj.authenticate('CRAM-MD5', self.md5handler)
|
imapobj.authenticate('CRAM-MD5', self.__md5handler)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _authn_plain(self, imapobj):
|
def __authn_plain(self, imapobj):
|
||||||
imapobj.authenticate('PLAIN', self.plainhandler)
|
imapobj.authenticate('PLAIN', self.__plainhandler)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _authn_login(self, imapobj):
|
def __authn_login(self, imapobj):
|
||||||
# Use LOGIN command, unless LOGINDISABLED is advertized
|
# Use LOGIN command, unless LOGINDISABLED is advertized
|
||||||
# (per RFC 2595)
|
# (per RFC 2595)
|
||||||
if 'LOGINDISABLED' in imapobj.capabilities:
|
if 'LOGINDISABLED' in imapobj.capabilities:
|
||||||
@ -259,11 +261,11 @@ class IMAPServer:
|
|||||||
"disabled by server. Need to use SSL?",
|
"disabled by server. Need to use SSL?",
|
||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
else:
|
else:
|
||||||
self.loginauth(imapobj)
|
self.__loginauth(imapobj)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _authn_helper(self, imapobj):
|
def __authn_helper(self, imapobj):
|
||||||
"""
|
"""
|
||||||
Authentication machinery for self.acquireconnection().
|
Authentication machinery for self.acquireconnection().
|
||||||
|
|
||||||
@ -283,10 +285,10 @@ class IMAPServer:
|
|||||||
# - tryTLS flag,
|
# - tryTLS flag,
|
||||||
# - check IMAP capability flag.
|
# - check IMAP capability flag.
|
||||||
auth_methods = {
|
auth_methods = {
|
||||||
"GSSAPI": (self._authn_gssapi, False, True),
|
"GSSAPI": (self.__authn_gssapi, False, True),
|
||||||
"CRAM-MD5": (self._authn_cram_md5, True, True),
|
"CRAM-MD5": (self.__authn_cram_md5, True, True),
|
||||||
"PLAIN": (self._authn_plain, True, True),
|
"PLAIN": (self.__authn_plain, True, True),
|
||||||
"LOGIN": (self._authn_login, True, False),
|
"LOGIN": (self.__authn_login, True, False),
|
||||||
}
|
}
|
||||||
# Stack stores pairs of (method name, exception)
|
# Stack stores pairs of (method name, exception)
|
||||||
exc_stack = []
|
exc_stack = []
|
||||||
@ -311,7 +313,7 @@ class IMAPServer:
|
|||||||
# they could have been changed after STARTTLS.
|
# they could have been changed after STARTTLS.
|
||||||
if tryTLS and not tried_tls:
|
if tryTLS and not tried_tls:
|
||||||
tried_tls = True
|
tried_tls = True
|
||||||
self._start_tls(imapobj)
|
self.__start_tls(imapobj)
|
||||||
|
|
||||||
if check_cap:
|
if check_cap:
|
||||||
cap = "AUTH=" + m
|
cap = "AUTH=" + m
|
||||||
@ -348,6 +350,7 @@ class IMAPServer:
|
|||||||
OfflineImapError.ERROR.REPO)
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
|
|
||||||
|
# XXX: move above, closer to releaseconnection()
|
||||||
def acquireconnection(self):
|
def acquireconnection(self):
|
||||||
"""Fetches a connection from the pool, making sure to create a new one
|
"""Fetches a connection from the pool, making sure to create a new one
|
||||||
if needed, to obey the maximum connection limits, etc.
|
if needed, to obey the maximum connection limits, etc.
|
||||||
@ -400,7 +403,7 @@ class IMAPServer:
|
|||||||
self.sslclientkey,
|
self.sslclientkey,
|
||||||
self.sslclientcert,
|
self.sslclientcert,
|
||||||
self.sslcacertfile,
|
self.sslcacertfile,
|
||||||
self.verifycert,
|
self.__verifycert,
|
||||||
self.sslversion,
|
self.sslversion,
|
||||||
timeout=socket.getdefaulttimeout(),
|
timeout=socket.getdefaulttimeout(),
|
||||||
fingerprint=fingerprint
|
fingerprint=fingerprint
|
||||||
@ -412,7 +415,7 @@ class IMAPServer:
|
|||||||
|
|
||||||
if not self.preauth_tunnel:
|
if not self.preauth_tunnel:
|
||||||
try:
|
try:
|
||||||
self._authn_helper(imapobj)
|
self.__authn_helper(imapobj)
|
||||||
self.goodpassword = self.password
|
self.goodpassword = self.password
|
||||||
success = 1
|
success = 1
|
||||||
except OfflineImapError as e:
|
except OfflineImapError as e:
|
||||||
@ -562,7 +565,7 @@ class IMAPServer:
|
|||||||
self.ui.debug('imap', 'keepalive: event is set; exiting')
|
self.ui.debug('imap', 'keepalive: event is set; exiting')
|
||||||
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.
|
||||||
|
|
||||||
@ -646,7 +649,7 @@ class IdleThread(object):
|
|||||||
self.parent.releaseconnection(imapobj)
|
self.parent.releaseconnection(imapobj)
|
||||||
self.stop_sig.wait() # wait until we are supposed to quit
|
self.stop_sig.wait() # wait until we are supposed to quit
|
||||||
|
|
||||||
def dosync(self):
|
def __dosync(self):
|
||||||
remoterepos = self.parent.repos
|
remoterepos = self.parent.repos
|
||||||
account = remoterepos.account
|
account = remoterepos.account
|
||||||
localrepos = account.localrepos
|
localrepos = account.localrepos
|
||||||
@ -663,7 +666,7 @@ class IdleThread(object):
|
|||||||
ui = getglobalui()
|
ui = getglobalui()
|
||||||
ui.unregisterthread(currentThread()) #syncfolder registered the thread
|
ui.unregisterthread(currentThread()) #syncfolder registered the thread
|
||||||
|
|
||||||
def idle(self):
|
def __idle(self):
|
||||||
"""Invoke IDLE mode until timeout or self.stop() is invoked"""
|
"""Invoke IDLE mode until timeout or self.stop() is invoked"""
|
||||||
def callback(args):
|
def callback(args):
|
||||||
"""IDLE callback function invoked by imaplib2
|
"""IDLE callback function invoked by imaplib2
|
||||||
@ -696,7 +699,7 @@ class IdleThread(object):
|
|||||||
else:
|
else:
|
||||||
success = True
|
success = True
|
||||||
if "IDLE" in imapobj.capabilities:
|
if "IDLE" in imapobj.capabilities:
|
||||||
imapobj.idle(callback=callback)
|
imapobj.__idle(callback=callback)
|
||||||
else:
|
else:
|
||||||
self.ui.warn("IMAP IDLE not supported on server '%s'."
|
self.ui.warn("IMAP IDLE not supported on server '%s'."
|
||||||
"Sleep until next refresh cycle." % imapobj.identifier)
|
"Sleep until next refresh cycle." % imapobj.identifier)
|
||||||
@ -716,4 +719,4 @@ class IdleThread(object):
|
|||||||
# here not via self.stop, but because IDLE responded. Do
|
# here not via self.stop, but because IDLE responded. Do
|
||||||
# another round and invoke actual syncing.
|
# another round and invoke actual syncing.
|
||||||
self.stop_sig.clear()
|
self.stop_sig.clear()
|
||||||
self.dosync()
|
self.__dosync()
|
||||||
|
@ -21,7 +21,7 @@ import string
|
|||||||
from offlineimap.ui import getglobalui
|
from offlineimap.ui import getglobalui
|
||||||
|
|
||||||
|
|
||||||
def debug(*args):
|
def __debug(*args):
|
||||||
msg = []
|
msg = []
|
||||||
for arg in args:
|
for arg in args:
|
||||||
msg.append(str(arg))
|
msg.append(str(arg))
|
||||||
@ -50,7 +50,7 @@ def flagsplit(string):
|
|||||||
raise ValueError("Passed string '%s' is not a flag list" % string)
|
raise ValueError("Passed string '%s' is not a flag list" % string)
|
||||||
return imapsplit(string[1:-1])
|
return imapsplit(string[1:-1])
|
||||||
|
|
||||||
def options2hash(list):
|
def __options2hash(list):
|
||||||
"""convert list [1,2,3,4,5,6] to {1:2, 3:4, 5:6}"""
|
"""convert list [1,2,3,4,5,6] to {1:2, 3:4, 5:6}"""
|
||||||
# effectively this does dict(zip(l[::2],l[1::2])), however
|
# effectively this does dict(zip(l[::2],l[1::2])), however
|
||||||
# measurements seemed to have indicated that the manual variant is
|
# measurements seemed to have indicated that the manual variant is
|
||||||
@ -60,7 +60,7 @@ def options2hash(list):
|
|||||||
while (counter < len(list)):
|
while (counter < len(list)):
|
||||||
retval[list[counter]] = list[counter + 1]
|
retval[list[counter]] = list[counter + 1]
|
||||||
counter += 2
|
counter += 2
|
||||||
debug("options2hash returning:", retval)
|
__debug("__options2hash returning:", retval)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def flags2hash(flags):
|
def flags2hash(flags):
|
||||||
@ -68,7 +68,7 @@ def flags2hash(flags):
|
|||||||
|
|
||||||
E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to
|
E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to
|
||||||
{'FLAGS': '(\\Seen Old)', 'UID': '4807'}"""
|
{'FLAGS': '(\\Seen Old)', 'UID': '4807'}"""
|
||||||
return options2hash(flagsplit(flags))
|
return __options2hash(flagsplit(flags))
|
||||||
|
|
||||||
def imapsplit(imapstring):
|
def imapsplit(imapstring):
|
||||||
"""Takes a string from an IMAP conversation and returns a list containing
|
"""Takes a string from an IMAP conversation and returns a list containing
|
||||||
@ -81,7 +81,7 @@ def imapsplit(imapstring):
|
|||||||
['(\\HasNoChildren)', '"."', '"INBOX.Sent"']"""
|
['(\\HasNoChildren)', '"."', '"INBOX.Sent"']"""
|
||||||
|
|
||||||
if not isinstance(imapstring, basestring):
|
if not isinstance(imapstring, basestring):
|
||||||
debug("imapsplit() got a non-string input; working around.")
|
__debug("imapsplit() got a non-string input; working around.")
|
||||||
# Sometimes, imaplib will throw us a tuple if the input
|
# Sometimes, imaplib will throw us a tuple if the input
|
||||||
# contains a literal. See Python bug
|
# contains a literal. See Python bug
|
||||||
# #619732 at https://sourceforge.net/tracker/index.php?func=detail&aid=619732&group_id=5470&atid=105470
|
# #619732 at https://sourceforge.net/tracker/index.php?func=detail&aid=619732&group_id=5470&atid=105470
|
||||||
@ -103,7 +103,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:
|
||||||
@ -113,10 +113,10 @@ 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
|
||||||
|
|
||||||
workstr = imapstring.strip()
|
workstr = imapstring.strip()
|
||||||
@ -137,7 +137,7 @@ def imapsplit(imapstring):
|
|||||||
retval.append(parenlist)
|
retval.append(parenlist)
|
||||||
elif workstr[0] == '"':
|
elif workstr[0] == '"':
|
||||||
# quoted fragments '"...\"..."'
|
# quoted fragments '"...\"..."'
|
||||||
(quoted, rest) = _split_quoted(workstr)
|
(quoted, rest) = __split_quoted(workstr)
|
||||||
retval.append(quoted)
|
retval.append(quoted)
|
||||||
workstr = rest
|
workstr = rest
|
||||||
else:
|
else:
|
||||||
@ -213,7 +213,7 @@ def uid_sequence(uidlist):
|
|||||||
return ",".join(retval)
|
return ",".join(retval)
|
||||||
|
|
||||||
|
|
||||||
def _split_quoted(string):
|
def __split_quoted(string):
|
||||||
"""
|
"""
|
||||||
Looks for the ending quote character in the string that starts
|
Looks for the ending quote character in the string that starts
|
||||||
with quote character, splitting out quoted component and the
|
with quote character, splitting out quoted component and the
|
||||||
|
@ -43,13 +43,13 @@ class OfflineImap:
|
|||||||
def run(self):
|
def run(self):
|
||||||
"""Parse the commandline and invoke everything"""
|
"""Parse the commandline and invoke everything"""
|
||||||
# next line also sets self.config and self.ui
|
# next line also sets self.config and self.ui
|
||||||
options, args = self.parse_cmd_options()
|
options, args = self.__parse_cmd_options()
|
||||||
if options.diagnostics:
|
if options.diagnostics:
|
||||||
self.serverdiagnostics(options)
|
self.__serverdiagnostics(options)
|
||||||
else:
|
else:
|
||||||
self.sync(options)
|
self.__sync(options)
|
||||||
|
|
||||||
def parse_cmd_options(self):
|
def __parse_cmd_options(self):
|
||||||
parser = OptionParser(version=offlineimap.__version__,
|
parser = OptionParser(version=offlineimap.__version__,
|
||||||
description="%s.\n\n%s" %
|
description="%s.\n\n%s" %
|
||||||
(offlineimap.__copyright__,
|
(offlineimap.__copyright__,
|
||||||
@ -297,7 +297,7 @@ class OfflineImap:
|
|||||||
self.config = config
|
self.config = config
|
||||||
return (options, args)
|
return (options, args)
|
||||||
|
|
||||||
def sync(self, options):
|
def __sync(self, options):
|
||||||
"""Invoke the correct single/multithread syncing
|
"""Invoke the correct single/multithread syncing
|
||||||
|
|
||||||
self.config is supposed to have been correctly initialized
|
self.config is supposed to have been correctly initialized
|
||||||
@ -360,7 +360,7 @@ class OfflineImap:
|
|||||||
|
|
||||||
if options.singlethreading:
|
if options.singlethreading:
|
||||||
#singlethreaded
|
#singlethreaded
|
||||||
self.sync_singlethreaded(syncaccounts)
|
self.__sync_singlethreaded(syncaccounts)
|
||||||
else:
|
else:
|
||||||
# multithreaded
|
# multithreaded
|
||||||
t = threadutil.ExitNotifyThread(target=syncmaster.syncitall,
|
t = threadutil.ExitNotifyThread(target=syncmaster.syncitall,
|
||||||
@ -376,7 +376,7 @@ class OfflineImap:
|
|||||||
self.ui.error(e)
|
self.ui.error(e)
|
||||||
self.ui.terminate()
|
self.ui.terminate()
|
||||||
|
|
||||||
def sync_singlethreaded(self, accs):
|
def __sync_singlethreaded(self, accs):
|
||||||
"""Executed if we do not want a separate syncmaster thread
|
"""Executed if we do not want a separate syncmaster thread
|
||||||
|
|
||||||
:param accs: A list of accounts that should be synced
|
:param accs: A list of accounts that should be synced
|
||||||
@ -387,7 +387,7 @@ class OfflineImap:
|
|||||||
threading.currentThread().name = "Account sync %s" % accountname
|
threading.currentThread().name = "Account sync %s" % accountname
|
||||||
account.syncrunner()
|
account.syncrunner()
|
||||||
|
|
||||||
def serverdiagnostics(self, options):
|
def __serverdiagnostics(self, options):
|
||||||
activeaccounts = self.config.get("general", "accounts")
|
activeaccounts = self.config.get("general", "accounts")
|
||||||
if options.accounts:
|
if options.accounts:
|
||||||
activeaccounts = options.accounts
|
activeaccounts = options.accounts
|
||||||
|
@ -44,9 +44,9 @@ def write():
|
|||||||
if account not in boxes:
|
if account not in boxes:
|
||||||
return
|
return
|
||||||
|
|
||||||
genmbnames()
|
__genmbnames()
|
||||||
|
|
||||||
def genmbnames():
|
def __genmbnames():
|
||||||
"""Takes a configparser object and a boxlist, which is a list of hashes
|
"""Takes a configparser object and a boxlist, which is a list of hashes
|
||||||
containing 'accountname' and 'foldername' keys."""
|
containing 'accountname' and 'foldername' keys."""
|
||||||
mblock.acquire()
|
mblock.acquire()
|
||||||
|
@ -70,11 +70,6 @@ class TestInternalFunctions(unittest.TestCase):
|
|||||||
res = imaputil.flagsplit(b'(FLAGS (\\Seen Old) UID 4807)')
|
res = imaputil.flagsplit(b'(FLAGS (\\Seen Old) UID 4807)')
|
||||||
self.assertEqual(res, [b'FLAGS', b'(\\Seen Old)', b'UID', b'4807'])
|
self.assertEqual(res, [b'FLAGS', b'(\\Seen Old)', b'UID', b'4807'])
|
||||||
|
|
||||||
def test_03_options2hash(self):
|
|
||||||
"""Test imaputil.options2hash()"""
|
|
||||||
res = imaputil.options2hash([1,2,3,4,5,6])
|
|
||||||
self.assertEqual(res, {1:2, 3:4, 5:6})
|
|
||||||
|
|
||||||
def test_04_flags2hash(self):
|
def test_04_flags2hash(self):
|
||||||
"""Test imaputil.flags2hash()"""
|
"""Test imaputil.flags2hash()"""
|
||||||
res = imaputil.flags2hash(b'(FLAGS (\\Seen Old) UID 4807)')
|
res = imaputil.flags2hash(b'(FLAGS (\\Seen Old) UID 4807)')
|
||||||
|
Loading…
Reference in New Issue
Block a user