def syncfolder(account, remotefolder, quick): """This function is called as target for the InstanceLimitedThread invokation in SyncableAccount. Filtered folders on the remote side will not invoke this function.""" remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) config = account.getconfig() global syncflagsignoremaxage syncflagsignoremaxage = config.getdefaultboolean("general", "syncflagsignoremaxage", True) global cachemessagelist if syncflagsignoremaxage: # free lists between folders cachemessagelist = {} # restore old maxage config global maxage_orig for a in config.getsectionlist('Account'): if maxage_orig.has_key(a): config.set("Account %s" % a, "maxage", str(maxage_orig[a])) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Write the mailboxes mbnames.add(account.name, localfolder.getname()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() if syncflagsignoremaxage: addcachemessagelist(localfolder.repository.name, localfolder, False, localfolder.getmessagelist()) ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() if syncflagsignoremaxage: addcachemessagelist(remotefolder.repository.name, remotefolder, False, remotefolder.getmessagelist()) ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug('imap', "Not syncing to read-only repository '%s'" \ % localrepos.getname()) # Synchronize local changes if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'" \ % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (account, remotefolder.getvisiblename(), traceback.format_exc()))
def syncfolder(account, remotefolder, quick): """Synchronizes given remote folder for the specified account. Filtered folders on the remote side will not invoke this function. When called in concurrently for the same localfolder, syncs are serialized.""" def acquire_mutex(): account_name = account.getname() localfolder_name = localfolder.getfullname() with SYNC_MUTEXES_LOCK: if SYNC_MUTEXES.get(account_name) is None: SYNC_MUTEXES[account_name] = {} # The localfolder full name is good to uniquely identify the sync # transaction. if SYNC_MUTEXES[account_name].get(localfolder_name) is None: #XXX: This lock could be an external file lock so we can remove # the lock at the account level. SYNC_MUTEXES[account_name][localfolder_name] = Lock() # Acquire the lock. SYNC_MUTEXES[account_name][localfolder_name].acquire() def release_mutex(): SYNC_MUTEXES[account.getname()][localfolder.getfullname()].release() def check_uid_validity(): # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() > 0 or statusfolder.getmessagecount( ) > 0: if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localfolder.repository.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY. localfolder.save_uidvalidity() remotefolder.save_uidvalidity() def cachemessagelists_upto_date(date): """Returns messages with uid > min(uids of messages newer than date).""" remotefolder.cachemessagelist( min_date=time.gmtime(time.mktime(date) + 24 * 60 * 60)) uids = remotefolder.getmessageuidlist() localfolder.dropmessagelistcache() if len(uids) > 0: # Reload the remote message list from min_uid. This avoid issues for # old messages, which has been added from local on any previous run # (IOW, message is older than maxage _and_ has high enough UID). remotefolder.dropmessagelistcache() remotefolder.cachemessagelist(min_uid=min(uids)) localfolder.cachemessagelist(min_uid=min(uids)) else: # Remote folder UIDs list is empty for the given range. We still # might have valid local UIDs for this range (e.g.: new local # emails). localfolder.cachemessagelist(min_date=date) uids = localfolder.getmessageuidlist() # Take care to only consider positive uids. Negative UIDs might be # present due to new emails. uids = [uid for uid in uids if uid > 0] if len(uids) > 0: # Update the remote cache list for this new min(uids). remotefolder.dropmessagelistcache() remotefolder.cachemessagelist(min_uid=min(uids)) def cachemessagelists_startdate(new, partial, date): """Retrieve messagelists when startdate has been set for the folder 'partial'. Idea: suppose you want to clone the messages after date in one account (partial) to a new one (new). If new is empty, then copy messages in partial newer than date to new, and keep track of the min uid. On subsequent syncs, sync all the messages in new against those after that min uid in partial. This is a partial replacement for maxage in the IMAP-IMAP sync case, where maxage doesn't work: the UIDs of the messages in localfolder might not be in the same order as those of corresponding messages in remotefolder, so if L in local corresponds to R in remote, the ranges [L, ...] and [R, ...] might not correspond. But, if we're cloning a folder into a new one, [min_uid, ...] does correspond to [1, ...]. This is just for IMAP-IMAP. For Maildir-IMAP, use maxage instead.""" new.cachemessagelist() min_uid = partial.retrieve_min_uid() if min_uid == None: # min_uid file didn't exist if len(new.getmessageuidlist()) > 0: raise OfflineImapError( "To use startdate on Repository %s, " "Repository %s must be empty" % (partial.repository.name, new.repository.name), OfflineImapError.ERROR.MESSAGE) else: partial.cachemessagelist(min_date=date) # messagelist.keys() instead of getuidmessagelist() because in # the UID mapped case we want the actual local UIDs, not their # remote counterparts. positive_uids = [ uid for uid in list(partial.messagelist.keys()) if uid > 0 ] if len(positive_uids) > 0: min_uid = min(positive_uids) else: min_uid = 1 partial.save_min_uid(min_uid) else: partial.cachemessagelist(min_uid=min_uid) remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Acquire the mutex to start syncing. acquire_mutex() # Add the folder to the mbnames mailboxes. mbnames.add(account.name, localrepos.getlocalroot(), localfolder.getname()) # Load status folder. statusfolder = statusrepos.getfolder( remotefolder.getvisiblename().replace(remoterepos.getsep(), statusrepos.getsep())) statusfolder.openfiles() statusfolder.cachemessagelist() # Load local folder. ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) # Retrieve messagelists, taking into account age-restriction # options. maxage = localfolder.getmaxage() localstart = localfolder.getstartdate() remotestart = remotefolder.getstartdate() if (maxage != None) + (localstart != None) + (remotestart != None) > 1: six.reraise( OfflineImapError, OfflineImapError( "You can set at most one of the " "following: maxage, startdate (for the local " "folder), startdate (for the remote folder)", OfflineImapError.ERROR.REPO), exc_info()[2]) if (maxage != None or localstart or remotestart) and quick: # IMAP quickchanged isn't compatible with options that # involve restricting the messagelist, since the "quick" # check can only retrieve a full list of UIDs in the folder. ui.warn("Quick syncs (-q) not supported in conjunction " "with maxage or startdate; ignoring -q.") if maxage != None: cachemessagelists_upto_date(maxage) check_uid_validity() elif localstart != None: cachemessagelists_startdate(remotefolder, localfolder, localstart) check_uid_validity() elif remotestart != None: cachemessagelists_startdate(localfolder, remotefolder, remotestart) check_uid_validity() else: localfolder.cachemessagelist() if quick: if (not localfolder.quickchanged(statusfolder) and not remotefolder.quickchanged(statusfolder)): ui.skippingfolder(remotefolder) localrepos.restore_atime() return check_uid_validity() remotefolder.cachemessagelist() # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug( '', "Not syncing to read-only repository '%s'" % localrepos.getname()) # Synchronize local changes. if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug( '', "Not syncing to read-only repository '%s'" % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # Bubble up severe Errors, skip folder otherwise. if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg="Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error( e, msg="ERROR in syncfolder for %s folder %s: %s" % (account, remotefolder.getvisiblename(), traceback.format_exc())) finally: for folder in ["statusfolder", "localfolder", "remotefolder"]: if folder in locals(): locals()[folder].dropmessagelistcache() statusfolder.closefiles() # Release the mutex of this sync transaction. release_mutex()
def syncfolder(account, remotefolder, quick): """Synchronizes given remote folder for the specified account. Filtered folders on the remote side will not invoke this function. However, this might be called in a concurrently.""" def check_uid_validity(): # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() > 0 or statusfolder.getmessagecount() > 0: if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localfolder.repository.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY. localfolder.save_uidvalidity() remotefolder.save_uidvalidity() def cachemessagelists_upto_date(date): """Returns messages with uid > min(uids of messages newer than date).""" # Warning: this makes sense only if the cached list is empty. localfolder.cachemessagelist(min_date=date) check_uid_validity() # Local messagelist had date restriction applied already. Restrict # sync to messages with UIDs >= min_uid from this list. # # Local messagelist might contain new messages (with uid's < 0). positive_uids = [uid for uid in localfolder.getmessageuidlist() if uid > 0] if len(positive_uids) > 0: remotefolder.cachemessagelist(min_uid=min(positive_uids)) else: # No messages with UID > 0 in range in localfolder. # date restriction was applied with respect to local dates but # remote folder timezone might be different from local, so be # safe and make sure the range isn't bigger than in local. remotefolder.cachemessagelist( min_date=time.gmtime(time.mktime(date) + 24*60*60)) def cachemessagelists_startdate(new, partial, date): """Retrieve messagelists when startdate has been set for the folder 'partial'. Idea: suppose you want to clone the messages after date in one account (partial) to a new one (new). If new is empty, then copy messages in partial newer than date to new, and keep track of the min uid. On subsequent syncs, sync all the messages in new against those after that min uid in partial. This is a partial replacement for maxage in the IMAP-IMAP sync case, where maxage doesn't work: the UIDs of the messages in localfolder might not be in the same order as those of corresponding messages in remotefolder, so if L in local corresponds to R in remote, the ranges [L, ...] and [R, ...] might not correspond. But, if we're cloning a folder into a new one, [min_uid, ...] does correspond to [1, ...]. This is just for IMAP-IMAP. For Maildir-IMAP, use maxage instead.""" new.cachemessagelist() min_uid = partial.retrieve_min_uid() if min_uid == None: # min_uid file didn't exist if len(new.getmessageuidlist()) > 0: raise OfflineImapError("To use startdate on Repository %s, " "Repository %s must be empty"% (partial.repository.name, new.repository.name), OfflineImapError.ERROR.MESSAGE) else: partial.cachemessagelist(min_date=date) # messagelist.keys() instead of getuidmessagelist() because in # the UID mapped case we want the actual local UIDs, not their # remote counterparts. positive_uids = [uid for uid in list(partial.messagelist.keys()) if uid > 0] if len(positive_uids) > 0: min_uid = min(positive_uids) else: min_uid = 1 partial.save_min_uid(min_uid) else: partial.cachemessagelist(min_uid=min_uid) remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Add the folder to the mbnames mailboxes. mbnames.add(account.name, localrepos.getlocalroot(), localfolder.getname()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename(). replace(remoterepos.getsep(), statusrepos.getsep())) statusfolder.openfiles() statusfolder.cachemessagelist() # Load local folder. ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) # Retrieve messagelists, taking into account age-restriction # options. maxage = localfolder.getmaxage() localstart = localfolder.getstartdate() remotestart = remotefolder.getstartdate() if (maxage != None) + (localstart != None) + (remotestart != None) > 1: six.reraise(OfflineImapError, OfflineImapError("You can set at most one of the " "following: maxage, startdate (for the local " "folder), startdate (for the remote folder)", OfflineImapError.ERROR.REPO), exc_info()[2]) if (maxage != None or localstart or remotestart) and quick: # IMAP quickchanged isn't compatible with options that # involve restricting the messagelist, since the "quick" # check can only retrieve a full list of UIDs in the folder. ui.warn("Quick syncs (-q) not supported in conjunction " "with maxage or startdate; ignoring -q.") if maxage != None: cachemessagelists_upto_date(maxage) check_uid_validity() elif localstart != None: cachemessagelists_startdate(remotefolder, localfolder, localstart) check_uid_validity() elif remotestart != None: cachemessagelists_startdate(localfolder, remotefolder, remotestart) check_uid_validity() else: localfolder.cachemessagelist() if quick: if (not localfolder.quickchanged(statusfolder) and not remotefolder.quickchanged(statusfolder)): ui.skippingfolder(remotefolder) localrepos.restore_atime() return check_uid_validity() remotefolder.cachemessagelist() # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'"% localrepos.getname()) # Synchronize local changes. if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'"% remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # Bubble up severe Errors, skip folder otherwise. if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg="Aborting sync, folder '%s' " "[acc: '%s']"% (localfolder, account)) except Exception as e: ui.error(e, msg="ERROR in syncfolder for %s folder %s: %s"% (account, remotefolder.getvisiblename(), traceback.format_exc())) finally: for folder in ["statusfolder", "localfolder", "remotefolder"]: if folder in locals(): locals()[folder].dropmessagelistcache() statusfolder.closefiles()
def syncfolder(accountname, remoterepos, remotefolder, localrepos, statusrepos, quick): """This function is called as target for the InstanceLimitedThread invokation in SyncableAccount.""" ui = getglobalui() ui.registerthread(accountname) try: # Load local folder. localfolder = localrepos.\ getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), localrepos.getsep())) # Write the mailboxes mbnames.add(accountname, localfolder.getvisiblename()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.getuidvalidity() == None: # This is a new folder, so delete the status cache to be sure # we don't have a conflict. statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and there is a UID # validity problem, warn and abort. If there are no messages, UW IMAPd # loses UIDVALIDITY. But we don't really need it if both local folders are # empty. So, in that case, just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.isuidvalidityok(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.isuidvalidityok(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: localfolder.saveuidvalidity() remotefolder.saveuidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconf('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug('imap', "Not syncing to read-only repository '%s'" \ % localrepos.getname()) # Synchronize local changes if not remoterepos.getconf('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'" \ % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError, e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.warn("Aborting folder sync '%s' [acc: '%s']\nReason was: %s" %\ (localfolder.name, accountname, e.reason))
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 ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Write the mailboxes mbnames.add(account.name, localfolder.getname(), localrepos.getlocalroot()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug('imap', "Not syncing to read-only repository '%s'" \ % localrepos.getname()) # Synchronize local changes if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'" \ % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg="Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (account, remotefolder.getvisiblename(), traceback.format_exc()))
def syncfolder(accountname, remoterepos, remotefolder, localrepos, statusrepos, quick): global mailboxes ui = UIBase.getglobalui() ui.registerthread(accountname) try: # Load local folder. localfolder = localrepos.\ getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), localrepos.getsep())) # Write the mailboxes mbnames.add(accountname, localfolder.getvisiblename()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.getuidvalidity() == None: # This is a new folder, so delete the status cache to be sure # we don't have a conflict. statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and there is a UID # validity problem, warn and abort. If there are no messages, UW IMAPd # loses UIDVALIDITY. But we don't really need it if both local folders are # empty. So, in that case, just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.isuidvalidityok(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.isuidvalidityok(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: localfolder.saveuidvalidity() remotefolder.saveuidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # if not statusfolder.isnewfolder(): # Delete local copies of remote messages. This way, # if a message's flag is modified locally but it has been # deleted remotely, we'll delete it locally. Otherwise, we # try to modify a deleted message's flags! This step # need only be taken if a statusfolder is present; otherwise, # there is no action taken *to* the remote repository. remotefolder.syncmessagesto_delete(localfolder, [localfolder, statusfolder]) ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder]) # Synchronize remote changes. ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, [localfolder, statusfolder]) # Make sure the status folder is up-to-date. ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder) localfolder.syncmessagesto(statusfolder) statusfolder.save() localrepos.restore_atime() except: ui.warn("ERROR in syncfolder for %s folder %s: %s" % \ (accountname,remotefolder.getvisiblename(),sys.exc_info()[1]))
def syncfolder(account, remotefolder, quick): """This function is called as target for the InstanceLimitedThread invokation in SyncableAccount. Filtered folders on the remote side will not invoke this function.""" remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Write the mailboxes mbnames.add(account.name, localfolder.getname()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() # Add localfolder to status repository, so we don't bother # uploading to the remote repository. This is often useful # if you nuked your status repository by setting maxage. # DO NOT RUN THIS IF YOU DID NOT DISABLE REMOTE->LOCAL DELETIONS!!! # XXX Make this a toggle that you can trigger from the command # line flags (but threading it through is a little nontrivial. if False: for uid in localfolder.getmessageuidlist(): statusfolder.savemessagefast(uid, None, localfolder.getmessageflags(uid), localfolder.getmessagetime(uid)) # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder, False) else: ui.debug('imap', "Not syncing to read-only repository '%s'" \ % localrepos.getname()) # Synchronize local changes if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder, True) else: ui.debug('', "Not syncing to read-only repository '%s'" \ % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (account, remotefolder.getvisiblename(), traceback.format_exc()))
def syncfolder(accountname, remoterepos, remotefolder, localrepos, statusrepos, quick): """This function is called as target for the InstanceLimitedThread invokation in SyncableAccount.""" ui = getglobalui() ui.registerthread(accountname) try: # Load local folder. localfolder = localrepos.\ getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), localrepos.getsep())) # Write the mailboxes mbnames.add(accountname, localfolder.getvisiblename()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.getuidvalidity() == None: # This is a new folder, so delete the status cache to be sure # we don't have a conflict. statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and there is a UID # validity problem, warn and abort. If there are no messages, UW IMAPd # loses UIDVALIDITY. But we don't really need it if both local folders are # empty. So, in that case, just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.isuidvalidityok(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.isuidvalidityok(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: localfolder.saveuidvalidity() remotefolder.saveuidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) # Synchronize local changes ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except: ui.warn("ERROR in syncfolder for %s folder %s: %s" % \ (accountname,remotefolder.getvisiblename(), traceback.format_exc()))
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 ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Write the mailboxes mbnames.add(account.name, localfolder.getname(), localrepos.getlocalroot()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug('imap', "Not syncing to read-only repository '%s'" \ % localrepos.getname()) # Synchronize local changes if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug('', "Not syncing to read-only repository '%s'" \ % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (account, remotefolder.getvisiblename(), traceback.format_exc())) finally: for folder in ["statusfolder", "localfolder", "remotefolder"]: if folder in locals(): locals()[folder].dropmessagelistcache()
def syncfolder(account, remotefolder, quick): """Synchronizes given remote folder for the specified account. Filtered folders on the remote side will not invoke this function.""" def check_uid_validity(localfolder, remotefolder, statusfolder): # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() > 0 or statusfolder.getmessagecount( ) > 0: if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localfolder.repository.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() def save_min_uid(folder, min_uid): uidfile = folder.get_min_uid_file() fd = open(uidfile, 'wt') fd.write(str(min_uid) + "\n") fd.close() def cachemessagelists_upto_date(localfolder, remotefolder, date): """ Returns messages with uid > min(uids of messages newer than date).""" localfolder.cachemessagelist(min_date=date) check_uid_validity(localfolder, remotefolder, statusfolder) # local messagelist had date restriction applied already. Restrict # sync to messages with UIDs >= min_uid from this list. # # local messagelist might contain new messages (with uid's < 0). positive_uids = [ uid for uid in localfolder.getmessageuidlist() if uid > 0 ] if len(positive_uids) > 0: remotefolder.cachemessagelist(min_uid=min(positive_uids)) else: # No messages with UID > 0 in range in localfolder. # date restriction was applied with respect to local dates but # remote folder timezone might be different from local, so be # safe and make sure the range isn't bigger than in local. remotefolder.cachemessagelist( min_date=time.gmtime(time.mktime(date) + 24 * 60 * 60)) def cachemessagelists_startdate(new, partial, date): """ Retrieve messagelists when startdate has been set for the folder 'partial'. Idea: suppose you want to clone the messages after date in one account (partial) to a new one (new). If new is empty, then copy messages in partial newer than date to new, and keep track of the min uid. On subsequent syncs, sync all the messages in new against those after that min uid in partial. This is a partial replacement for maxage in the IMAP-IMAP sync case, where maxage doesn't work: the UIDs of the messages in localfolder might not be in the same order as those of corresponding messages in remotefolder, so if L in local corresponds to R in remote, the ranges [L, ...] and [R, ...] might not correspond. But, if we're cloning a folder into a new one, [min_uid, ...] does correspond to [1, ...]. This is just for IMAP-IMAP. For Maildir-IMAP, use maxage instead. """ new.cachemessagelist() min_uid = partial.retrieve_min_uid() if min_uid == None: # min_uid file didn't exist if len(new.getmessageuidlist()) > 0: raise OfflineImapError( "To use startdate on Repository %s, " "Repository %s must be empty" % (partial.repository.name, new.repository.name), OfflineImapError.ERROR.MESSAGE) else: partial.cachemessagelist(min_date=date) # messagelist.keys() instead of getuidmessagelist() because in # the UID mapped case we want the actual local UIDs, not their # remote counterparts positive_uids = [ uid for uid in list(partial.messagelist.keys()) if uid > 0 ] if len(positive_uids) > 0: min_uid = min(positive_uids) else: min_uid = 1 save_min_uid(partial, min_uid) else: partial.cachemessagelist(min_uid=min_uid) remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = account.get_local_folder(remotefolder) # Write the mailboxes mbnames.add(account.name, localfolder.getname(), localrepos.getlocalroot()) # Load status folder. statusfolder = statusrepos.getfolder( remotefolder.getvisiblename().replace(remoterepos.getsep(), statusrepos.getsep())) statusfolder.openfiles() if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() # Load local folder. ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) # Retrieve messagelists, taking into account age-restriction # options maxage = localfolder.getmaxage() localstart = localfolder.getstartdate() remotestart = remotefolder.getstartdate() if (maxage != None) + (localstart != None) + (remotestart != None) > 1: six.reraise( OfflineImapError( "You can set at most one of the " "following: maxage, startdate (for the local folder), " "startdate (for the remote folder)", OfflineImapError.ERROR.REPO), None, exc_info()[2]) if (maxage != None or localstart or remotestart) and quick: # IMAP quickchanged isn't compatible with options that # involve restricting the messagelist, since the "quick" # check can only retrieve a full list of UIDs in the folder. ui.warn("Quick syncs (-q) not supported in conjunction " "with maxage or startdate; ignoring -q.") if maxage != None: cachemessagelists_upto_date(localfolder, remotefolder, maxage) elif localstart != None: cachemessagelists_startdate(remotefolder, localfolder, localstart) check_uid_validity(localfolder, remotefolder, statusfolder) elif remotestart != None: cachemessagelists_startdate(localfolder, remotefolder, remotestart) check_uid_validity(localfolder, remotefolder, statusfolder) else: localfolder.cachemessagelist() if quick: if (not localfolder.quickchanged(statusfolder) and not remotefolder.quickchanged(statusfolder)): ui.skippingfolder(remotefolder) localrepos.restore_atime() return check_uid_validity(localfolder, remotefolder, statusfolder) remotefolder.cachemessagelist() # Synchronize remote changes. if not localrepos.getconfboolean('readonly', False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug( '', "Not syncing to read-only repository '%s'" % localrepos.getname()) # Synchronize local changes. if not remoterepos.getconfboolean('readonly', False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug( '', "Not syncing to read-only repository '%s'" % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: ui.error(e, exc_info()[2], msg="Aborting sync, folder '%s' " "[acc: '%s']" % (localfolder, account)) except Exception as e: ui.error( e, msg="ERROR in syncfolder for %s folder %s: %s" % (account, remotefolder.getvisiblename(), traceback.format_exc())) finally: for folder in ["statusfolder", "localfolder", "remotefolder"]: if folder in locals(): locals()[folder].dropmessagelistcache() statusfolder.closefiles()
def syncfolder(account, remotefolder, quick): """This function is called as target for the InstanceLimitedThread invokation in SyncableAccount. Filtered folders on the remote side will not invoke this function.""" remoterepos = account.remoterepos localrepos = account.localrepos statusrepos = account.statusrepos ui = getglobalui() ui.registerthread(account) try: # Load local folder. localfolder = localrepos.getfolder( remotefolder.getvisiblename().replace(remoterepos.getsep(), localrepos.getsep()) ) # Filtered folders on the remote side will not invoke this # function, but we need to NOOP if the local folder is filtered # out too: if not localfolder.sync_this: ui.debug("", "Not syncing filtered local folder '%s'" % localfolder) return # Write the mailboxes mbnames.add(account.name, localfolder.getname()) # Load status folder. statusfolder = statusrepos.getfolder( remotefolder.getvisiblename().replace(remoterepos.getsep(), statusrepos.getsep()) ) if localfolder.get_uidvalidity() == None: # This is a new folder, so delete the status cache to be # sure we don't have a conflict. # TODO: This does not work. We always return a value, need # to rework this... statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, localfolder.getmessagecount()) # If either the local or the status folder has messages and # there is a UID validity problem, warn and abort. If there are # no messages, UW IMAPd loses UIDVALIDITY. But we don't really # need it if both local folders are empty. So, in that case, # just save it off. if localfolder.getmessagecount() or statusfolder.getmessagecount(): if not localfolder.check_uidvalidity(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.check_uidvalidity(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: # Both folders empty, just save new UIDVALIDITY localfolder.save_uidvalidity() remotefolder.save_uidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, remotefolder.getmessagecount()) # Synchronize remote changes. if not localrepos.getconfboolean("readonly", False): ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, statusfolder) else: ui.debug("imap", "Not syncing to read-only repository '%s'" % localrepos.getname()) # Synchronize local changes if not remoterepos.getconfboolean("readonly", False): ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(remotefolder, statusfolder) else: ui.debug("", "Not syncing to read-only repository '%s'" % remoterepos.getname()) statusfolder.save() localrepos.restore_atime() except (KeyboardInterrupt, SystemExit): raise except OfflineImapError as e: # bubble up severe Errors, skip folder otherwise if e.severity > OfflineImapError.ERROR.FOLDER: raise else: # if the initial localfolder assignement bailed out, the localfolder var will not be available, so we need ui.error( e, exc_info()[2], msg="Aborting sync, folder '%s' " "[acc: '%s']" % (remotefolder.getvisiblename().replace(remoterepos.getsep(), localrepos.getsep()), account), ) # we reconstruct foldername above rather than using # localfolder, as the localfolder var is not # available if assignment fails. except Exception as e: ui.error( e, msg="ERROR in syncfolder for %s folder %s: %s" % (account, remotefolder.getvisiblename(), traceback.format_exc()), )
def syncfolder(accountname, remoterepos, remotefolder, localrepos, statusrepos, quick): global mailboxes ui = UIBase.getglobalui() ui.registerthread(accountname) try: # Load local folder. localfolder = localrepos.\ getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), localrepos.getsep())) # Write the mailboxes mbnames.add(accountname, localfolder.getvisiblename()) # Load status folder. statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().\ replace(remoterepos.getsep(), statusrepos.getsep())) if localfolder.getuidvalidity() == None: # This is a new folder, so delete the status cache to be sure # we don't have a conflict. statusfolder.deletemessagelist() statusfolder.cachemessagelist() if quick: if not localfolder.quickchanged(statusfolder) \ and not remotefolder.quickchanged(statusfolder): ui.skippingfolder(remotefolder) localrepos.restore_atime() return # Load local folder ui.syncingfolder(remoterepos, remotefolder, localrepos, localfolder) ui.loadmessagelist(localrepos, localfolder) localfolder.cachemessagelist() ui.messagelistloaded(localrepos, localfolder, len(localfolder.getmessagelist().keys())) # If either the local or the status folder has messages and there is a UID # validity problem, warn and abort. If there are no messages, UW IMAPd # loses UIDVALIDITY. But we don't really need it if both local folders are # empty. So, in that case, just save it off. if len(localfolder.getmessagelist()) or len( statusfolder.getmessagelist()): if not localfolder.isuidvalidityok(): ui.validityproblem(localfolder) localrepos.restore_atime() return if not remotefolder.isuidvalidityok(): ui.validityproblem(remotefolder) localrepos.restore_atime() return else: localfolder.saveuidvalidity() remotefolder.saveuidvalidity() # Load remote folder. ui.loadmessagelist(remoterepos, remotefolder) remotefolder.cachemessagelist() ui.messagelistloaded(remoterepos, remotefolder, len(remotefolder.getmessagelist().keys())) # if not statusfolder.isnewfolder(): # Delete local copies of remote messages. This way, # if a message's flag is modified locally but it has been # deleted remotely, we'll delete it locally. Otherwise, we # try to modify a deleted message's flags! This step # need only be taken if a statusfolder is present; otherwise, # there is no action taken *to* the remote repository. remotefolder.syncmessagesto_delete(localfolder, [localfolder, statusfolder]) ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder) localfolder.syncmessagesto(statusfolder, [remotefolder, statusfolder]) # Synchronize remote changes. ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder) remotefolder.syncmessagesto(localfolder, [localfolder, statusfolder]) # Make sure the status folder is up-to-date. ui.syncingmessages(localrepos, localfolder, statusrepos, statusfolder) localfolder.syncmessagesto(statusfolder) statusfolder.save() localrepos.restore_atime() except: ui.warn("ERROR in syncfolder for " + accountname + " folder " + \ remotefolder.getvisiblename() +" : " +str(sys.exc_info()[1]))