Пример #1
0
 def load_variables(self, prof_path):
     """Loads the variables for the given profile"""
     if os.path.isfile(prof_path):
         with open_file_read(prof_path) as f_in:
             for line in f_in:
                 line = line.strip()
                 # If any includes, load variables from them first
                 match = re_match_include(line)
                 if match:
                     new_path = self.PROF_DIR + '/' + match
                     self.load_variables(new_path)
                 else:
                     # Remove any comments
                     if '#' in line:
                         line = line.split('#')[0].rstrip()
                     # Expected format is @{Variable} = value1 value2 ..
                     if line.startswith('@') and '=' in line:
                         if '+=' in line:
                             line = line.split('+=')
                             try:
                                 self.severity['VARIABLES'][line[0]] += [i.strip('"') for i in line[1].split()]
                             except KeyError:
                                 raise AppArmorException("Variable %s was not previously declared, but is being assigned additional value in file: %s" % (line[0], prof_path))
                         else:
                             line = line.split('=')
                             if line[0] in self.severity['VARIABLES'].keys():
                                 raise AppArmorException("Variable %s was previously declared in file: %s" % (line[0], prof_path))
                             self.severity['VARIABLES'][line[0]] = [i.strip('"') for i in line[1].split()]
Пример #2
0
    def _run_test(self, params, expected):
        with open_file_read(params['file']) as f_in:
            data = f_in.readlines()

        if params['disabled']:
            # skip disabled testcases
            return

        if params['tools_wrong']:
            # if the tools are marked as being wrong about a profile, expect the opposite result
            # this makes sure we notice any behaviour change, especially not being wrong anymore
            expected = not expected

        # make sure the profile is known in active_profiles.files
        apparmor.active_profiles.init_file(params['file'])

        if expected:
            apparmor.parse_profile_data(data, params['file'], 0)
            apparmor.active_profiles.get_all_merged_variables(
                params['file'],
                apparmor.include_list_recursive(
                    apparmor.active_profiles.files[params['file']]))

        else:
            with self.assertRaises(AppArmorException):
                apparmor.parse_profile_data(data, params['file'], 0)
                apparmor.active_profiles.get_all_merged_variables(
                    params['file'],
                    apparmor.include_list_recursive(
                        apparmor.active_profiles.files[params['file']]))
Пример #3
0
def _raw_transform(fin, fout):
    '''Copy input file to output file'''
    if fin.endswith('.profile'):
        name = os.path.basename(os.path.splitext(fin)[0])
    else:
        name = os.path.basename(fin)

    orig = open_file_read(fin).read()
    out = re.sub(r'###PROFILEATTACH###', 'profile "%s"' % name, orig)

    tmp = name.split("_")
    if len(tmp) != 3:
        raise AppArmorException("unable to parse profile name %s" % (name))
    (package, appname, version) = tuple(tmp)

    profile_vars = '''@{CLICK_DIR}="%s"
@{APP_PKGNAME}="%s"
@{APP_APPNAME}="%s"
@{APP_VERSION}="%s"''' % (_get_click_dir_variable(), package, appname, version)

    out = re.sub(r'###VAR###', profile_vars, out)

    tmp, tmp_fn = tempfile.mkstemp(prefix='aa-profile-hook')
    if not isinstance(out, bytes):
        out = out.encode('utf-8')
    os.write(tmp, out)
    os.close(tmp)

    shutil.move(tmp_fn, fout)
    os.chmod(fout, 0o644)
Пример #4
0
    def read_log(self, logmark):
        self.logmark = logmark
        seenmark = True
        if self.logmark:
            seenmark = False
        #last = None
        #event_type = None
        try:
            #print(self.filename)
            self.LOG = open_file_read(self.filename)
        except IOError:
            raise AppArmorException('Can not read AppArmor logfile: ' + self.filename)
        #LOG = open_file_read(log_open)
        line = True
        while line:
            line = self.get_next_log_entry()
            if not line:
                break
            line = line.strip()
            self.debug_logger.debug('read_log: %s' % line)
            if self.logmark in line:
                seenmark = True

            self.debug_logger.debug('read_log: seenmark = %s' % seenmark)
            if not seenmark:
                continue

            event = self.parse_log_record(line)
            #print(event)
            if event:
                self.add_event_to_tree(event)
        self.LOG.close()
        self.logmark = ''
        return self.log
Пример #5
0
    def __init__(self, dbname=None, default_rank=10):
        """Initialises the class object"""
        self.PROF_DIR = '/etc/apparmor.d'  # The profile directory
        self.NOT_IMPLEMENTED = '_-*not*implemented*-_'  # used for rule types that don't have severity ratings
        self.severity = dict()
        self.severity['DATABASENAME'] = dbname
        self.severity['CAPABILITIES'] = {}
        self.severity['FILES'] = {}
        self.severity['REGEXPS'] = {}
        self.severity['DEFAULT_RANK'] = default_rank
        # For variable expansions for the profile
        self.severity['VARIABLES'] = dict()
        if not dbname:
            raise AppArmorException("No severity db file given")

        with open_file_read(dbname) as database:  # open(dbname, 'r')
            for lineno, line in enumerate(database, start=1):
                line = line.strip()  # or only rstrip and lstrip?
                if line == '' or line.startswith('#'):
                    continue
                if line.startswith('/'):
                    try:
                        path, read, write, execute = line.split()
                        read, write, execute = int(read), int(write), int(execute)
                    except ValueError:
                        raise AppArmorException("Insufficient values for permissions in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
                    else:
                        if read not in range(0, 11) or write not in range(0, 11) or execute not in range(0, 11):
                            raise AppArmorException("Inappropriate values for permissions in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
                        path = path.lstrip('/')
                        if '*' not in path:
                            self.severity['FILES'][path] = {'r': read, 'w': write, 'x': execute}
                        else:
                            ptr = self.severity['REGEXPS']
                            pieces = path.split('/')
                            for index, piece in enumerate(pieces):
                                if '*' in piece:
                                    path = '/'.join(pieces[index:])
                                    regexp = convert_regexp(path)
                                    ptr[regexp] = {'AA_RANK': {'r': read, 'w': write, 'x': execute}}
                                    break
                                else:
                                    ptr[piece] = ptr.get(piece, {})
                                    ptr = ptr[piece]
                elif line.startswith('CAP_'):
                    try:
                        resource, severity = line.split()
                        severity = int(severity)
                    except ValueError:
                        error_message = 'No severity value present in file: %s\n\t[Line %s]: %s' % (dbname, lineno, line)
                        #error(error_message)
                        raise AppArmorException(error_message)  # from None
                    else:
                        if severity not in range(0, 11):
                            raise AppArmorException("Inappropriate severity value present in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
                        self.severity['CAPABILITIES'][resource] = severity
                else:
                    raise AppArmorException("Unexpected line in file: %s\n\t[Line %s]: %s" % (dbname, lineno, line))
Пример #6
0
def py2_parser(filename):
    """Returns the de-dented ini file from the new format ini"""
    tmp = tempfile.NamedTemporaryFile('rw')
    f_out = open(tmp.name, 'w')
    if os.path.exists(filename):
        with open_file_read(filename) as f_in:
            for line in f_in:
                # The ini format allows for multi-line entries, with the subsequent
                # entries being indented deeper hence simple lstrip() is not appropriate
                if line[:2] == '  ':
                    line = line[2:]
                elif line[0] == '\t':
                    line = line[1:]
                f_out.write(line)
    f_out.flush()
    return tmp
Пример #7
0
def py2_parser(filename):
    """Returns the de-dented ini file from the new format ini"""
    tmp = tempfile.NamedTemporaryFile('rw')
    f_out = open(tmp.name, 'w')
    if os.path.exists(filename):
        with open_file_read(filename) as f_in:
            for line in f_in:
                # The ini format allows for multi-line entries, with the subsequent
                # entries being indented deeper hence simple lstrip() is not appropriate
                if line[:2] == '  ':
                    line = line[2:]
                elif line[0] == '\t':
                    line = line[1:]
                f_out.write(line)
    f_out.flush()
    return tmp
    def read_log(self, logmark):
        self.logmark = logmark
        seenmark = True
        if self.logmark:
            seenmark = False
        #last = None
        #event_type = None
        try:
            #print(self.filename)
            self.LOG = open_file_read(self.filename)
        except IOError:
            raise AppArmorException('Can not read AppArmor logfile: ' +
                                    self.filename)
        #LOG = open_file_read(log_open)
        line = True
        while line:
            line = self.get_next_log_entry()
            if not line:
                break
            line = line.strip()
            self.debug_logger.debug('read_log: %s' % line)
            if self.logmark in line:
                seenmark = True

            self.debug_logger.debug('read_log: seenmark = %s' % seenmark)
            if not seenmark:
                continue

            event = self.parse_log_record(line)
            #print(event)
            if event:
                try:
                    self.add_event_to_tree(event)
                except AppArmorException as e:
                    ex_msg = (
                        '%(msg)s\n\nThis error was caused by the log line:\n%(logline)s'
                        % {
                            'msg': e.value,
                            'logline': line
                        })
                    # when py3 only: Drop the original AppArmorException by passing None as the parent exception
                    raise AppArmorBug(ex_msg)  # py3-only: from None

        self.LOG.close()
        self.logmark = ''
        return self.log
Пример #9
0
 def read_shell(self, filepath):
     """Reads the shell type conf files and returns config[''][option]=value"""
     config = {'': dict()}
     with open_file_read(filepath) as conf_file:
         for line in conf_file:
             result = shlex.split(line, True)
             # If not a comment of empty line
             if result:
                 # option="value" or option=value type
                 if '=' in result[0]:
                     option, value = result[0].split('=')
                 # option type
                 else:
                     option = result[0]
                     value = None
                 config[''][option] = value
     return config
Пример #10
0
 def read_shell(self, filepath):
     """Reads the shell type conf files and returns config[''][option]=value"""
     config = {'': dict()}
     with open_file_read(filepath) as conf_file:
         for line in conf_file:
             result = shlex.split(line, True)
             # If not a comment of empty line
             if result:
                 # option="value" or option=value type
                 if '=' in result[0]:
                     option, value = result[0].split('=')
                 # option type
                 else:
                     option = result[0]
                     value = None
                 config[''][option] = value
     return config
Пример #11
0
 def read_shell(self, filepath):
     """Reads the shell type conf files and returns config[''][option]=value"""
     # @TODO: Use standard ConfigParser when https://bugs.python.org/issue22253 is fixed
     config = {'': dict()}
     with open_file_read(filepath) as conf_file:
         for line in conf_file:
             result = shlex.split(line, True)
             # If not a comment of empty line
             if result:
                 # option="value" or option=value type
                 if '=' in result[0]:
                     option, value = result[0].split('=')
                 # option type
                 else:
                     option = result[0]
                     value = None
                 config[''][option] = value
     return config
Пример #12
0
    def _run_test(self, params, expected):
        with open_file_read(params['file']) as f_in:
            data = f_in.readlines()

        if params['disabled']:
            # skip disabled testcases
            return

        if params['tools_wrong']:
            # if the tools are marked as being wrong about a profile, expect the opposite result
            # this makes sure we notice any behaviour change, especially not being wrong anymore
            expected = not expected

        if expected:
            apparmor.parse_profile_data(data, params['file'], 0)
        else:
            with self.assertRaises(AppArmorException):
                apparmor.parse_profile_data(data, params['file'], 0)
Пример #13
0
    def _parse_libapparmor_test_multi(self, file_with_path):
        '''parse the libapparmor test_multi *.in tests and their expected result in *.out'''

        with open_file_read('%s.out' % file_with_path) as f_in:
            expected = f_in.readlines()

        if expected[0].rstrip('\n') != 'START':
            raise Exception(
                "%s.out doesn't have 'START' in its first line! (%s)" %
                (file_with_path, expected[0]))

        expected.pop(0)

        exresult = dict()
        for line in expected:
            label, value = line.split(':', 1)

            # test_multi doesn't always use the original labels :-/
            if label in self.label_map.keys():
                label = self.label_map[label]
            label = label.replace(' ', '_').lower()
            exresult[label] = value.strip()

        if not exresult['event_type'].startswith('AA_RECORD_'):
            raise Exception(
                "event_type doesn't start with AA_RECORD_: %s in file %s" %
                (exresult['event_type'], file_with_path))

        exresult['aamode'] = exresult['event_type'].replace('AA_RECORD_', '')
        if exresult['aamode'] == 'ALLOWED':
            exresult['aamode'] = 'PERMITTING'
        if exresult['aamode'] == 'DENIED':
            exresult['aamode'] = 'REJECTING'

        if exresult[
                'event_type'] == 'AA_RECORD_INVALID':  # or exresult.get('error_code', 0) != 0:  # XXX should events with errors be ignored?
            exresult = None

        return exresult
Пример #14
0
 def load_variables(self, prof_path):
     """Loads the variables for the given profile"""
     if os.path.isfile(prof_path):
         with open_file_read(prof_path) as f_in:
             for line in f_in:
                 line = line.strip()
                 # If any includes, load variables from them first
                 match = re_match_include(line)
                 if match:
                     new_path = match
                     if not new_path.startswith('/'):
                         new_path = self.PROF_DIR + '/' + match
                     self.load_variables(new_path)
                 else:
                     # Remove any comments
                     if '#' in line:
                         line = line.split('#')[0].rstrip()
                     # Expected format is @{Variable} = value1 value2 ..
                     if line.startswith('@') and '=' in line:
                         if '+=' in line:
                             line = line.split('+=')
                             try:
                                 self.severity['VARIABLES'][line[0]] += [
                                     i.strip('"') for i in line[1].split()
                                 ]
                             except KeyError:
                                 raise AppArmorException(
                                     "Variable %s was not previously declared, but is being assigned additional value in file: %s"
                                     % (line[0], prof_path))
                         else:
                             line = line.split('=')
                             if line[0] in self.severity['VARIABLES'].keys(
                             ):
                                 raise AppArmorException(
                                     "Variable %s was previously declared in file: %s"
                                     % (line[0], prof_path))
                             self.severity['VARIABLES'][line[0]] = [
                                 i.strip('"') for i in line[1].split()
                             ]
Пример #15
0
    def _run_test(self, params, expected):
        # tests[][expected] is a dummy, replace it with the real values
        if params.split('/')[-1] in log_to_skip:
            return

        expected = self._parse_libapparmor_test_multi(params)

        with open_file_read('%s.in' % params) as f_in:
            loglines = f_in.readlines()

        loglines2 = []
        for line in loglines:
            if line.strip():
                loglines2 += [line]

        self.assertEqual(len(loglines2), 1,
                         '%s.in should only contain one line!' % params)

        parser = ReadLog('', '', '')
        parsed_event = parser.parse_event(loglines2[0])

        if parsed_event and expected:
            parsed_items = dict(parsed_event.items())

            # check if the line passes the regex in logparser.py
            if not parser.RE_LOG_ALL.search(loglines2[0]):
                raise Exception("Log event doesn't match RE_LOG_ALL")

            for label in expected:
                if label in [
                        'file',  # filename of the *.in file
                        'event_type',  # mapped to aamode
                        'audit_id',
                        'audit_sub_id',  # not set nor relevant
                        'comm',  # not set, and not too useful
                        # XXX most of the keywords listed below mean "TODO"
                        'fsuid',
                        'ouid',  # file events
                        'flags',
                        'fs_type',  # mount
                        'namespace',  # file_lock only?? (at least the tests don't contain this in other event types with namespace)
                        'net_local_addr',
                        'net_foreign_addr',
                        'net_local_port',
                        'net_foreign_port',  # detailed network events
                        'peer',
                        'signal',  # signal
                        'src_name',  # pivotroot
                        'dbus_bus',
                        'dbus_interface',
                        'dbus_member',
                        'dbus_path',  # dbus
                        'peer_pid',
                        'peer_profile',  # dbus
                ]:
                    pass
                elif parsed_items['operation'] == 'exec' and label in [
                        'sock_type', 'family', 'protocol'
                ]:
                    pass  # XXX 'exec' + network? really?
                elif parsed_items[
                        'operation'] == 'ptrace' and label == 'name2' and params.endswith(
                            '/ptrace_garbage_lp1689667_1'):
                    pass  # libapparmor would better qualify this case as invalid event
                elif not parsed_items.get(label, None):
                    raise Exception('parsed_items[%s] not set' % label)
                elif not expected.get(label, None):
                    raise Exception('expected[%s] not set' % label)
                else:
                    self.assertEqual(str(parsed_items[label]), expected[label],
                                     '%s differs' % label)
        elif expected:
            self.assertIsNone(parsed_event)  # that's why we end up here
            self.assertEqual(dict(), expected, 'parsed_event is none'
                             )  # effectively print the content of expected
        elif parsed_event:
            self.assertIsNone(expected)  # that's why we end up here
            self.assertEqual(parsed_event, dict(), 'expected is none'
                             )  # effectively print the content of parsed_event
        else:
            self.assertIsNone(expected)  # that's why we end up here
            self.assertIsNone(parsed_event)  # that's why we end up here
            self.assertEqual(parsed_event, expected)  # both are None
Пример #16
0
def parse_test_profiles(file_with_path):
    '''parse the test-related headers of a profile (for example EXRESULT) and add the profile to the set of tests'''
    exresult = None
    exresult_found = False
    description = None
    todo = False
    disabled = False

    with open_file_read(file_with_path) as f_in:
        data = f_in.readlines()

    relfile = os.path.relpath(file_with_path, apparmor.profile_dir)

    for line in data:
        if line.startswith('#=EXRESULT '):
            exresult = line.split()[1]
            if exresult == 'PASS':
                exresult == True
                exresult_found = True
            elif exresult == 'FAIL':
                exresult = False
                exresult_found = True
            else:
                raise Exception('%s contains unknown EXRESULT %s' %
                                (file_with_path, exresult))

        elif line.upper().startswith('#=DESCRIPTION '):
            description = line.split()[1]

        elif line.rstrip() == '#=TODO':
            todo = True

        elif line.rstrip() == '#=DISABLED':
            disabled = True

    if not exresult_found:
        raise Exception('%s does not contain EXRESULT' % file_with_path)

    if not description:
        raise Exception('%s does not contain description' % file_with_path)

    tools_wrong = False
    if relfile in exception_not_raised:
        if exresult:
            raise Exception(
                "%s listed in exception_not_raised, but has EXRESULT PASS" %
                file_with_path)
        tools_wrong = 'EXCEPTION_NOT_RAISED'
    elif relfile.startswith(skip_startswith):
        return 1  # XXX *** SKIP *** those tests
    elif relfile in unknown_line:
        if not exresult:
            raise Exception(
                "%s listed in unknown_line, but has EXRESULT FAIL" %
                file_with_path)
        tools_wrong = 'UNKNOWN_LINE'
    elif relfile in syntax_failure:
        if not exresult:
            raise Exception(
                "%s listed in syntax_failure, but has EXRESULT FAIL" %
                file_with_path)
        tools_wrong = 'SYNTAX_FAILURE'

    params = {
        'file': file_with_path,
        'relfile': relfile,
        'todo': todo,
        'disabled': disabled,
        'tools_wrong': tools_wrong,
        'exresult': exresult,
    }

    TestParseParserTests.tests.append((params, exresult))
    return 0
Пример #17
0
    def __init__(self, dbname=None, default_rank=10):
        """Initialises the class object"""
        self.PROF_DIR = '/etc/apparmor.d'  # The profile directory
        self.NOT_IMPLEMENTED = '_-*not*implemented*-_'  # used for rule types that don't have severity ratings
        self.severity = dict()
        self.severity['DATABASENAME'] = dbname
        self.severity['CAPABILITIES'] = {}
        self.severity['FILES'] = {}
        self.severity['REGEXPS'] = {}
        self.severity['DEFAULT_RANK'] = default_rank
        # For variable expansions for the profile
        self.severity['VARIABLES'] = dict()
        if not dbname:
            raise AppArmorException("No severity db file given")

        with open_file_read(dbname) as database:  # open(dbname, 'r')
            for lineno, line in enumerate(database, start=1):
                line = line.strip()  # or only rstrip and lstrip?
                if line == '' or line.startswith('#'):
                    continue
                if line.startswith('/'):
                    try:
                        path, read, write, execute = line.split()
                        read, write, execute = int(read), int(write), int(
                            execute)
                    except ValueError:
                        raise AppArmorException(
                            "Insufficient values for permissions in file: %s\n\t[Line %s]: %s"
                            % (dbname, lineno, line))
                    else:
                        if read not in range(0, 11) or write not in range(
                                0, 11) or execute not in range(0, 11):
                            raise AppArmorException(
                                "Inappropriate values for permissions in file: %s\n\t[Line %s]: %s"
                                % (dbname, lineno, line))
                        path = path.lstrip('/')
                        if '*' not in path:
                            self.severity['FILES'][path] = {
                                'r': read,
                                'w': write,
                                'x': execute
                            }
                        else:
                            ptr = self.severity['REGEXPS']
                            pieces = path.split('/')
                            for index, piece in enumerate(pieces):
                                if '*' in piece:
                                    path = '/'.join(pieces[index:])
                                    regexp = convert_regexp(path)
                                    ptr[regexp] = {
                                        'AA_RANK': {
                                            'r': read,
                                            'w': write,
                                            'x': execute
                                        }
                                    }
                                    break
                                else:
                                    ptr[piece] = ptr.get(piece, {})
                                    ptr = ptr[piece]
                elif line.startswith('CAP_'):
                    try:
                        resource, severity = line.split()
                        severity = int(severity)
                    except ValueError:
                        error_message = 'No severity value present in file: %s\n\t[Line %s]: %s' % (
                            dbname, lineno, line)
                        #error(error_message)
                        raise AppArmorException(error_message)  # from None
                    else:
                        if severity not in range(0, 11):
                            raise AppArmorException(
                                "Inappropriate severity value present in file: %s\n\t[Line %s]: %s"
                                % (dbname, lineno, line))
                        self.severity['CAPABILITIES'][resource] = severity
                else:
                    raise AppArmorException(
                        "Unexpected line in file: %s\n\t[Line %s]: %s" %
                        (dbname, lineno, line))
Пример #18
0
 def write_shell(self, filepath, f_out, config):
     """Writes the config object in shell file format"""
     # All the options in the file
     options = [key for key in config[''].keys()]
     # If a previous file exists modify it keeping the comments
     if os.path.exists(self.input_file):
         with open_file_read(self.input_file) as f_in:
             for line in f_in:
                 result = shlex.split(line, True)
                 # If line is not empty or comment
                 if result:
                     # If option=value or option="value" type
                     if '=' in result[0]:
                         option, value = result[0].split('=')
                         if '#' in line:
                             comment = value.split('#', 1)[1]
                             comment = '#' + comment
                         else:
                             comment = ''
                         # If option exists in the new config file
                         if option in options:
                             # If value is different
                             if value != config[''][option]:
                                 value_new = config[''][option]
                                 if value_new is not None:
                                     # Update value
                                     if '"' in line:
                                         value_new = '"' + value_new + '"'
                                     line = option + '=' + value_new + comment + '\n'
                                 else:
                                     # If option changed to option type from option=value type
                                     line = option + comment + '\n'
                             f_out.write(line)
                             # Remove from remaining options list
                             options.remove(option)
                     else:
                         # If option type
                         option = result[0]
                         value = None
                         # If option exists in the new config file
                         if option in options:
                             # If its no longer option type
                             if config[''][option] is not None:
                                 value = config[''][option]
                                 line = option + '=' + value + '\n'
                             f_out.write(line)
                             # Remove from remaining options list
                             options.remove(option)
                 else:
                     # If its empty or comment copy as it is
                     f_out.write(line)
     # If any new options are present
     if options:
         for option in options:
             value = config[''][option]
             # option type entry
             if value is None:
                 line = option + '\n'
             # option=value type entry
             else:
                 line = option + '=' + value + '\n'
             f_out.write(line)
Пример #19
0
 def write_configparser(self, filepath, f_out, config):
     """Writes/updates the given file with given config object"""
     # All the sections in the file
     sections = config.sections()
     write = True
     section = None
     options = []
     # If a previous file exists modify it keeping the comments
     if os.path.exists(self.input_file):
         with open_file_read(self.input_file) as f_in:
             for line in f_in:
                 # If its a section
                 if line.lstrip().startswith('['):
                     # If any options from preceding section remain write them
                     if options:
                         for option in options:
                             line_new = '  ' + option + ' = ' + config[section][option] + '\n'
                             f_out.write(line_new)
                         options = []
                     if section in sections:
                         # Remove the written section from the list
                         sections.remove(section)
                     section = line.strip()[1:-1]
                     if section in sections:
                         # enable write for all entries in that section
                         write = True
                         options = config.options(section)
                         # write the section
                         f_out.write(line)
                     else:
                         # disable writing until next valid section
                         write = False
                 # If write enabled
                 elif write:
                     value = shlex.split(line, True)
                     # If the line is empty or a comment
                     if not value:
                         f_out.write(line)
                     else:
                         option, value = line.split('=', 1)
                         try:
                             # split any inline comments
                             value, comment = value.split('#', 1)
                             comment = '#' + comment
                         except ValueError:
                             comment = ''
                         if option.strip() in options:
                             if config[section][option.strip()] != value.strip():
                                 value = value.replace(value, config[section][option.strip()])
                                 line = option + '=' + value + comment
                             f_out.write(line)
                             options.remove(option.strip())
     # If any options remain from the preceding section
     if options:
         for option in options:
             line = '  ' + option + ' = ' + config[section][option] + '\n'
             f_out.write(line)
     options = []
     # If any new sections are present
     if section in sections:
         sections.remove(section)
     for section in sections:
         f_out.write('\n[%s]\n' % section)
         options = config.options(section)
         for option in options:
             line = '  ' + option + ' = ' + config[section][option] + '\n'
             f_out.write(line)
Пример #20
0
 def write_shell(self, filepath, f_out, config):
     """Writes the config object in shell file format"""
     # All the options in the file
     options = [key for key in config[''].keys()]
     # If a previous file exists modify it keeping the comments
     if os.path.exists(self.input_file):
         with open_file_read(self.input_file) as f_in:
             for line in f_in:
                 result = shlex.split(line, True)
                 # If line is not empty or comment
                 if result:
                     # If option=value or option="value" type
                     if '=' in result[0]:
                         option, value = result[0].split('=')
                         if '#' in line:
                             comment = value.split('#', 1)[1]
                             comment = '#' + comment
                         else:
                             comment = ''
                         # If option exists in the new config file
                         if option in options:
                             # If value is different
                             if value != config[''][option]:
                                 value_new = config[''][option]
                                 if value_new is not None:
                                     # Update value
                                     if '"' in line:
                                         value_new = '"' + value_new + '"'
                                     line = option + '=' + value_new + comment + '\n'
                                 else:
                                     # If option changed to option type from option=value type
                                     line = option + comment + '\n'
                             f_out.write(line)
                             # Remove from remaining options list
                             options.remove(option)
                     else:
                         # If option type
                         option = result[0]
                         value = None
                         # If option exists in the new config file
                         if option in options:
                             # If its no longer option type
                             if config[''][option] is not None:
                                 value = config[''][option]
                                 line = option + '=' + value + '\n'
                             f_out.write(line)
                             # Remove from remaining options list
                             options.remove(option)
                 else:
                     # If its empty or comment copy as it is
                     f_out.write(line)
     # If any new options are present
     if options:
         for option in options:
             value = config[''][option]
             # option type entry
             if value is None:
                 line = option + '\n'
             # option=value type entry
             else:
                 line = option + '=' + value + '\n'
             f_out.write(line)
Пример #21
0
 def write_configparser(self, filepath, f_out, config):
     """Writes/updates the given file with given config object"""
     # All the sections in the file
     sections = config.sections()
     write = True
     section = None
     options = []
     # If a previous file exists modify it keeping the comments
     if os.path.exists(self.input_file):
         with open_file_read(self.input_file) as f_in:
             for line in f_in:
                 # If its a section
                 if line.lstrip().startswith('['):
                     # If any options from preceding section remain write them
                     if options:
                         for option in options:
                             line_new = '  ' + option + ' = ' + config[
                                 section][option] + '\n'
                             f_out.write(line_new)
                         options = []
                     if section in sections:
                         # Remove the written section from the list
                         sections.remove(section)
                     section = line.strip()[1:-1]
                     if section in sections:
                         # enable write for all entries in that section
                         write = True
                         options = config.options(section)
                         # write the section
                         f_out.write(line)
                     else:
                         # disable writing until next valid section
                         write = False
                 # If write enabled
                 elif write:
                     value = shlex.split(line, True)
                     # If the line is empty or a comment
                     if not value:
                         f_out.write(line)
                     else:
                         option, value = line.split('=', 1)
                         try:
                             # split any inline comments
                             value, comment = value.split('#', 1)
                             comment = '#' + comment
                         except ValueError:
                             comment = ''
                         if option.strip() in options:
                             if config[section][
                                     option.strip()] != value.strip():
                                 value = value.replace(
                                     value, config[section][option.strip()])
                                 line = option + '=' + value + comment
                             f_out.write(line)
                             options.remove(option.strip())
     # If any options remain from the preceding section
     if options:
         for option in options:
             line = '  ' + option + ' = ' + config[section][option] + '\n'
             f_out.write(line)
     options = []
     # If any new sections are present
     if section in sections:
         sections.remove(section)
     for section in sections:
         f_out.write('\n[%s]\n' % section)
         options = config.options(section)
         for option in options:
             line = '  ' + option + ' = ' + config[section][option] + '\n'
             f_out.write(line)