blob: c12771cc0be5a5f8a2a5f353841567c7ac3b9812 [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This file contains helpers for representing, manipulating, and writing
OpenSSL configuration files [1]
Configuration files are simply a collection of name=value "properties", which
are grouped into "sections".
[1] https://www.openssl.org/docs/manmaster/apps/config.html
"""
class Property(object):
"""Represents a key/value pair in OpenSSL .cnf files.
Names and values are not quoted in any way, so callers need to pass the text
exactly as it should be written to the file (leading and trailing whitespace
doesn't matter).
For instance:
baseConstraints = critical, CA:false
Could be represented by a Property where:
name = 'baseConstraints'
value = 'critical, CA:false'
"""
def __init__(self, name, value):
self.name = name
self.value = value
def write_to(self, out):
"""Outputs this property to .cnf file."""
out.write('%s = %s\n' % (self.name, self.value))
class Section(object):
"""Represents a section in OpenSSL. For instance:
[CA_root]
preserve = true
Could be represented by a Section where:
name = 'CA_root'
properties = [Property('preserve', 'true')]
"""
def __init__(self, name):
self.name = name
self.properties = []
def ensure_property_name_not_duplicated(self, name):
"""Raises an exception of there is more than 1 property named |name|."""
count = 0
for prop in self.properties:
if prop.name == name:
count += 1
if count > 1:
raise Exception('Duplicate property: %s' % (name))
def set_property(self, name, value):
"""Replaces, adds, or removes a Property from the Section:
- If |value| is None, then this is equivalent to calling
remove_property(name)
- If there is an existing property matching |name| then its value is
replaced with |value|
- If there are no properties matching |name| then a new one is added at
the end of the section
It is expected that there is AT MOST 1 property with the given name. If
that is not the case then this function will raise an error."""
if value is None:
self.remove_property(name)
return
self.ensure_property_name_not_duplicated(name)
for prop in self.properties:
if prop.name == name:
prop.value = value
return
self.add_property(name, value)
def add_property(self, name, value):
"""Adds a property (allows duplicates)"""
self.properties.append(Property(name, value))
def remove_property(self, name):
"""Removes the property with the indicated name, if it exists.
It is expected that there is AT MOST 1 property with the given name. If
that is not the case then this function will raise an error."""
self.ensure_property_name_not_duplicated(name)
for i in range(len(self.properties)):
if self.properties[i].name == name:
self.properties.pop(i)
return
def clear_properties(self):
"""Removes all configured properties."""
self.properties = []
def write_to(self, out):
"""Outputs the section in the format used by .cnf files"""
out.write('[%s]\n' % (self.name))
for prop in self.properties:
prop.write_to(out)
out.write('\n')
class Config(object):
"""Represents a .cnf (configuration) file in OpenSSL"""
def __init__(self):
self.sections = []
def get_section(self, name):
"""Gets or creates a section with the given name."""
for section in self.sections:
if section.name == name:
return section
new_section = Section(name)
self.sections.append(new_section)
return new_section
def write_to_file(self, path):
"""Outputs the Config to a .cnf files."""
with open(path, 'w') as out:
for section in self.sections:
section.write_to(out)