diff options
Diffstat (limited to 'server/config/ipachangeconf.py')
-rw-r--r-- | server/config/ipachangeconf.py | 588 |
1 files changed, 0 insertions, 588 deletions
diff --git a/server/config/ipachangeconf.py b/server/config/ipachangeconf.py deleted file mode 100644 index ea73a9b9..00000000 --- a/server/config/ipachangeconf.py +++ /dev/null @@ -1,588 +0,0 @@ -# -# ipachangeconf - configuration file manipulation classes and functions -# partially based on authconfig code -# Copyright (c) 1999-2007 Red Hat, Inc. -# Author: Simo Sorce <ssorce@redhat.com> -# -# This is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -import fcntl -import os -import string -import time -import shutil -import re - -def openLocked(filename, perms, create = True): - fd = -1 - - flags = os.O_RDWR - if create: - flags = flags | os.O_CREAT - - try: - fd = os.open(filename, flags, perms) - fcntl.lockf(fd, fcntl.LOCK_EX) - except OSError, (errno, strerr): - if fd != -1: - try: - os.close(fd) - except OSError: - pass - raise IOError(errno, strerr) - return os.fdopen(fd, "r+") - - - #TODO: add subsection as a concept - # (ex. REALM.NAME = { foo = x bar = y } ) - #TODO: put section delimiters as separating element of the list - # so that we can process multiple sections in one go - #TODO: add a comment all but provided options as a section option -class IPAChangeConf: - - def __init__(self, name): - self.progname = name - self.indent = ("","","") - self.assign = (" = ","=") - self.dassign = self.assign[0] - self.comment = ("#",) - self.dcomment = self.comment[0] - self.eol = ("\n",) - self.deol = self.eol[0] - self.sectnamdel = ("[","]") - self.subsectdel = ("{","}") - self.backup_suffix = ".ipabkp" - - def setProgName(self, name): - self.progname = name - - def setIndent(self, indent): - if type(indent) is tuple: - self.indent = indent - elif type(indent) is str: - self.indent = (indent, ) - else: - raise ValueError, 'Indent must be a list of strings' - - def setOptionAssignment(self, assign): - if type(assign) is tuple: - self.assign = assign - else: - self.assign = (assign, ) - self.dassign = self.assign[0] - - def setCommentPrefix(self, comment): - if type(comment) is tuple: - self.comment = comment - else: - self.comment = (comment, ) - self.dcomment = self.comment[0] - - def setEndLine(self, eol): - if type(eol) is tuple: - self.eol = eol - else: - self.eol = (eol, ) - self.deol = self.eol[0] - - def setSectionNameDelimiters(self, delims): - self.sectnamdel = delims - - def setSubSectionDelimiters(self, delims): - self.subsectdel = delims - - def matchComment(self, line): - for v in self.comment: - if line.lstrip().startswith(v): - return line.lstrip()[len(v):] - return False - - def matchEmpty(self, line): - if line.strip() == "": - return True - return False - - def matchSection(self, line): - cl = "".join(line.strip().split()) - if len(self.sectnamdel) != 2: - return False - if not cl.startswith(self.sectnamdel[0]): - return False - if not cl.endswith(self.sectnamdel[1]): - return False - return cl[len(self.sectnamdel[0]):-len(self.sectnamdel[1])] - - def matchSubSection(self, line): - if self.matchComment(line): - return False - - parts = line.split(self.dassign, 1) - if len(parts) < 2: - return False - - if parts[1].strip() == self.subsectdel[0]: - return parts[0].strip() - - return False - - def matchSubSectionEnd(self, line): - if self.matchComment(line): - return False - - if line.strip() == self.subsectdel[1]: - return True - - return False - - def getSectionLine(self, section): - if len(self.sectnamdel) != 2: - return section - return self.sectnamdel[0]+section+self.sectnamdel[1]+self.deol - - def dump(self, options, level=0): - output = "" - if level >= len(self.indent): - level = len(self.indent)-1 - - for o in options: - if o['type'] == "section": - output += self.sectnamdel[0]+o['name']+self.sectnamdel[1]+self.deol - output += self.dump(o['value'], level+1) - continue - if o['type'] == "subsection": - output += self.indent[level]+o['name']+self.dassign+self.subsectdel[0]+self.deol - output += self.dump(o['value'], level+1) - output += self.indent[level]+self.subsectdel[1]+self.deol - continue - if o['type'] == "option": - output += self.indent[level]+o['name']+self.dassign+o['value']+self.deol - continue - if o['type'] == "comment": - output += self.dcomment+o['value']+self.deol - continue - if o['type'] == "empty": - output += self.deol - continue - raise SyntaxError, 'Unknown type: ['+o['type']+']' - - return output - - def parseLine(self, line): - - if self.matchEmpty(line): - return {'name':'empty', 'type':'empty'} - - value = self.matchComment(line) - if value: - return {'name':'comment', 'type':'comment', 'value':value.rstrip()} - - parts = line.split(self.dassign, 1) - if len(parts) < 2: - raise SyntaxError, 'Syntax Error: Unknown line format' - - return {'name':parts[0].strip(), 'type':'option', 'value':parts[1].rstrip()} - - def findOpts(self, opts, type, name, exclude_sections=False): - - num = 0 - for o in opts: - if o['type'] == type and o['name'] == name: - return (num, o) - if exclude_sections and (o['type'] == "section" or o['type'] == "subsection"): - return (num, None) - num += 1 - return (num, None) - - def commentOpts(self, inopts, level = 0): - - opts = [] - - if level >= len(self.indent): - level = len(self.indent)-1 - - for o in inopts: - if o['type'] == 'section': - no = self.commentOpts(o['value'], level+1) - val = self.dcomment+self.sectnamdel[0]+o['name']+self.sectnamdel[1] - opts.append({'name':'comment', 'type':'comment', 'value':val}) - for n in no: - opts.append(n) - continue - if o['type'] == 'subsection': - no = self.commentOpts(o['value'], level+1) - val = self.indent[level]+o['name']+self.dassign+self.subsectdel[0] - opts.append({'name':'comment', 'type':'comment', 'value':val}) - for n in no: - opts.append(n) - val = self.indent[level]+self.subsectdel[1] - opts.append({'name':'comment', 'type':'comment', 'value':val}) - continue - if o['type'] == 'option': - val = self.indent[level]+o['name']+self.dassign+o['value'] - opts.append({'name':'comment', 'type':'comment', 'value':val}) - continue - if o['type'] == 'comment': - opts.append(o) - continue - if o['type'] == 'empty': - opts.append({'name':'comment', 'type':'comment', 'value':''}) - continue - raise SyntaxError, 'Unknown type: ['+o['type']+']' - - return opts - - def mergeOld(self, oldopts, newopts): - - opts = [] - - for o in oldopts: - if o['type'] == "section" or o['type'] == "subsection": - (num, no) = self.findOpts(newopts, o['type'], o['name']) - if not no: - opts.append(o) - continue - if no['action'] == "set": - mo = self.mergeOld(o['value'], no['value']) - opts.append({'name':o['name'], 'type':o['type'], 'value':mo}) - continue - if no['action'] == "comment": - co = self.commentOpts(o['value']) - for c in co: - opts.append(c) - continue - if no['action'] == "remove": - continue - raise SyntaxError, 'Unknown action: ['+no['action']+']' - - if o['type'] == "comment" or o['type'] == "empty": - opts.append(o) - continue - - if o['type'] == "option": - (num, no) = self.findOpts(newopts, 'option', o['name'], True) - if not no: - opts.append(o) - continue - if no['action'] == 'comment' or no['action'] == 'remove': - if no['value'] != None and o['value'] != no['value']: - opts.append(o) - continue - if no['action'] == 'comment': - opts.append({'name':'comment', 'type':'comment', - 'value':self.dcomment+o['name']+self.dassign+o['value']}) - continue - if no['action'] == 'set': - opts.append(no) - continue - raise SyntaxError, 'Unknown action: ['+o['action']+']' - - raise SyntaxError, 'Unknown type: ['+o['type']+']' - - return opts - - def mergeNew(self, opts, newopts): - - cline = 0 - - for no in newopts: - - if no['type'] == "section" or no['type'] == "subsection": - (num, o) = self.findOpts(opts, no['type'], no['name']) - if not o: - if no['action'] == 'set': - opts.append(no) - continue - if no['action'] == "set": - self.mergeNew(o['value'], no['value']) - continue - cline = num+1 - continue - - if no['type'] == "option": - (num, o) = self.findOpts(opts, no['type'], no['name'], True) - if not o: - if no['action'] == 'set': - opts.append(no) - continue - cline = num+1 - continue - - if no['type'] == "comment" or no['type'] == "empty": - opts.insert(cline, no) - cline += 1 - continue - - raise SyntaxError, 'Unknown type: ['+no['type']+']' - - - def merge(self, oldopts, newopts): - - #Use a two pass strategy - #First we create a new opts tree from oldopts removing/commenting - # the options as indicated by the contents of newopts - #Second we fill in the new opts tree with options as indicated - # in the newopts tree (this is becaus eentire (sub)sections may - # exist in the newopts that do not exist in oldopts) - - opts = self.mergeOld(oldopts, newopts) - self.mergeNew(opts, newopts) - return opts - - #TODO: Make parse() recursive? - def parse(self, f): - - opts = [] - sectopts = [] - section = None - subsectopts = [] - subsection = None - curopts = opts - fatheropts = opts - - # Read in the old file. - for line in f: - - # It's a section start. - value = self.matchSection(line) - if value: - if section is not None: - opts.append({'name':section, 'type':'section', 'value':sectopts}) - sectopts = [] - curopts = sectopts - fatheropts = sectopts - section = value - continue - - # It's a subsection start. - value = self.matchSubSection(line) - if value: - if subsection is not None: - raise SyntaxError, 'nested subsections are not supported yet' - subsectopts = [] - curopts = subsectopts - subsection = value - continue - - value = self.matchSubSectionEnd(line) - if value: - if subsection is None: - raise SyntaxError, 'Unmatched end subsection terminator found' - fatheropts.append({'name':subsection, 'type':'subsection', 'value':subsectopts}) - subsection = None - curopts = fatheropts - continue - - # Copy anything else as is. - curopts.append(self.parseLine(line)) - - #Add last section if any - if len(sectopts) is not 0: - opts.append({'name':section, 'type':'section', 'value':sectopts}) - - return opts - - # Write settings to configuration file - # file is a path - # options is a set of dictionaries in the form: - # [{'name': 'foo', 'value': 'bar', 'action': 'set/comment'}] - # section is a section name like 'global' - def changeConf(self, file, newopts): - autosection = False - savedsection = None - done = False - output = "" - f = None - try: - #Do not catch an unexisting file error, we want to fail in that case - shutil.copy2(file, file+self.backup_suffix) - - f = openLocked(file, 0644) - - oldopts = self.parse(f) - - options = self.merge(oldopts, newopts) - - output = self.dump(options) - - # Write it out and close it. - f.seek(0) - f.truncate(0) - f.write(output) - finally: - try: - if f: - f.close() - except IOError: - pass - return True - - # Write settings to new file, backup old - # file is a path - # options is a set of dictionaries in the form: - # [{'name': 'foo', 'value': 'bar', 'action': 'set/comment'}] - # section is a section name like 'global' - def newConf(self, file, options): - autosection = False - savedsection = None - done = False - output = "" - f = None - try: - try: - shutil.copy2(file, file+self.backup_suffix) - except IOError, err: - if err.errno == 2: - # The orign file did not exist - pass - - f = openLocked(file, 0644) - - # Trunkate - f.seek(0) - f.truncate(0) - - output = self.dump(options) - - f.write(output) - finally: - try: - if f: - f.close() - except IOError: - pass - return True - -# A SSSD-specific subclass of IPAChangeConf -class SSSDChangeConf(IPAChangeConf): - OPTCRE = re.compile( - r'(?P<option>[^:=\s][^:=]*)' # very permissive! - r'\s*=\s*' # any number of space/tab, - # followed by separator - # followed by any # space/tab - r'(?P<value>.*)$' # everything up to eol - ) - - def __init__(self): - IPAChangeConf.__init__(self, "SSSD") - self.comment = ("#",";") - self.backup_suffix = ".bak" - self.opts = [] - - def parseLine(self, line): - """ - Overrides IPAChangeConf parseLine so that lines are splitted - using any separator in self.assign, not just the default one - """ - - if self.matchEmpty(line): - return {'name':'empty', 'type':'empty'} - - value = self.matchComment(line) - if value: - return {'name':'comment', 'type':'comment', 'value':value.rstrip()} - - mo = self.OPTCRE.match(line) - if not mo: - raise SyntaxError, 'Syntax Error: Unknown line format' - - try: - name, value = mo.group('option', 'value') - except IndexError: - raise SyntaxError, 'Syntax Error: Unknown line format' - - return {'name':name.strip(), 'type':'option', 'value':value.strip()} - - def readfp(self, fd): - self.opts.extend(self.parse(fd)) - - def read(self, filename): - fd = open(filename, 'r') - self.readfp(fd) - fd.close() - - def get(self, section, name): - index, item = self.get_option_index(section, name) - if item: - return item['value'] - - def set(self, section, name, value): - modkw = { 'type' : 'section', - 'name' : section, - 'value' : [{ - 'type' : 'option', - 'name' : name, - 'value' : value, - 'action': 'set', - }], - 'action': 'set', - } - self.opts = self.merge(self.opts, [ modkw ]) - - def add_section(self, name, optkw, index=0): - optkw.append({'type':'empty', 'value':'empty'}) - addkw = { 'type' : 'section', - 'name' : name, - 'value' : optkw, - } - self.opts.insert(index, addkw) - - def delete_section(self, name): - self.delete_option('section', name) - - def sections(self): - return [ o for o in self.opts if o['type'] == 'section' ] - - def has_section(self, section): - return len([ o for o in self.opts if o['type'] == 'section' if o['name'] == section ]) > 0 - - def options(self, section): - for opt in self.opts: - if opt['type'] == 'section' and opt['name'] == section: - return opt['value'] - - def delete_option(self, type, name, exclude_sections=False): - return self.delete_option_subtree(self.opts, type, name) - - def delete_option_subtree(self, subtree, type, name, exclude_sections=False): - index, item = self.findOpts(subtree, type, name, exclude_sections) - if item: - del subtree[index] - return index - - def has_option(self, section, name): - index, item = self.get_option_index(section, name) - if index != -1 and item != None: - return True - return False - - def strip_comments_empty(self, optlist): - retlist = [] - for opt in optlist: - if opt['type'] in ('comment', 'empty'): - continue - retlist.append(opt) - return retlist - - def get_option_index(self, parent_name, name, type='option'): - subtree = None - if parent_name: - pindex, pdata = self.findOpts(self.opts, 'section', parent_name) - if not pdata: - return (-1, None) - subtree = pdata['value'] - else: - subtree = self.opts - return self.findOpts(subtree, type, name) - |