Exemplo n.º 1
0
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()))
Exemplo n.º 2
0
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()
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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))
Exemplo n.º 5
0
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()))
Exemplo n.º 6
0
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]))
Exemplo n.º 7
0
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))
Exemplo n.º 8
0
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()))
Exemplo n.º 9
0
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()))
Exemplo n.º 10
0
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()
Exemplo n.º 11
0
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()))
Exemplo n.º 12
0
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()
Exemplo n.º 13
0
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()),
        )
Exemplo n.º 14
0
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]))