#!/usr/bin/env python

import sys, os, re, ConfigParser
from optparse import OptionParser

sanitize_mapping = [ ('_','--') ]

def guesssyntax(parm):
    # integer value
    if re.match('^\d+$', parm):        
        return 'integerMatch', '1.3.6.1.4.1.1466.115.121.1.27'

    # boolean value
    if parm.lower() in [ 'true', 'false' ]:
        return 'caseExactIA5Match', '1.3.6.1.4.1.1466.115.121.1.26'
        #return 'booleanMatch', '1.3.6.1.4.1.1466.115.121.1.7' # FIXME

    # string value (default)
    return 'caseExactIA5Match', '1.3.6.1.4.1.1466.115.121.1.26'

def sanitize(name):
    """Certain characters are not allowed in LDAP schema definitions. Maps to okay values."""
    sanitized = name
    for old, new in sanitize_mapping:
        sanitized = sanitized.replace(old, new)
    return sanitized

def desanitize(name):
    """Certain characters are not allowed in LDAP schema definitions. This returns to the original value."""
    sanitized = name
    for old, new in sanitize_mapping:
        sanitized = sanitized.replace(new, old)
    return sanitized

def formatattributes(attributes, maxperline=3):
    """openLDAP does not like long lines of attributes.
       This function splits to multiple lines based on maxperline"""
    if len(attributes) <= maxperline:
        return ' $ '.join(attributes)
    else:
        return '%s $ \\\n              %s' % \
            (' $ '.join(attributes[0:maxperline]), formatattributes(attributes[maxperline :], maxperline))
        
def genschema(oid, conffile, project='x-com-example', out=sys.stdout):
    cp = ConfigParser.SafeConfigParser()
    cp.read(conffile)
    shortname = os.path.basename(conffile).replace('.conf', '')

    out.write('#\n#  schema based on config file %s\n#\n' % (conffile))
    attributes = []
    sectionsN = 0
    for section in cp.sections():
        sectionsN += 1
        #print '\nSection: %s\n%s' % (section, '-'*50)

        optionsN = 0
        for option in cp.options(section):
            optionsN += 1
            #print '%s = "%s"' % (option, cp.get(section, option))
            equality, syntax = guesssyntax(cp.get(section,option))
            attribute_name = sanitize('%s--%s--%s' % (shortname, section, option))
            attributes.append(attribute_name)
            out.write('%s\n' % ("""
  attributetype ( %s.1.%d.%d
        NAME '%s'
        DESC 'Brief description of %s found in section %s - UPDATEME'
        EQUALITY %s
        SYNTAX %s )""" % (oid, sectionsN, optionsN, \
                              attribute_name, \
                              option, section, \
                              equality, \
                              syntax)))

    out.write('%s\n' % ("""
  objectclass   ( %s.2.%d NAME '%s-%s' SUP top STRUCTURAL
        DESC 'object collection description for %s-%s - UPDATEME'
        MUST ( cn )
	MAY ( %s )
        )\n""" % (oid, sectionsN, project, shortname, \
                    shortname, section, \
                    formatattributes(attributes))))

def genldif(conffile, project='x-com-example', basedn='dc=my-domain,dc=com', out=sys.stdout):
    cp = ConfigParser.SafeConfigParser()
    cp.read(conffile)
    shortname = os.path.basename(conffile).replace('.conf', '')

    out.write('#\n#  LDIF data based on config file date from %s\n#\n' % (conffile))
    out.write('dn: cn=%s,%s\n' % (shortname, basedn))
    out.write('objectclass: %s-%s\n' % (project, shortname))
    out.write('cn: %s\n' % (shortname))
    for section in cp.sections():
        for option in cp.options(section):
            attribute_name = sanitize('%s--%s--%s' % (shortname, section, option))
            out.write('%s: %s\n' % (attribute_name, cp.get(section, option).replace('\n', '--\\NEWLINE--\n  ')))
            #for option_section in cp.get(section, option).split('\n'):
            #    out.write('%s: %s\n' % (attribute_name, option_section))

    out.write('\n') # extra newline to denote the end of the entry

if __name__ == '__main__':


    USAGE = """%s configfile [configfile2...]

       Each config file should be in the form used by ConfigParser or typical ini style config.
       Will attempt to produce LDAP schema for you and LDIF from your data.
       E.g. If your package is called BLAH, then try:
            %s -o 666.1 -n BLAH $(rpm -ql BLAH | egrep '\.conf$)""" % \
        (os.path.basename(sys.argv[0]), os.path.basename(sys.argv[0]))

    parser = OptionParser(usage=USAGE)
    parser.add_option("-o", "--oid", dest="oid", default="1.3.6.1.4.1.1733",
                      help="Base OID value to use when generating a sample LDAP schema")
    parser.add_option("-n", "--name", dest="name", default="x-com-example",
                      help="Base NAME value used in labeling the new ObjectClasses")
    parser.add_option("-b", "--basedn", dest="basedn", default="dc=my-domain,dc=com",
                      help="BaseDN used in anchoring values in the LDIF data")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
                      help="Tell you what I'm doing or not")

    (options, args) = parser.parse_args()

    N = 0
    schemafile = '%s.schema' % os.path.join(os.path.dirname(sys.argv[0]), options.name)
    fh = open(schemafile, 'w')
    for conf in args:
        N += 1
        if options.verbose:
            print 'Compiling %s data to build schema file %s' % (conf, schemafile)
        genschema("%s.%d" % (options.oid, N), conf, options.name, fh)
    fh.close()
    
    ldiffile = '%s.ldif' % os.path.join(os.path.dirname(sys.argv[0]), options.name)
    fh = open(ldiffile, 'w')
    for conf in args:
        if options.verbose:
            print 'Compiling %s data to build ldif file %s' % (conf, ldiffile)
        genldif(conf, options.name, options.basedn, fh)
    fh.close()
