def _check_accepted_state(self, accepted_state, storage_content):
        """
        If there was an accepted_state, checks if the declared state is
        coherent with the accepted one.

        The server cannot declare same basis with different content or
        viceversa.

        @param accepted_state:
                an accepted state, None if not state was accepted or a
                string as "server_basis<space>get_hash(declared_content)"
        @param storage_content:
                list of files declared from server
        """
        declared_content = get_hash(
            remove_lmtime_from_filelist(storage_content))
        if accepted_state is not None:
            accepted_basis, accepted_content = accepted_state.split()
            same_basis = (accepted_basis == self.server_basis)
            same_content = (accepted_content == declared_content)

            if (same_basis and not same_content)\
            or (same_content and not same_basis):
                raise HashMismatchException(
                    'ERROR! Basis mismatch Server declare inconsistent ServerBasis and StorageContent'
                )
    def _catch_wrong_states(self, storage_content, accepted_state,
                            out_of_sync):
        """Perform basic integrity checks on the basis/filelist sent
        by the server.

        The (basis, filelist) pair can be one of those that we already
        know: the trusted, the candidate, the accepted. In such case
        there is nothing new to synchronize. If both the elements of the
        pair have changed then we have to ask the user for confirmation.
        If only one has changed then we can tell for sure that something
        is wrong about the integrity of this synchronization.

        @param storage_content:
                    Filelist of the remote storage.
        @param accepted_state:
                    A (basis, filelist_hash) pair that we accepted on
                    last synchronization, if any.
        @param out_of_sync:
                    Boolean flag telling whether there are remote
                    modification to replicate on the local data.
        """

        declared_content = get_hash(
            remove_lmtime_from_filelist(storage_content))
        current_state = "%s %s" % (self.server_basis, declared_content)

        self.logger.debug('storage_content = %s', storage_content)
        self.logger.debug('server_state = %s', current_state)
        self.logger.debug('accepted_state = %s', accepted_state)
        self.logger.debug('out_of_sync = %s', out_of_sync)

        if out_of_sync:
            if self.server_basis == self.client_basis:
                raise HashMismatchException(
                    'ERROR! Something to sync with same basis')
            else:
                self._check_accepted_state(accepted_state, storage_content)
        else:
            if self.server_basis == self.client_basis:
                accepted_state = None

            self._check_accepted_state(accepted_state, storage_content)

            if accepted_state is not None:
                accepted_basis, _ = accepted_state.split()

            if self.candidate_basis is None:
                check_candidate_basis = True
            else:
                check_candidate_basis = (self.server_basis !=
                                         self.candidate_basis)

            if (accepted_state is None or self.server_basis != accepted_basis) \
            and self.server_basis != self.client_basis \
            and check_candidate_basis:
                raise HashMismatchException(
                    'ERROR! Basis mismatch but nothing to sync')

        return current_state
Beispiel #3
0
    def _catch_wrong_states(self,
                            storage_content,
                            accepted_state,
                            out_of_sync):
        """Perform basic integrity checks on the basis/filelist sent
        by the server.

        The (basis, filelist) pair can be one of those that we already
        know: the trusted, the candidate, the accepted. In such case
        there is nothing new to synchronize. If both the elements of the
        pair have changed then we have to ask the user for confirmation.
        If only one has changed then we can tell for sure that something
        is wrong about the integrity of this synchronization.

        @param storage_content:
                    Filelist of the remote storage.
        @param accepted_state:
                    A (basis, filelist_hash) pair that we accepted on
                    last synchronization, if any.
        @param out_of_sync:
                    Boolean flag telling whether there are remote
                    modification to replicate on the local data.
        """

        declared_content = get_hash(storage_content)
        current_state = "%s %s" % (self.server_basis, declared_content)

        self.logger.debug('storage_content = %s', storage_content)
        self.logger.debug('current_state = %s', current_state)
        self.logger.debug('accepted_state = %s', accepted_state)
        self.logger.debug('out_of_sync = %s', out_of_sync)

        if out_of_sync:
            if self.server_basis == self.client_basis:
                raise HashMismatchException('ERROR! Something to sync with same basis')
            else:
                self._check_accepted_state(accepted_state, storage_content)
        else:
            if self.server_basis == self.client_basis:
                accepted_state = None

            self._check_accepted_state(accepted_state, storage_content)

            if accepted_state is not None:
                accepted_basis, _ = accepted_state.split()

            if self.candidate_basis is None:
                check_candidate_basis = True
            else:
                check_candidate_basis = (self.server_basis != self.candidate_basis)

            if (accepted_state is None or self.server_basis != accepted_basis) \
            and self.server_basis != self.client_basis \
            and check_candidate_basis:
                raise HashMismatchException('ERROR! Basis mismatch but nothing to sync')

        return current_state
Beispiel #4
0
    def _check_accepted_state(self, accepted_state, storage_content):
        """
        If there was an accepted_state, checks if the declared state is
        coherent with the accepted one.

        The server cannot declare same basis with different content or
        viceversa.

        @param accepted_state:
                an accepted state, None if not state was accepted or a
                string as "server_basis<space>get_hash(declared_content)"
        @param storage_content:
                list of files declared from server
        """
        declared_content = get_hash(storage_content)
        if accepted_state is not None:
            accepted_basis, accepted_content = accepted_state.split()
            same_basis = (accepted_basis == self.server_basis)
            same_content = (accepted_content == declared_content)

            if (same_basis and not same_content)\
            or (same_content and not same_basis):
                raise HashMismatchException('ERROR! Basis mismatch Server declare inconsistent ServerBasis and StorageContent')
Beispiel #5
0
    def _check_hash_mismatch(self):
        """Handles the hash mismatch, if any.

        An "hash mismatch" means that the content on the storage is
        different from what we remember from our last connection. We
        have to ask the user to tell if such modifications are acceptable
        (meaning that the user himself did them with another client)
        or if it's the result of malicious data tampering. The user
        will match both the hash (together with its visual representation,
        the "robohash") and the list of remote modification to replicate
        on the local data to make a decision.

        @return
                    Boolean telling whether it's OK to proceed with the
                    synchronization or not.
        """
        # Collect the data resulting from the diff algorithm
        diff = self._context.startup_synchronization
        content_to_download = sorted(diff.content_to_download)
        content_to_delete_locally = sorted(diff.content_to_delete_locally)
        content_to_delete_locally.reverse()
        edit_conflicts = diff.edit_conflicts
        deletion_conflicts = diff.deletion_conflicts

        # Any pathname to synchronize?
        something_to_sync = len(content_to_download) > 0
        something_to_sync |= len(content_to_delete_locally) > 0
        something_to_sync |= len(deletion_conflicts) > 0

        # Any modification to the storage cache to do?
        storage_cache_needs_update = len(diff.remote_deletions) > 0
        storage_cache_needs_update |= len(diff.ignored_conflicts) > 0

        # Anything the user should be notified of by opening the dialog?
        out_of_sync = something_to_sync or storage_cache_needs_update

        remote_sizes = self._context.startup_synchronization.remote_size
        local_sizes = self._context.startup_synchronization.local_size

        accepted_state = self._context.metadataDB.try_get(
                                                metadata.LASTACCEPTEDSTATEKEY)

#        if there is an accepted basis but it is the same of the current basis
#        and there is nothing to sync, maybe the client was crashed before
#        removing it from storage_cache
        if accepted_state is not None:
            accepted_basis, _ = accepted_state.split()
            if not out_of_sync and self.client_basis == accepted_basis:
                self._context.metadataDB.delete_key(metadata.LASTACCEPTEDSTATEKEY)
                accepted_state = None

        if self.client_basis is not None: # First Start, I cannot check anything
            current_state = self._catch_wrong_states(self.storage_content,
                                                     accepted_state,
                                                     out_of_sync)

        if self.client_basis is None:
            client_server_differ = True
        else:
            client_server_differ = (self.server_basis != self.client_basis)

        if self.candidate_basis is None:
            candidate_server_differ = True
        else:
            candidate_server_differ = (self.server_basis != self.candidate_basis)

        # Ask the user if he's OK with proceeding with the synchronization
        if self.client_basis is None or accepted_state is None \
        or accepted_state != current_state:
            if client_server_differ and candidate_server_differ:
                if out_of_sync \
                and not self._user_accepts_synchronization(
                            content_to_download, content_to_delete_locally,
                            edit_conflicts, deletion_conflicts,
                            self.client_basis, self.server_basis,
                            remote_sizes, local_sizes):
                    self.logger.warning(
                        u'User has refused the synchronization, shutting down')
                    return False

                storage_list_hash = get_hash(self.storage_content)
                to_save = "%s %s" % (self.server_basis, storage_list_hash)
                self._context.metadataDB.set(metadata.LASTACCEPTEDSTATEKEY, to_save)
                self._context._save_basis_in_history(self.client_basis,
                                                     self.server_basis,
                                                     True)

        if self._context._internal_facade.is_first_startup():
            self._context._save_basis_in_history(self.client_basis,
                                                 self.server_basis,
                                                 True)

        return True
    def _check_hash_mismatch(self):
        """Handles the hash mismatch, if any.

        An "hash mismatch" means that the content on the storage is
        different from what we remember from our last connection. We
        have to ask the user to tell if such modifications are acceptable
        (meaning that the user himself did them with another client)
        or if it's the result of malicious data tampering. The user
        will match both the hash (together with its visual representation,
        the "robohash") and the list of remote modification to replicate
        on the local data to make a decision.

        @return
                    Boolean telling whether it's OK to proceed with the
                    synchronization or not.
        """
        # Collect the data resulting from the diff algorithm
        diff = self._context.startup_synchronization
        content_to_download = sorted(diff.content_to_download)
        content_to_delete_locally = sorted(diff.content_to_delete_locally)
        content_to_delete_locally.reverse()
        edit_conflicts = diff.edit_conflicts
        deletion_conflicts = diff.deletion_conflicts

        # Any pathname to synchronize?
        something_to_sync = len(content_to_download) > 0
        something_to_sync |= len(content_to_delete_locally) > 0
        something_to_sync |= len(deletion_conflicts) > 0

        # Any modification to the storage cache to do?
        storage_cache_needs_update = len(diff.remote_deletions) > 0
        storage_cache_needs_update |= len(diff.ignored_conflicts) > 0

        # Anything the user should be notified of by opening the dialog?
        out_of_sync = something_to_sync or storage_cache_needs_update

        remote_sizes = self._context.startup_synchronization.remote_size
        local_sizes = self._context.startup_synchronization.local_size

        accepted_state = self._context.metadataDB.try_get(
            metadata.LASTACCEPTEDSTATEKEY)

        #        if there is an accepted basis but it is the same of the current basis
        #        and there is nothing to sync, maybe the client was crashed before
        #        removing it from storage_cache
        if accepted_state is not None:
            accepted_basis, _ = accepted_state.split()
            if not out_of_sync and self.client_basis == accepted_basis:
                self._context.metadataDB.delete_key(
                    metadata.LASTACCEPTEDSTATEKEY)
                accepted_state = None

        if self.client_basis is not None:  # First Start, I cannot check anything
            current_state = self._catch_wrong_states(self.storage_content,
                                                     accepted_state,
                                                     out_of_sync)

        if self.client_basis is None:
            client_server_differ = True
        else:
            client_server_differ = (self.server_basis != self.client_basis)

        if self.candidate_basis is None:
            candidate_server_differ = True
        else:
            candidate_server_differ = (self.server_basis !=
                                       self.candidate_basis)

        # Ask the user if he's OK with proceeding with the synchronization
        if self.client_basis is None or accepted_state is None \
        or accepted_state != current_state:
            if client_server_differ and candidate_server_differ:
                if out_of_sync \
                and not self._user_accepts_synchronization(
                            content_to_download, content_to_delete_locally,
                            edit_conflicts, deletion_conflicts,
                            self.client_basis, self.server_basis,
                            remote_sizes, local_sizes):
                    self.logger.warning(
                        u'User has refused the synchronization, shutting down')
                    return False

                storage_list_hash = get_hash(
                    remove_lmtime_from_filelist(self.storage_content))
                to_save = "%s %s" % (self.server_basis, storage_list_hash)
                self._context.metadataDB.set(metadata.LASTACCEPTEDSTATEKEY,
                                             to_save)
                self._save_basis_in_history(self.client_basis,
                                            self.server_basis, True)

        if self._context._internal_facade.is_first_startup():
            self._save_basis_in_history(self.client_basis, self.server_basis,
                                        True)

        return True