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