Ejemplo n.º 1
0
class RestoreSettings(object):
    """Class responsible for restoring settings from a 7z backup file
    created by BackupSettings. We need bass.dirs initialized to restore the
    settings, which depends on the bash.ini - so this exports also functions
    to restore the backed up ini, if it exists. Restoring the settings must
    be done on boot as soon as we are able to initialize bass#dirs."""
    __tmpdir_prefix = u'RestoreSettingsWryeBash_'
    __unset = object()

    def __init__(self, settings_file):
        self._settings_file = GPath(settings_file)
        self._saved_settings_version = self._settings_saved_with = None
        # bash.ini handling
        self._timestamped_old = self.__unset
        self._bash_ini_path = self.__unset
        # extract folder
        self._extract_dir = self.__unset

    # Restore API -------------------------------------------------------------
    def extract_backup(self):
        """Extract the backup file and return the tmp directory used. If
        the backup file is a dir we assume it was created by us before
        restarting."""
        if self._settings_file.isfile():
            temp_dir = bolt.Path.tempDir(
                prefix=RestoreSettings.__tmpdir_prefix)
            archives.extract7z(self._settings_file, temp_dir)
            self._extract_dir = temp_dir
        elif self._settings_file.isdir():
            self._extract_dir = self._settings_file
        else:
            raise BoltError(u'%s is not a valid backup location' %
                            self._settings_file)
        return self._extract_dir

    def backup_ini_path(self):
        """Get the path to the backup bash.ini if it exists - must be run
        before restore_settings is called, as we need the Bash ini to
        initialize bass.dirs."""
        if self._extract_dir is self.__unset:
            raise BoltError(
                u'backup_ini_path: you must extract the settings file first')
        for r, d, fs in bolt.walkdir(u'%s' % self._extract_dir):
            for f in fs:
                if f == u'bash.ini':
                    self._bash_ini_path = jo(r, f)
                    break
        else:
            self.bash_ini_path = None
        return self._bash_ini_path

    def restore_settings(self, fsName):
        if self._bash_ini_path is self.__unset:
            raise BoltError(
                u'restore_settings: you must handle bash ini first')
        if self._extract_dir is self.__unset:
            raise BoltError(
                u'restore_settings: you must extract the settings file first')
        try:
            self._restore_settings(fsName)
        finally:
            self.remove_extract_dir(self._extract_dir)

    def _restore_settings(self, fsName):
        deprint(u'')
        deprint(_(u'RESTORE BASH SETTINGS: ') + self._settings_file.s)
        # backup previous Bash ini if it exists
        old_bash_ini = dirs['mopy'].join(u'bash.ini')
        self._timestamped_old = u''.join(
            [old_bash_ini.root.s, u'(',
             bolt.timestamp(), u').ini'])
        try:
            old_bash_ini.moveTo(self._timestamped_old)
            deprint(u'Existing bash.ini moved to %s' % self._timestamped_old)
        except StateError:  # does not exist
            self._timestamped_old = None
        # restore all the settings files
        restore_paths = _init_settings_files(fsName).keys()
        for dest_dir, back_path in restore_paths:
            full_back_path = self._extract_dir.join(back_path)
            for name in full_back_path.list():
                if full_back_path.join(name).isfile():
                    deprint(
                        GPath(back_path).join(name).s + u' --> ' +
                        dest_dir.join(name).s)
                    full_back_path.join(name).copyTo(dest_dir.join(name))
        # restore savegame profile settings
        back_path = GPath(u'My Games').join(fsName, u'Saves')
        saves_dir = dirs['saveBase'].join(u'Saves')
        full_back_path = self._extract_dir.join(back_path)
        if full_back_path.exists():
            for root_dir, folders, files_ in full_back_path.walk(
                    True, None, True):
                root_dir = GPath(u'.' + root_dir.s)
                for name in files_:
                    deprint(
                        back_path.join(root_dir, name).s + u' --> ' +
                        saves_dir.join(root_dir, name).s)
                    full_back_path.join(root_dir, name).copyTo(
                        saves_dir.join(root_dir, name))

    # Validation --------------------------------------------------------------
    def incompatible_backup_error(self, current_game):
        saved_settings_version, settings_saved_with = \
            self._get_settings_versions()
        if saved_settings_version > bass.settings['bash.version']:
            # Disallow restoring settings saved on a newer version of bash # TODO(ut) drop?
            return u'\n'.join([
                _(u'The data format of the selected backup file is newer than '
                  u'the current Bash version!'),
                _(u'Backup v%s is not compatible with v%s') %
                (saved_settings_version, bass.settings['bash.version']), u'',
                _(u'You cannot use this backup with this version of '
                  u'Bash.')
            ]), _(u'Error: Settings are from newer Bash version')
        else:
            game_name = self._get_backup_game()
            if game_name != current_game:
                return u'\n'.join([
                    _(u'The selected backup file is for %(game_name)s while '
                      u'your current game is %(current_game)s') % locals(),
                    _(u'You cannot use this backup with this game.')
                ]), _(u'Error: Settings are from a different game')
        return u'', u''

    def incompatible_backup_warn(self):
        saved_settings_version, settings_saved_with = \
            self._get_settings_versions()
        if settings_saved_with != bass.settings['bash.version']:
            return u'\n'.join([
                _(u'The version of Bash used to create the selected backup '
                  u'file does not match the current Bash version!'),
                _(u'Backup v%s does not match v%s') %
                (settings_saved_with, bass.settings['bash.version']), u'',
                _(u'Do you want to restore this backup anyway?')
            ]), _(u'Warning: Version Mismatch!')
        return u'', u''

    def _get_settings_versions(self):
        if self._extract_dir is self.__unset:
            raise BoltError(
                u'_get_settings_versions: you must extract the settings file '
                u'first')
        if self._saved_settings_version is None:
            backup_dat = self._extract_dir.join(u'backup.dat')
            try:
                with backup_dat.open('rb') as ins:
                    # version of Bash that created the backed up settings
                    self._saved_settings_version = cPickle.load(ins)
                    # version of Bash that created the backup
                    self._settings_saved_with = cPickle.load(ins)
            except (OSError, IOError, cPickle.UnpicklingError, EOFError):
                raise_bolt_error(u'Failed to read %s' % backup_dat)
        return self._saved_settings_version, self._settings_saved_with

    def _get_backup_game(self):
        """Get the game this backup was for - hack, this info belongs to backup.dat."""
        for node in os.listdir(u'%s' % self._extract_dir):
            if node != u'My Games' and not node.endswith(
                    u'Mods') and os.path.isdir(self._extract_dir.join(node).s):
                return node
        raise BoltError(u'%s does not contain a game dir' % self._extract_dir)

    # Dialogs and cleanup/error handling --------------------------------------
    @staticmethod
    def remove_extract_dir(backup_dir):
        backup_dir.rmtree(safety=RestoreSettings.__tmpdir_prefix)

    def restore_ini(self):
        if self._timestamped_old is self.__unset:
            return  # we did not move bash.ini
        if self._timestamped_old is not None:
            bolt.deprint(u'Restoring bash.ini')
            GPath(self._timestamped_old).copyTo(u'bash.ini')
        elif self._bash_ini_path:
            # remove bash.ini as it is the one from the backup
            bolt.GPath(u'bash.ini').remove()

    @staticmethod
    def warn_message(balt_, msg=u''):
        if balt_ is None: return
        balt_.showWarning(
            balt_.Link.Frame, u'\n'.join([
                _(u'There was an error while trying to restore your settings from '
                  u'the backup file!'), msg,
                _(u'No settings were restored.')
            ]), _(u'Unable to restore backup!'))
Ejemplo n.º 2
0
def test_permissions(path, permissions='rwcd'):
    """Test file permissions for a path:
        r = read permission
        w = write permission
        c = file creation permission
        d = file deletion permission"""
    return True  # Temporarily disabled, for testing purposes
    path = GPath(path)
    permissions = permissions.lower()

    def getTemp(path_):  # Get a temp file name
        if path_.isdir():
            tmp = path_.join(u'temp.temp')
        else:
            tmp = path_.temp
        while tmp.exists():
            tmp = tmp.temp
        return tmp

    def getSmallest():  # Get the smallest file in the directory
        if path.isfile(): return path
        smallsize = -1
        ret = None
        for node in path.list():
            node = path.join(node)
            if not node.isfile(): continue
            node_size = node.size
            if smallsize == -1 or node_size < smallsize:
                smallsize = node_size
                ret = node
        return ret

    #--Test read permissions
    try:
        smallestFile = None
        path_exists = path.exists()
        if 'r' in permissions and path_exists:
            smallestFile = getSmallest()
            if smallestFile:
                with smallestFile.open('rb'):
                    pass
        #--Test write permissions
        if 'w' in permissions and path_exists:
            smallestFile = smallestFile or getSmallest()
            if smallestFile:
                with smallestFile.open('ab'):
                    pass
        #--Test file creation permission (only for directories)
        if 'c' in permissions:
            if path.isdir() or not path_exists:
                if not path_exists:
                    path.makedirs()
                    removeAtEnd = True
                else:
                    removeAtEnd = False
                temp = getTemp(path)
                with temp.open('wb'):
                    pass
                temp.remove()
                if removeAtEnd:
                    path.removedirs()
        #--Test file deletion permission
        if 'd' in permissions and path_exists:
            smallestFile = smallestFile or getSmallest()
            if smallestFile:
                temp = getTemp(smallestFile)
                smallestFile.copyTo(temp)
                smallestFile.remove()
                temp.moveTo(smallestFile)
    except Exception as e:
        if getattr(e, 'errno', 0) == 13:
            return False  # Access denied
        elif getattr(e, 'winerror', 0) == 183:
            return False  # Cannot create file if already exists
        else:
            raise
    return True