#!/usr/bin/python -t

# Authors:
#     Matthew Harmsen <mharmsen@redhat.com>
#
# This program 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 of the License.
#
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright (C) 2012 Red Hat, Inc.
# All rights reserved.
#

# System Imports
import errno
import sys
import os
import fileinput
import pickle
import random
import re
import shutil
import string
import subprocess
import time
import types
from datetime import datetime
from grp import getgrgid
from grp import getgrnam
from pwd import getpwnam
from pwd import getpwuid
import xml.etree.ElementTree as ET
import zipfile
import selinux
if selinux.is_selinux_enabled():
    import seobject


# PKI Deployment Imports
import pkiconfig as config
from pkiconfig import pki_master_dict as master
from pkiconfig import pki_slots_dict as slots
from pkiconfig import pki_selinux_config_ports as ports
import pkimanifest as manifest
import pkimessages as log
from pkiparser import PKIConfigParser
import pki.account
import pki.client
import pki.system

# PKI Deployment Helper Functions
def pki_copytree(src, dst, symlinks=False, ignore=None):
    """Recursively copy a directory tree using copy2().

    PATCH:  This code was copied from 'shutil.py' and patched to
            allow 'The destination directory to already exist.'

    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    The optional ignore argument is a callable. If given, it
    is called with the `src` parameter, which is the directory
    being visited by pki_copytree(), and `names` which is the list of
    `src` contents, as returned by os.listdir():

        callable(src, names) -> ignored_names

    Since pki_copytree() is called recursively, the callable will be
    called once for each directory that is copied. It returns a
    list of names relative to the `src` directory that should
    not be copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    # PATCH:  ONLY execute 'os.makedirs(dst)' if the top-level
    #         destination directory does NOT exist!
    if not os.path.exists(dst):
        os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                pki_copytree(srcname, dstname, symlinks, ignore)
            else:
                # Will raise a SpecialFileError for unsupported file types
                shutil.copy2(srcname, dstname)
        # catch the Error from the recursive pki_copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
        except EnvironmentError, why:
            errors.append((srcname, dstname, str(why)))
    try:
        shutil.copystat(src, dst)
    except OSError, why:
        if WindowsError is not None and isinstance(why, WindowsError):
            # Copying file access times may fail on Windows
            pass
        else:
            errors.extend((src, dst, str(why)))
    if errors:
        raise Error(errors)

# PKI Deployment Identity Class
class identity:
    def __add_gid(self, pki_group):
        pki_gid = None
        try:
            # Does the specified 'pki_group' exist?
            pki_gid = getgrnam(pki_group)[2]
            # Yes, group 'pki_group' exists!
            config.pki_log.info(log.PKIHELPER_GROUP_ADD_2, pki_group, pki_gid,
                                extra=config.PKI_INDENTATION_LEVEL_2)
        except KeyError as exc:
            # No, group 'pki_group' does not exist!
            config.pki_log.debug(log.PKIHELPER_GROUP_ADD_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            try:
                # Is the default well-known GID already defined?
                group = getgrgid(config.PKI_DEPLOYMENT_DEFAULT_GID)[0]
                # Yes, the default well-known GID exists!
                config.pki_log.info(log.PKIHELPER_GROUP_ADD_DEFAULT_2,
                                    group, config.PKI_DEPLOYMENT_DEFAULT_GID,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                # Attempt to create 'pki_group' using a random GID.
                command = "/usr/sbin/groupadd" + " " +\
                          pki_group + " " +\
                          "> /dev/null 2>&1"
            except KeyError as exc:
                # No, the default well-known GID does not exist!
                config.pki_log.debug(log.PKIHELPER_GROUP_ADD_GID_KEYERROR_1,
                                     exc, extra=config.PKI_INDENTATION_LEVEL_2)
                # Is the specified 'pki_group' the default well-known group?
                if pki_group == config.PKI_DEPLOYMENT_DEFAULT_GROUP:
                    # Yes, attempt to create the default well-known group
                    # using the default well-known GID.
                    command = "/usr/sbin/groupadd" + " " +\
                              "-g" + " " +\
                              str(config.PKI_DEPLOYMENT_DEFAULT_GID) + " " +\
                              "-r" + " " +\
                              pki_group + " " +\
                              "> /dev/null 2>&1"
                else:
                    # No, attempt to create 'pki_group' using a random GID.
                    command = "/usr/sbin/groupadd" + " " +\
                              pki_group + " " +\
                              "> /dev/null 2>&1"
            # Execute this "groupadd" command.
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return

    def __add_uid(self, pki_user, pki_group):
        pki_uid = None
        try:
            # Does the specified 'pki_user' exist?
            pki_uid = getpwnam(pki_user)[2]
            # Yes, user 'pki_user' exists!
            config.pki_log.info(log.PKIHELPER_USER_ADD_2, pki_user, pki_uid,
                                extra=config.PKI_INDENTATION_LEVEL_2)
            # NOTE:  For now, never check validity of specified 'pki_group'!
        except KeyError as exc:
            # No, user 'pki_user' does not exist!
            config.pki_log.debug(log.PKIHELPER_USER_ADD_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            try:
                # Is the default well-known UID already defined?
                user = getpwuid(config.PKI_DEPLOYMENT_DEFAULT_UID)[0]
                # Yes, the default well-known UID exists!
                config.pki_log.info(log.PKIHELPER_USER_ADD_DEFAULT_2,
                                    user, config.PKI_DEPLOYMENT_DEFAULT_UID,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                # Attempt to create 'pki_user' using a random UID.
                command = "/usr/sbin/useradd" + " " +\
                          "-g" + " " +\
                          pki_group + " " +\
                          "-d" + " " +\
                          config.PKI_DEPLOYMENT_SOURCE_ROOT + " " +\
                          "-s" + " " +\
                          config.PKI_DEPLOYMENT_DEFAULT_SHELL + " " +\
                          "-c" + " " +\
                          config.PKI_DEPLOYMENT_DEFAULT_COMMENT + " " +\
                          pki_user + " " +\
                          "> /dev/null 2>&1"
            except KeyError as exc:
                # No, the default well-known UID does not exist!
                config.pki_log.debug(log.PKIHELPER_USER_ADD_UID_KEYERROR_1,
                                     exc, extra=config.PKI_INDENTATION_LEVEL_2)
                # Is the specified 'pki_user' the default well-known user?
                if pki_user == config.PKI_DEPLOYMENT_DEFAULT_USER:
                    # Yes, attempt to create the default well-known user
                    # using the default well-known UID.
                    command = "/usr/sbin/useradd" + " " +\
                              "-g" + " " +\
                              pki_group + " " +\
                              "-d" + " " +\
                              config.PKI_DEPLOYMENT_SOURCE_ROOT + " " +\
                              "-s" + " " +\
                              config.PKI_DEPLOYMENT_DEFAULT_SHELL + " " +\
                              "-c" + " " +\
                              config.PKI_DEPLOYMENT_DEFAULT_COMMENT + " " +\
                              "-u" + " " +\
                              str(config.PKI_DEPLOYMENT_DEFAULT_UID) + " " +\
                              "-r" + " " +\
                              pki_user + " " +\
                              "> /dev/null 2>&1"
                else:
                    # No, attempt to create 'pki_user' using a random UID.
                    command = "/usr/sbin/useradd" + " " +\
                              "-g" + " " +\
                              pki_group + " " +\
                              "-d" + " " +\
                              config.PKI_DEPLOYMENT_SOURCE_ROOT + " " +\
                              "-s" + " " +\
                              config.PKI_DEPLOYMENT_DEFAULT_SHELL + " " +\
                              "-c" + " " +\
                              config.PKI_DEPLOYMENT_DEFAULT_COMMENT + " " +\
                              pki_user + " " +\
                              "> /dev/null 2>&1"
            # Execute this "useradd" command.
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return

    def add_uid_and_gid(self, pki_user, pki_group):
        self.__add_gid(pki_group)
        self.__add_uid(pki_user, pki_group)
        return

    def get_uid(self, critical_failure=True):
        try:
            pki_uid = master['pki_uid']
        except KeyError as exc:
            config.pki_log.error(log.PKI_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return pki_uid

    def get_gid(self, critical_failure=True):
        try:
            pki_gid = master['pki_gid']
        except KeyError as exc:
            config.pki_log.error(log.PKI_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return pki_gid

    def set_uid(self, name, critical_failure=True):
        try:
            config.pki_log.debug(log.PKIHELPER_USER_1, name,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            # id -u <name>
            pki_uid = getpwnam(name)[2]
            master['pki_uid']=pki_uid
            config.pki_log.debug(log.PKIHELPER_UID_2, name, pki_uid,
                                 extra=config.PKI_INDENTATION_LEVEL_3)
        except KeyError as exc:
            config.pki_log.error(log.PKI_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return pki_uid

    def set_gid(self, name, critical_failure=True):
        try:
            config.pki_log.debug(log.PKIHELPER_GROUP_1, name,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            # id -g <name>
            pki_gid = getgrnam(name)[2]
            master['pki_gid']=pki_gid
            config.pki_log.debug(log.PKIHELPER_GID_2, name, pki_gid,
                                 extra=config.PKI_INDENTATION_LEVEL_3)
        except KeyError as exc:
            config.pki_log.error(log.PKI_KEYERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return pki_gid


# PKI Deployment Namespace Class
class namespace:
    # Silently verify that the selected 'pki_instance_name' will
    # NOT produce any namespace collisions
    def collision_detection(self):
        # Run simple checks for pre-existing namespace collisions
        if os.path.exists(master['pki_instance_path']):
            if os.path.exists(master['pki_subsystem_path']):
                # Top-Level PKI base path collision
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_COLLISION_2,
                    master['pki_instance_name'],
                    master['pki_instance_path'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        else:
            if os.path.exists(master['pki_target_tomcat_conf_instance_id']):
                # Top-Level "/etc/sysconfig" path collision
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_COLLISION_2,
                    master['pki_instance_name'],
                    master['pki_target_tomcat_conf_instance_id'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if os.path.exists(master['pki_cgroup_systemd_service']):
                # Systemd cgroup path collision
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_COLLISION_2,
                    master['pki_instance_name'],
                    master['pki_cgroup_systemd_service_path'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if os.path.exists(master['pki_cgroup_cpu_systemd_service']):
                # Systemd cgroup CPU path collision
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_COLLISION_2,
                    master['pki_instance_name'],
                    master['pki_cgroup_cpu_systemd_service_path'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        if os.path.exists(master['pki_instance_log_path']) and\
           os.path.exists(master['pki_subsystem_log_path']):
            # Top-Level PKI log path collision
            config.pki_log.error(
                log.PKIHELPER_NAMESPACE_COLLISION_2,
                master['pki_instance_name'],
                master['pki_instance_log_path'],
                extra=config.PKI_INDENTATION_LEVEL_2)
            sys.exit(1)
        if os.path.exists(master['pki_instance_configuration_path']) and\
           os.path.exists(master['pki_subsystem_configuration_path']):
            # Top-Level PKI configuration path collision
            config.pki_log.error(
                log.PKIHELPER_NAMESPACE_COLLISION_2,
                master['pki_instance_name'],
                master['pki_instance_configuration_path'],
                extra=config.PKI_INDENTATION_LEVEL_2)
            sys.exit(1)
        if os.path.exists(master['pki_instance_registry_path']) and\
           os.path.exists(master['pki_subsystem_registry_path']):
            # Top-Level PKI registry path collision
            config.pki_log.error(
                log.PKIHELPER_NAMESPACE_COLLISION_2,
                master['pki_instance_name'],
                master['pki_instance_registry_path'],
                extra=config.PKI_INDENTATION_LEVEL_2)
            sys.exit(1)
        # Run simple checks for reserved name namespace collisions
        if master['pki_instance_name'] in config.PKI_BASE_RESERVED_NAMES:
            # Top-Level PKI base path reserved name collision
            config.pki_log.error(
                log.PKIHELPER_NAMESPACE_RESERVED_NAME_2,
                master['pki_instance_name'],
                master['pki_instance_path'],
                extra=config.PKI_INDENTATION_LEVEL_2)
            sys.exit(1)
        # No need to check for reserved name under Top-Level PKI log path
        if master['pki_instance_name'] in config.PKI_CONFIGURATION_RESERVED_NAMES:
            # Top-Level PKI configuration path reserved name collision
            config.pki_log.error(
                log.PKIHELPER_NAMESPACE_RESERVED_NAME_2,
                master['pki_instance_name'],
                master['pki_instance_configuration_path'],
                extra=config.PKI_INDENTATION_LEVEL_2)
            sys.exit(1)
        if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
            # Top-Level Apache PKI registry path reserved name collision
            if master['pki_instance_name'] in\
               config.PKI_APACHE_REGISTRY_RESERVED_NAMES:
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_RESERVED_NAME_2,
                    master['pki_instance_name'],
                    master['pki_instance_registry_path'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
            # Top-Level Tomcat PKI registry path reserved name collision
            if master['pki_instance_name'] in\
               config.PKI_TOMCAT_REGISTRY_RESERVED_NAMES:
                config.pki_log.error(
                    log.PKIHELPER_NAMESPACE_RESERVED_NAME_2,
                    master['pki_instance_name'],
                    master['pki_instance_registry_path'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)


# PKI Deployment Configuration File Class
class configuration_file:
    def log_configuration_url(self):
        # NOTE:  This is the one and only parameter containing a sensitive
        #        parameter that may be stored in a log file.
        config.pki_log.info(log.PKI_CONFIGURATION_WIZARD_URL_1,
                            master['pki_configuration_url'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
        config.pki_log.info(log.PKI_CONFIGURATION_WIZARD_RESTART_1,
                            master['pki_registry_initscript_command'],
                            extra=config.PKI_INDENTATION_LEVEL_2)

    def display_configuration_url(self):
        # NOTE:  This is the one and only parameter containing a sensitive
        #        parameter that may be displayed to the screen.
        print log.PKI_CONFIGURATION_URL_1 % master['pki_configuration_url']
        print
        print log.PKI_CONFIGURATION_RESTART_1 %\
              master['pki_registry_initscript_command']
        print

    def verify_sensitive_data(self):
        # Silently verify the existence of 'sensitive' data
        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
            # Verify existence of Directory Server Password (ALWAYS)
            if not master.has_key('pki_ds_password') or\
               not len(master['pki_ds_password']):
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                    "pki_ds_password",
                    master['pki_user_deployment_cfg'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            # Verify existence of Admin Password (except for Clones)
            if not config.str2bool(master['pki_clone']):
                if not master.has_key('pki_admin_password') or\
                   not len(master['pki_admin_password']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_admin_password",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # If required, verify existence of Backup Password
            if config.str2bool(master['pki_backup_keys']):
                if not master.has_key('pki_backup_password') or\
                   not len(master['pki_backup_password']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_backup_password",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # Verify existence of Client Pin for NSS client security databases
            if not master.has_key('pki_client_database_password') or\
               not len(master['pki_client_database_password']):
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_CLIENT_DATABASE_PASSWORD_2,
                    "pki_client_database_password",
                    master['pki_user_deployment_cfg'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            # Verify existence of Client PKCS #12 Password for Admin Cert
            if not master.has_key('pki_client_pkcs12_password') or\
               not len(master['pki_client_pkcs12_password']):
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                    "pki_client_pkcs12_password",
                    master['pki_user_deployment_cfg'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            # Verify existence of PKCS #12 Password (ONLY for Clones)
            if config.str2bool(master['pki_clone']):
                if not master.has_key('pki_clone_pkcs12_password') or\
                   not len(master['pki_clone_pkcs12_password']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_clone_pkcs12_password",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # Verify existence of Security Domain Password File
            # (ONLY for Clones, KRA, OCSP, TKS, or Subordinate CA)
            if config.str2bool(master['pki_clone']) or\
               not master['pki_subsystem'] == "CA" or\
               config.str2bool(master['pki_subordinate']):
                if not master.has_key('pki_security_domain_password') or\
                   not len(master['pki_security_domain_password']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_security_domain_password",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # If required, verify existence of Token Password
            if not master['pki_token_name'] == "internal":
                if not master.has_key('pki_token_password') or\
                   not len(master['pki_token_password']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_token_password",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
        return

    def verify_mutually_exclusive_data(self):
        # Silently verify the existence of 'mutually exclusive' data
        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
            if master['pki_subsystem'] == "CA":
                if config.str2bool(master['pki_clone']) and\
                   config.str2bool(master['pki_external']) and\
                   config.str2bool(master['pki_subordinate']):
                    config.pki_log.error(
                        log.PKIHELPER_MUTUALLY_EXCLUSIVE_CLONE_EXTERNAL_SUB_CA,
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                elif config.str2bool(master['pki_clone']) and\
                     config.str2bool(master['pki_external']):
                    config.pki_log.error(
                        log.PKIHELPER_MUTUALLY_EXCLUSIVE_CLONE_EXTERNAL_CA,
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                elif config.str2bool(master['pki_clone']) and\
                     config.str2bool(master['pki_subordinate']):
                    config.pki_log.error(
                        log.PKIHELPER_MUTUALLY_EXCLUSIVE_CLONE_SUB_CA,
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                elif config.str2bool(master['pki_external']) and\
                     config.str2bool(master['pki_subordinate']):
                    config.pki_log.error(
                        log.PKIHELPER_MUTUALLY_EXCLUSIVE_EXTERNAL_SUB_CA,
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)

    def verify_predefined_configuration_file_data(self):
        # Silently verify the existence of any required 'predefined' data
        #
        # FUTURE:  As much as is possible, alter this routine to verify
        #          ALL name/value pairs for the requested configuration
        #          scenario.  This should include checking for the
        #          "existence" of ALL required "name" parameters, as well as
        #          the "existence", "type" (e. g. -  string, boolean, number,
        #          etc.), and "correctness" (e. g. - file, directory, boolean
        #          'True' or 'False', etc.) of ALL required "value" parameters.
        #
        if master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
            if config.str2bool(master['pki_clone']):
                # Verify existence of clone parameters
                if not master.has_key('pki_ds_base_dn') or\
                   not len(master['pki_ds_base_dn']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_ds_base_dn",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_ds_ldap_port') or\
                   not len(master['pki_ds_ldap_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_ds_ldap_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_ds_ldaps_port') or\
                   not len(master['pki_ds_ldaps_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_ds_ldaps_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                # NOTE:  Although this will be checked prior to getting to
                #        this method, this clone's 'pki_instance_name' MUST
                #        be different from the master's 'pki_instance_name'
                #        IF AND ONLY IF the master and clone are located on
                #        the same host!
                if not master.has_key('pki_ajp_port') or\
                   not len(master['pki_ajp_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_ajp_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_http_port') or\
                   not len(master['pki_http_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_http_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_https_port') or\
                   not len(master['pki_https_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_https_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_tomcat_server_port') or\
                   not len(master['pki_tomcat_server_port']):
                    # FUTURE:  Check for unused port value
                    #          (e. g. - must be different from master if the
                    #                   master is located on the same host)
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_tomcat_server_port",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_clone_pkcs12_path') or\
                   not len(master['pki_clone_pkcs12_path']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_clone_pkcs12_path",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                elif not os.path.isfile(master['pki_clone_pkcs12_path']):
                    config.pki_log.error(
                        log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
                        master['pki_clone_pkcs12_path'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_clone_replication_security') or\
                   not len(master['pki_clone_replication_security']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_clone_replication_security",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not master.has_key('pki_clone_uri') or\
                   not len(master['pki_clone_uri']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_clone_uri",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            elif master['pki_subsystem'] == "CA" and\
                 config.str2bool(master['pki_external']):
                if not master.has_key('pki_external_step_two') or\
                   not len(master['pki_external_step_two']):
                    config.pki_log.error(
                        log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                        "pki_external_step_two",
                        master['pki_user_deployment_cfg'],
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
                if not config.str2bool(master['pki_external_step_two']):
                    # External CA (Step 1)
                    if not master.has_key('pki_external_csr_path') or\
                       not len(master['pki_external_csr_path']):
                        config.pki_log.error(
                            log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                            "pki_external_csr_path",
                            master['pki_user_deployment_cfg'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                    elif os.path.exists(master['pki_external_csr_path']) and\
                         not os.path.isfile(master['pki_external_csr_path']):
                        config.pki_log.error(
                            log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
                            master['pki_external_csr_path'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                else:
                    # External CA (Step 2)
                    if not master.has_key('pki_external_ca_cert_chain_path') or\
                       not len(master['pki_external_ca_cert_chain_path']):
                        config.pki_log.error(
                            log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                            "pki_external_ca_cert_chain_path",
                            master['pki_user_deployment_cfg'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                    elif os.path.exists(
                                 master['pki_external_ca_cert_chain_path']) and\
                         not os.path.isfile(
                                 master['pki_external_ca_cert_chain_path']):
                        config.pki_log.error(
                            log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
                            master['pki_external_ca_cert_chain_path'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                    if not master.has_key('pki_external_ca_cert_path') or\
                       not len(master['pki_external_ca_cert_path']):
                        config.pki_log.error(
                            log.PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2,
                            "pki_external_ca_cert_path",
                            master['pki_user_deployment_cfg'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                    elif os.path.exists(master['pki_external_ca_cert_path']) and\
                         not os.path.isfile(
                                 master['pki_external_ca_cert_path']):
                        config.pki_log.error(
                            log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
                            master['pki_external_ca_cert_path'],
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
        return

    def populate_non_default_ports(self):
        if master['pki_http_port'] != \
            str(config.PKI_DEPLOYMENT_DEFAULT_TOMCAT_HTTP_PORT):
                ports.append(master['pki_http_port'])
        if master['pki_https_port'] != \
            str(config.PKI_DEPLOYMENT_DEFAULT_TOMCAT_HTTPS_PORT):
                ports.append(master['pki_https_port'])
        if master['pki_tomcat_server_port'] != \
            str(config.PKI_DEPLOYMENT_DEFAULT_TOMCAT_SERVER_PORT):
                ports.append(master['pki_tomcat_server_port'])
        if master['pki_ajp_port'] != \
            str(config.PKI_DEPLOYMENT_DEFAULT_TOMCAT_AJP_PORT):
                ports.append(master['pki_ajp_port'])
        return

    def verify_selinux_ports(self):
        # Determine which ports still need to be labelled, and if any are
        # incorrectly labelled
        if len(ports) == 0:
            return

        if not bool(selinux.is_selinux_enabled()):
            config.pki_log.error(
                log.PKIHELPER_SELINUX_DISABLED,
                extra=config.PKI_INDENTATION_LEVEL_2)
            return

        portrecs = seobject.portRecords().get_all()
        portlist = ports[:]
        for port in portlist:
            context = ""
            for i in portrecs:
                if portrecs[i][0] == "unreserved_port_t" or \
                   portrecs[i][0] == "reserved_port_t" or \
                   i[2] != "tcp":
                       continue
                if i[0] <= int(port) and int(port) <= i[1]:
                    context = portrecs[i][0]
                    break
            if context == "":
                # port has no current context
                # leave it in list of ports to set
                continue
            elif context == config.PKI_PORT_SELINUX_CONTEXT:
                # port is already set correctly
                # remove from list of ports to set
                ports.remove(port)
            else:
                config.pki_log.error(
                        log.PKIHELPER_INVALID_SELINUX_CONTEXT_FOR_PORT,
                        port, context,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        return

    def verify_command_matches_configuration_file(self):
        # Silently verify that the command-line parameters match the values
        # that are present in the corresponding configuration file
        if master['pki_deployment_executable'] == 'pkidestroy':
            if master['pki_deployed_instance_name'] !=\
               master['pki_instance_name']:
                config.pki_log.error(
                    log.PKIHELPER_COMMAND_LINE_PARAMETER_MISMATCH_2,
                    master['pki_deployed_instance_name'],
                    master['pki_instance_name'],
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        return



# PKI Deployment XML File Class
#class xml_file:
#    def remove_filter_section_from_web_xml(self,
#                                           web_xml_source,
#                                           web_xml_target):
#        config.pki_log.info(log.PKIHELPER_REMOVE_FILTER_SECTION_1,
#            master['pki_target_subsystem_web_xml'],
#            extra=config.PKI_INDENTATION_LEVEL_2)
#        begin_filters_section = False
#        begin_servlet_section = False
#        FILE = open(web_xml_target, "w")
#        for line in fileinput.FileInput(web_xml_source):
#            if not begin_filters_section:
#                # Read and write lines until first "<filter>" tag
#                if line.count("<filter>") >= 1:
#                    # Mark filters section
#                    begin_filters_section = True
#                else:
#                    FILE.write(line)
#            elif not begin_servlet_section:
#                # Skip lines until first "<servlet>" tag
#                if line.count("<servlet>") >= 1:
#                    # Mark servlets section and write out the opening tag
#                    begin_servlet_section = True
#                    FILE.write(line)
#                else:
#                    continue
#            else:
#                # Read and write lines all lines after "<servlet>" tag
#                FILE.write(line)
#        FILE.close()


# PKI Deployment Instance Class
class instance:
    def apache_instance_subsystems(self):
        rv = 0
        try:
            # count number of PKI subsystems present
            # within the specified Apache instance
            for subsystem in config.PKI_APACHE_SUBSYSTEMS:
                path = master['pki_instance_path'] + "/" + subsystem.lower()
                if os.path.exists(path) and os.path.isdir(path):
                    rv = rv + 1
            config.pki_log.debug(log.PKIHELPER_APACHE_INSTANCE_SUBSYSTEMS_2,
                                 master['pki_instance_path'],
                                 rv, extra=config.PKI_INDENTATION_LEVEL_2)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return rv

    def apache_instances(self):
        rv = 0
        try:
            # Since ALL directories under the top-level PKI 'apache' registry
            # directory SHOULD represent PKI Apache instances, and there
            # shouldn't be any stray files or symbolic links at this level,
            # simply count the number of PKI 'apache' instances (directories)
            # present within the PKI 'apache' registry directory
            for instance in\
                os.listdir(master['pki_instance_type_registry_path']):
                if os.path.isdir(
                       os.path.join(master['pki_instance_type_registry_path'],
                       instance)) and not\
                   os.path.islink(
                       os.path.join(master['pki_instance_type_registry_path'],
                       instance)):
                    rv = rv + 1
            config.pki_log.debug(log.PKIHELPER_APACHE_INSTANCES_2,
                                 master['pki_instance_type_registry_path'],
                                 rv,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return rv

    def pki_instance_subsystems(self):
        rv = 0
        try:
            # Since ALL directories within the top-level PKI infrastructure
            # SHOULD represent PKI instances, look for all possible
            # PKI instances within the top-level PKI infrastructure
            for instance in os.listdir(master['pki_path']):
                if os.path.isdir(os.path.join(master['pki_path'],instance))\
                   and not\
                   os.path.islink(os.path.join(master['pki_path'],instance)):
                    dir = os.path.join(master['pki_path'],instance)
                    # Since ANY directory within this PKI instance COULD
                    # be a PKI subsystem, look for all possible
                    # PKI subsystems within this PKI instance
                    for name in os.listdir(dir):
                        if os.path.isdir(os.path.join(dir,name)) and\
                           not os.path.islink(os.path.join(dir,name)):
                            if name.upper() in config.PKI_SUBSYSTEMS:
                                rv = rv + 1
            config.pki_log.debug(log.PKIHELPER_PKI_INSTANCE_SUBSYSTEMS_2,
                                 master['pki_instance_path'], rv,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return rv

    def tomcat_instance_subsystems(self):
        # Return list of PKI subsystems in the specified tomcat instance
        rv = []
        try:
            for subsystem in config.PKI_TOMCAT_SUBSYSTEMS:
                path = master['pki_instance_path'] + "/" + subsystem.lower()
                if os.path.exists(path) and os.path.isdir(path):
                    rv.append(subsystem)
        except OSErr as e:
            config.pki_log.error(log.PKI_OSERROR_1, str(e),
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return rv

    def tomcat_instances(self):
        rv = 0
        try:
            # Since ALL directories under the top-level PKI 'tomcat' registry
            # directory SHOULD represent PKI Tomcat instances, and there
            # shouldn't be any stray files or symbolic links at this level,
            # simply count the number of PKI 'tomcat' instances (directories)
            # present within the PKI 'tomcat' registry directory
            for instance in\
                os.listdir(master['pki_instance_type_registry_path']):
                if os.path.isdir(
                       os.path.join(master['pki_instance_type_registry_path'],
                       instance)) and not\
                   os.path.islink(
                       os.path.join(master['pki_instance_type_registry_path'],
                       instance)):
                    rv = rv + 1
            config.pki_log.debug(log.PKIHELPER_TOMCAT_INSTANCES_2,
                                 master['pki_instance_type_registry_path'],
                                 rv,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return rv

    def verify_subsystem_exists(self):
        try:
            if not os.path.exists(master['pki_subsystem_path']):
                config.pki_log.error(log.PKI_SUBSYSTEM_DOES_NOT_EXIST_2,
                                     master['pki_subsystem'],
                                     master['pki_instance_name'],
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

    def verify_subsystem_does_not_exist(self):
        try:
            if os.path.exists(master['pki_subsystem_path']):
                config.pki_log.error(log.PKI_SUBSYSTEM_ALREADY_EXISTS_2,
                                     master['pki_subsystem'],
                                     master['pki_instance_name'],
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

    def get_instance_status(self):
        self.connection = pki.client.PKIConnection(
            protocol='https',
            hostname=master['pki_hostname'],
            port=master['pki_https_port'],
            subsystem=master['pki_subsystem_type'],
            accept = 'application/xml')

        # catching all exceptions because we do not want to break if underlying
        # requests or urllib3 use a different exception.  
        # If the connection fails, we will time out in any case
        # pylint: disable-msg=W0703
        try:
            client = pki.system.SystemStatusClient(self.connection)
            response = client.getStatus()
            config.pki_log.debug(response,
                extra=config.PKI_INDENTATION_LEVEL_3)

            root = ET.fromstring(response)
            status = root.findtext("Status")
            return status
        except Exception as exc:
            config.pki_log.debug("No connection - server may still be down",
                extra=config.PKI_INDENTATION_LEVEL_3)
            config.pki_log.debug("No connection - exception thrown: " +\
                str(exc),
                extra=config.PKI_INDENTATION_LEVEL_3)
            return None

    def wait_for_startup(self, timeout):
        start_time = datetime.today()
        status = None
        while status != "running":
            status = self.get_instance_status()
            time.sleep(1);
            stop_time = datetime.today()
            if (stop_time - start_time).total_seconds() >= timeout:
                break
        return status

# PKI Deployment Directory Class
class directory:
    def create(self, name, uid=None, gid=None,
               perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
               acls=None, critical_failure=True):
        try:
            if not os.path.exists(name):
                # mkdir -p <name>
                config.pki_log.info(log.PKIHELPER_MKDIR_1, name,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                os.makedirs(name)
                # chmod <perms> <name>
                config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(name, perms)
                # chown <uid>:<gid> <name>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                     uid, gid, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(name, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = name
                record.type = manifest.RECORD_TYPE_DIRECTORY
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions = perms
                record.acls = acls
                manifest.database.append(record)
            elif not os.path.isdir(name):
                config.pki_log.error(
                    log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            if exc.errno == errno.EEXIST:
                pass
            else:
                config.pki_log.error(log.PKI_OSERROR_1, exc,
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    raise
        return

    def modify(self, name, uid=None, gid=None,
               perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
               acls=None, silent=False, critical_failure=True):
        try:
            if os.path.exists(name):
                if not os.path.isdir(name):
                    config.pki_log.error(
                        log.PKI_DIRECTORY_ALREADY_EXISTS_NOT_A_DIRECTORY_1,
                        name, extra=config.PKI_INDENTATION_LEVEL_2)
                    if critical_failure == True:
                        sys.exit(1)
                # Always re-process each directory whether it needs it or not
                if not silent:
                    config.pki_log.info(log.PKIHELPER_MODIFY_DIR_1, name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                # chmod <perms> <name>
                if not silent:
                    config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(name, perms)
                # chown <uid>:<gid> <name>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                if not silent:
                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                         uid, gid, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(name, uid, gid)
                # Store record in installation manifest
                if not silent:
                    record = manifest.record()
                    record.name = name
                    record.type = manifest.RECORD_TYPE_DIRECTORY
                    record.user = master['pki_user']
                    record.group = master['pki_group']
                    record.uid = uid
                    record.gid = gid
                    record.permissions = perms
                    record.acls = acls
                    manifest.database.append(record)
            else:
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def delete(self, name, recursive_flag=True, critical_failure=True):
        try:
            if not os.path.exists(name) or not os.path.isdir(name):
                # Simply issue a warning and continue
                config.pki_log.warning(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
            else:
                if recursive_flag == True:
                    # rm -rf <name>
                    config.pki_log.info(log.PKIHELPER_RM_RF_1, name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                    shutil.rmtree(name)
                else:
                    # rmdir <name>
                    config.pki_log.info(log.PKIHELPER_RMDIR_1, name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                    os.rmdir(name)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def exists(self, name):
        try:
            if not os.path.exists(name) or not os.path.isdir(name):
                return False
            else:
                return True
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

    def is_empty(self, name):
        try:
            if not os.listdir(name):
                config.pki_log.debug(log.PKIHELPER_DIRECTORY_IS_EMPTY_1,
                                     name, extra=config.PKI_INDENTATION_LEVEL_2)
                return True
            else:
                config.pki_log.debug(log.PKIHELPER_DIRECTORY_IS_NOT_EMPTY_1,
                                     name, extra=config.PKI_INDENTATION_LEVEL_2)
                return False
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

    def set_mode(self, name, uid=None, gid=None,
                 dir_perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
                 file_perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
                 symlink_perms=\
                     config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
                 dir_acls=None, file_acls=None, symlink_acls=None,
                 recursive_flag=True, critical_failure=True):
        try:
            if not os.path.exists(name) or not os.path.isdir(name):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            else:
                config.pki_log.info(
                    log.PKIHELPER_SET_MODE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                if recursive_flag == True:
                    for root, dirs, files in os.walk(name):
                        for name in files:
                            entity = os.path.join(root, name)
                            if not os.path.islink(entity):
                                file = entity
                                config.pki_log.debug(
                                    log.PKIHELPER_IS_A_FILE_1, file,
                                    extra=config.PKI_INDENTATION_LEVEL_3)
                                # chmod <file_perms> <name>
                                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                    file_perms, file,
                                    extra=config.PKI_INDENTATION_LEVEL_3)
                                os.chmod(file, file_perms)
                                # chown <uid>:<gid> <name>
                                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                    uid, gid, file,
                                    extra=config.PKI_INDENTATION_LEVEL_3)
                                os.chown(file, uid, gid)
                                # Store record in installation manifest
                                record = manifest.record()
                                record.name = name
                                record.type = manifest.RECORD_TYPE_FILE
                                record.user = master['pki_user']
                                record.group = master['pki_group']
                                record.uid = uid
                                record.gid = gid
                                record.permissions = file_perms
                                record.acls = file_acls
                                manifest.database.append(record)
                            else:
                                symlink = entity
                                config.pki_log.debug(
                                    log.PKIHELPER_IS_A_SYMLINK_1, symlink,
                                    extra=config.PKI_INDENTATION_LEVEL_3)
                                # REMINDER:  Due to POSIX compliance, 'lchmod'
                                #            is NEVER implemented on Linux
                                #            systems since 'chmod' CANNOT be
                                #            run directly against symbolic
                                #            links!
                                # chown -h <uid>:<gid> <symlink>
                                config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
                                    uid, gid, symlink,
                                    extra=config.PKI_INDENTATION_LEVEL_3)
                                os.lchown(symlink, uid, gid)
                                # Store record in installation manifest
                                record = manifest.record()
                                record.name = name
                                record.type = manifest.RECORD_TYPE_SYMLINK
                                record.user = master['pki_user']
                                record.group = master['pki_group']
                                record.uid = uid
                                record.gid = gid
                                record.permissions = symlink_perms
                                record.acls = symlink_acls
                                manifest.database.append(record)
                        for name in dirs:
                            dir = os.path.join(root, name)
                            config.pki_log.debug(
                                log.PKIHELPER_IS_A_DIRECTORY_1, dir,
                                extra=config.PKI_INDENTATION_LEVEL_3)
                            # chmod <dir_perms> <name>
                            config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                dir_perms, dir,
                                extra=config.PKI_INDENTATION_LEVEL_3)
                            os.chmod(dir, dir_perms)
                            # chown <uid>:<gid> <name>
                            config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                uid, gid, dir,
                                extra=config.PKI_INDENTATION_LEVEL_3)
                            os.chown(dir, uid, gid)
                            # Store record in installation manifest
                            record = manifest.record()
                            record.name = name
                            record.type = manifest.RECORD_TYPE_DIRECTORY
                            record.user = master['pki_user']
                            record.group = master['pki_group']
                            record.uid = uid
                            record.gid = gid
                            record.permissions = dir_perms
                            record.acls = dir_acls
                            manifest.database.append(record)
                else:
                    config.pki_log.debug(
                        log.PKIHELPER_IS_A_DIRECTORY_1, name,
                        extra=config.PKI_INDENTATION_LEVEL_3)
                    name = os.path.join(root, name)
                    # chmod <dir_perms> <name>
                    config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                         dir_perms, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                    os.chmod(name, dir_perms)
                    # chown <uid>:<gid> <name>
                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                         uid, gid, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                    os.chown(name, uid, gid)
                    # Store record in installation manifest
                    record = manifest.record()
                    record.name = name
                    record.type = manifest.RECORD_TYPE_DIRECTORY
                    record.user = master['pki_user']
                    record.group = master['pki_group']
                    record.uid = uid
                    record.gid = gid
                    record.permissions = dir_perms
                    record.acls = dir_acls
                    manifest.database.append(record)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise

    def copy(self, old_name, new_name, uid=None, gid=None,
             dir_perms=config.PKI_DEPLOYMENT_DEFAULT_DIR_PERMISSIONS,
             file_perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
             symlink_perms=config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS,
             dir_acls=None, file_acls=None, symlink_acls=None,
             recursive_flag=True, overwrite_flag=False, critical_failure=True):
        try:
            if not os.path.exists(old_name) or not os.path.isdir(old_name):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, old_name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            else:
                if os.path.exists(new_name):
                    if not overwrite_flag:
                        config.pki_log.error(
                            log.PKI_DIRECTORY_ALREADY_EXISTS_1, new_name,
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                if recursive_flag == True:
                    # cp -rp <old_name> <new_name>
                    config.pki_log.info(log.PKIHELPER_CP_RP_2,
                                        old_name, new_name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                    # Due to a limitation in the 'shutil.copytree()'
                    # implementation which requires that
                    # 'The destination directory must not already exist.',
                    # an OSError exception is always thrown due to the
                    # implementation's unchecked call to 'os.makedirs(dst)'.
                    # Consequently, a 'patched' local copy of this routine has
                    # been included in this file with the appropriate fix.
                    pki_copytree(old_name, new_name)
                else:
                    # cp -p <old_name> <new_name>
                    config.pki_log.info(log.PKIHELPER_CP_P_2,
                                        old_name, new_name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                    shutil.copy2(old_name, new_name)
                # set ownerships, permissions, and acls
                # of newly created top-level directory
                self.modify(new_name, uid, gid, dir_perms, dir_acls,
                            True, critical_failure)
                # set ownerships, permissions, and acls
                # of contents of newly created top-level directory
                self.set_mode(new_name, uid, gid,
                              dir_perms, file_perms, symlink_perms,
                              dir_acls, file_acls, symlink_acls,
                              recursive_flag, critical_failure)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except shutil.Error as exc:
            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return


# PKI Deployment File Class (also used for executables)
class file:
    def create(self, name, uid=None, gid=None,
               perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
               acls=None, critical_failure=True):
        try:
            if not os.path.exists(name):
                # touch <name>
                config.pki_log.info(log.PKIHELPER_TOUCH_1, name,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                open(name, "w").close()
                # chmod <perms> <name>
                config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(name, perms)
                # chown <uid>:<gid> <name>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                     uid, gid, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(name, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = name
                record.type = manifest.RECORD_TYPE_FILE
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions = perms
                record.acls = acls
                manifest.database.append(record)
            elif not os.path.isfile(name):
                config.pki_log.error(
                    log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            if exc.errno == errno.EEXIST:
                pass
            else:
                config.pki_log.error(log.PKI_OSERROR_1, exc,
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    raise
        return

    def modify(self, name, uid=None, gid=None,
               perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
               acls=None, silent=False, critical_failure=True):
        try:
            if os.path.exists(name):
                if not os.path.isfile(name):
                    config.pki_log.error(
                        log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1,
                        name, extra=config.PKI_INDENTATION_LEVEL_2)
                    if critical_failure == True:
                        sys.exit(1)
                # Always re-process each file whether it needs it or not
                if not silent:
                    config.pki_log.info(log.PKIHELPER_MODIFY_FILE_1, name,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                # chmod <perms> <name>
                if not silent:
                    config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(name, perms)
                # chown <uid>:<gid> <name>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                if not silent:
                    config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                         uid, gid, name,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(name, uid, gid)
                # Store record in installation manifest
                if not silent:
                    record = manifest.record()
                    record.name = name
                    record.type = manifest.RECORD_TYPE_FILE
                    record.user = master['pki_user']
                    record.group = master['pki_group']
                    record.uid = uid
                    record.gid = gid
                    record.permissions = perms
                    record.acls = acls
                    manifest.database.append(record)
            else:
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def delete(self, name, critical_failure=True):
        try:
            if not os.path.exists(name) or not os.path.isfile(name):
                # Simply issue a warning and continue
                config.pki_log.warning(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
            else:
                # rm -f <name>
                config.pki_log.info(log.PKIHELPER_RM_F_1, name,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                os.remove(name)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def exists(self, name):
        try:
            if not os.path.exists(name) or not os.path.isfile(name):
                return False
            else:
                return True
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

    def copy(self, old_name, new_name, uid=None, gid=None,
             perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS, acls=None,
             overwrite_flag=False, critical_failure=True):
        try:
            if not os.path.exists(old_name) or not os.path.isfile(old_name):
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, old_name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            else:
                if os.path.exists(new_name):
                    if not overwrite_flag:
                        config.pki_log.error(
                            log.PKI_FILE_ALREADY_EXISTS_1, new_name,
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                # cp -p <old_name> <new_name>
                config.pki_log.info(log.PKIHELPER_CP_P_2,
                                    old_name, new_name,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                shutil.copy2(old_name, new_name)
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                # chmod <perms> <new_name>
                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                     perms, new_name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(new_name, perms)
                # chown <uid>:<gid> <new_name>
                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                     uid, gid, new_name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(new_name, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = new_name
                record.type = manifest.RECORD_TYPE_FILE
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions = perms
                record.acls = acls
                manifest.database.append(record)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except shutil.Error as exc:
            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def apply_slot_substitution(
                         self, name, uid=None, gid=None,
                         perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
                         acls=None, critical_failure=True):
        try:
            if not os.path.exists(name) or not os.path.isfile(name):
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            # applying in-place slot substitutions on <name>
            config.pki_log.info(log.PKIHELPER_APPLY_SLOT_SUBSTITUTION_1,
                                name,
                                extra=config.PKI_INDENTATION_LEVEL_2)
            for line in fileinput.FileInput(name, inplace=1):
                for slot in slots:
                    if slot != '__name__' and slots[slot] in line:
                        config.pki_log.debug(
                            log.PKIHELPER_SLOT_SUBSTITUTION_2,
                            slots[slot], master[slot],
                            extra=config.PKI_INDENTATION_LEVEL_3)
                        line=line.replace(slots[slot],master[slot])
                sys.stdout.write(line)
            if uid == None:
                uid = identity.get_uid()
            if gid == None:
                gid = identity.get_gid()
            # chmod <perms> <name>
            config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                 perms, name,
                                 extra=config.PKI_INDENTATION_LEVEL_3)
            os.chmod(name, perms)
            # chown <uid>:<gid> <name>
            config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                 uid, gid, name,
                                 extra=config.PKI_INDENTATION_LEVEL_3)
            os.chown(name, uid, gid)
            # Store record in installation manifest
            record = manifest.record()
            record.name = name
            record.type = manifest.RECORD_TYPE_FILE
            record.user = master['pki_user']
            record.group = master['pki_group']
            record.uid = uid
            record.gid = gid
            record.permissions = perms
            record.acls = acls
            manifest.database.append(record)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except shutil.Error as exc:
            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def copy_with_slot_substitution(
                         self, old_name, new_name, uid=None, gid=None,
                         perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
                         acls=None, overwrite_flag=False,
                         critical_failure=True):
        try:
            if not os.path.exists(old_name) or not os.path.isfile(old_name):
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, old_name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            else:
                if os.path.exists(new_name):
                    if not overwrite_flag:
                        config.pki_log.error(
                            log.PKI_FILE_ALREADY_EXISTS_1, new_name,
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                # copy <old_name> to <new_name> with slot substitutions
                config.pki_log.info(log.PKIHELPER_COPY_WITH_SLOT_SUBSTITUTION_2,
                                    old_name, new_name,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                FILE = open(new_name, "w")
                for line in fileinput.FileInput(old_name):
                    for slot in slots:
                        if slot != '__name__' and slots[slot] in line:
                            config.pki_log.debug(
                                log.PKIHELPER_SLOT_SUBSTITUTION_2,
                                slots[slot], master[slot],
                                extra=config.PKI_INDENTATION_LEVEL_3)
                            line=line.replace(slots[slot],master[slot])
                    FILE.write(line)
                FILE.close()
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                # chmod <perms> <new_name>
                config.pki_log.debug(log.PKIHELPER_CHMOD_2,
                                     perms, new_name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(new_name, perms)
                # chown <uid>:<gid> <new_name>
                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                     uid, gid, new_name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(new_name, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = new_name
                record.type = manifest.RECORD_TYPE_FILE
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions = perms
                record.acls = acls
                manifest.database.append(record)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except shutil.Error as exc:
            config.pki_log.error(log.PKI_SHUTIL_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def generate_noise_file(self, name, bytes, uid=None, gid=None,
            perms=config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS,
            acls=None, critical_failure=True):
        try:
            if not os.path.exists(name):
                # generating noise file called <name> and
                # filling it with <bytes> random bytes
                config.pki_log.info(log.PKIHELPER_NOISE_FILE_2, name, bytes,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                open(name, "w").close()
                FILE = open(name, "w")
                noise = ''.join(random.choice(string.ascii_letters +\
                        string.digits) for x in range(bytes))
                FILE.write(noise)
                FILE.close()
                # chmod <perms> <name>
                config.pki_log.debug(log.PKIHELPER_CHMOD_2, perms, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chmod(name, perms)
                # chown <uid>:<gid> <name>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                config.pki_log.debug(log.PKIHELPER_CHOWN_3,
                                     uid, gid, name,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.chown(name, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = name
                record.type = manifest.RECORD_TYPE_FILE
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions = perms
                record.acls = acls
                manifest.database.append(record)
            elif not os.path.isfile(name):
                config.pki_log.error(
                    log.PKI_FILE_ALREADY_EXISTS_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            if exc.errno == errno.EEXIST:
                pass
            else:
                config.pki_log.error(log.PKI_OSERROR_1, exc,
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    raise
        return


# PKI Deployment Symbolic Link Class
class symlink:
    def create(self, name, link, uid=None, gid=None,
               acls=None, allow_dangling_symlink=False, critical_failure=True):
        try:
            if not os.path.exists(link):
                if not os.path.exists(name):
                    config.pki_log.warning(
                        log.PKIHELPER_DANGLING_SYMLINK_2, link, name,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    if not allow_dangling_symlink:
                        sys.exit(1)
                # ln -s <name> <link>
                config.pki_log.info(log.PKIHELPER_LINK_S_2, name, link,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                os.symlink(name, link)
                # REMINDER:  Due to POSIX compliance, 'lchmod' is NEVER
                #            implemented on Linux systems since 'chmod'
                #            CANNOT be run directly against symbolic links!
                # chown -h <uid>:<gid> <link>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
                                     uid, gid, link,
                                     extra=config.PKI_INDENTATION_LEVEL_3)
                os.lchown(link, uid, gid)
                # Store record in installation manifest
                record = manifest.record()
                record.name = link
                record.type = manifest.RECORD_TYPE_SYMLINK
                record.user = master['pki_user']
                record.group = master['pki_group']
                record.uid = uid
                record.gid = gid
                record.permissions =\
                    config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS
                record.acls = acls
                manifest.database.append(record)
            elif not os.path.islink(link):
                config.pki_log.error(
                    log.PKI_SYMLINK_ALREADY_EXISTS_NOT_A_SYMLINK_1, link,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            if exc.errno == errno.EEXIST:
                pass
            else:
                config.pki_log.error(log.PKI_OSERROR_1, exc,
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    raise
        return

    def modify(self, link, uid=None, gid=None,
               acls=None, silent=False, critical_failure=True):
        try:
            if os.path.exists(link):
                if not os.path.islink(link):
                    config.pki_log.error(
                        log.PKI_SYMLINK_ALREADY_EXISTS_NOT_A_SYMLINK_1,
                        link, extra=config.PKI_INDENTATION_LEVEL_2)
                    if critical_failure == True:
                        sys.exit(1)
                # Always re-process each link whether it needs it or not
                if not silent:
                    config.pki_log.info(log.PKIHELPER_MODIFY_SYMLINK_1, link,
                                        extra=config.PKI_INDENTATION_LEVEL_2)
                # REMINDER:  Due to POSIX compliance, 'lchmod' is NEVER
                #            implemented on Linux systems since 'chmod'
                #            CANNOT be run directly against symbolic links!
                # chown -h <uid>:<gid> <link>
                if uid == None:
                    uid = identity.get_uid()
                if gid == None:
                    gid = identity.get_gid()
                if not silent:
                    config.pki_log.debug(log.PKIHELPER_CHOWN_H_3,
                                         uid, gid, link,
                                         extra=config.PKI_INDENTATION_LEVEL_3)
                os.lchown(link, uid, gid)
                # Store record in installation manifest
                if not silent:
                    record = manifest.record()
                    record.name = link
                    record.type = manifest.RECORD_TYPE_SYMLINK
                    record.user = master['pki_user']
                    record.group = master['pki_group']
                    record.uid = uid
                    record.gid = gid
                    record.permissions =\
                        config.PKI_DEPLOYMENT_DEFAULT_SYMLINK_PERMISSIONS
                    record.acls = acls
                    manifest.database.append(record)
            else:
                config.pki_log.error(
                    log.PKI_SYMLINK_MISSING_OR_NOT_A_SYMLINK_1, link,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def delete(self, link, critical_failure=True):
        try:
            if not os.path.exists(link) or not os.path.islink(link):
                # Simply issue a warning and continue
                config.pki_log.warning(
                    log.PKI_SYMLINK_MISSING_OR_NOT_A_SYMLINK_1, link,
                    extra=config.PKI_INDENTATION_LEVEL_2)
            else:
                # rm -f <link>
                config.pki_log.info(log.PKIHELPER_RM_F_1, link,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                os.remove(link)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def exists(self, name):
        try:
            if not os.path.exists(name) or not os.path.islink(name):
                return False
            else:
                return True
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            raise

# PKI Deployment War File Class
class war:
    def explode(self, name, path, critical_failure=True):
        try:
            if os.path.exists(name) and os.path.isfile(name):
                if not zipfile.is_zipfile(name):
                    config.pki_log.error(
                        log.PKI_FILE_NOT_A_WAR_FILE_1,
                        name, extra=config.PKI_INDENTATION_LEVEL_2)
                    if critical_failure == True:
                        sys.exit(1)
                if not os.path.exists(path) or not os.path.isdir(path):
                    config.pki_log.error(
                        log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
                        path, extra=config.PKI_INDENTATION_LEVEL_2)
                    if critical_failure == True:
                        sys.exit(1)
                # jar -xf <name> -C <path>
                config.pki_log.info(log.PKIHELPER_JAR_XF_C_2, name, path,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                # Open war file
                war = zipfile.ZipFile(name, 'r')
                # Extract contents of war file to path
                war.extractall(path)
            else:
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1, name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(1)
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except zipfile.BadZipfile as exc:
            config.pki_log.error(log.PKI_BADZIPFILE_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except zipfile.LargeZipFile as exc:
            config.pki_log.error(log.PKI_LARGEZIPFILE_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return


# PKI Deployment Password Class
class password:
    def create_password_conf(self, path, pin, pin_sans_token=False,
                             overwrite_flag=False, critical_failure=True):
        try:
            if os.path.exists(path):
                if overwrite_flag:
                    config.pki_log.info(
                        log.PKIHELPER_PASSWORD_CONF_1, path,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    # overwrite the existing 'password.conf' file
                    with open(path, "w") as fd:
                        if pin_sans_token == True:
                            fd.write(str(pin))
                        elif master['pki_subsystem'] in\
                           config.PKI_APACHE_SUBSYSTEMS:
                            fd.write(master['pki_self_signed_token'] +\
                                     ":" + str(pin))
                        else:
                            fd.write(master['pki_self_signed_token'] +\
                                     "=" + str(pin))
                    fd.closed
            else:
                config.pki_log.info(log.PKIHELPER_PASSWORD_CONF_1, path,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                # create a new 'password.conf' file
                with open(path, "w") as fd:
                    if pin_sans_token == True:
                        fd.write(str(pin))
                    elif master['pki_subsystem'] in\
                       config.PKI_APACHE_SUBSYSTEMS:
                        fd.write(master['pki_self_signed_token'] +\
                                 ":" + str(pin))
                    else:
                        fd.write(master['pki_self_signed_token'] +\
                                 "=" + str(pin))
                fd.closed
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def create_client_pkcs12_password_conf(self, path, overwrite_flag=False,
                                           critical_failure=True):
        try:
            if os.path.exists(path):
                if overwrite_flag:
                    config.pki_log.info(
                        log.PKIHELPER_PASSWORD_CONF_1, path,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    # overwrite the existing 'pkcs12_password.conf' file
                    with open(path, "w") as fd:
                        fd.write(master['pki_client_pkcs12_password'])
                    fd.closed
            else:
                config.pki_log.info(log.PKIHELPER_PASSWORD_CONF_1, path,
                                    extra=config.PKI_INDENTATION_LEVEL_2)
                # create a new 'pkcs12_password.conf' file
                with open(path, "w") as fd:
                    fd.write(master['pki_client_pkcs12_password'])
                fd.closed
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def get_password(self, path, token_name, critical_failure=True):
        if os.path.exists(path) and os.path.isfile(path) and\
           os.access(path, os.R_OK):
            tokens = PKIConfigParser.read_simple_configuration_file(path)
            hardware_token = "hardware-" + token_name
            if tokens.has_key(hardware_token):
                token_name = hardware_token
                token_pwd = tokens[hardware_token]
            elif tokens.has_key(token_name):
                   token_pwd = tokens[token_name]

        if token_pwd is None or token_pwd == '':
            # TODO prompt for this password
            config.pki_log.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
                                 token_name,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
            else:
                return
        return token_pwd


# PKI Deployment NSS 'certutil' Class
class certutil:
    def create_security_databases(self, path, pki_cert_database,
                                  pki_key_database, pki_secmod_database,
                                  password_file=None, prefix=None,
                                  critical_failure=True):
        try:
            # Compose this "certutil" command
            command = "certutil" + " " + "-N"
            #   Provide a path to the NSS security databases
            if path:
                command = command + " " + "-d" + " " + path
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_PATH,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if password_file != None:
                command = command + " " + "-f" + " " + password_file
            if prefix != None:
                command = command + " " + "-P" + " " + prefix
            if not os.path.exists(path):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, path,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if os.path.exists(pki_cert_database) or\
               os.path.exists(pki_key_database) or\
               os.path.exists(pki_secmod_database):
                # Simply notify user that the security databases exist
                config.pki_log.info(
                    log.PKI_SECURITY_DATABASES_ALREADY_EXIST_3,
                    pki_cert_database,
                    pki_key_database,
                    pki_secmod_database,
                    extra=config.PKI_INDENTATION_LEVEL_2)
            else:
                if password_file != None:
                    if not os.path.exists(password_file) or\
                       not os.path.isfile(password_file):
                        config.pki_log.error(
                            log.PKI_FILE_MISSING_OR_NOT_A_FILE_1,
                            password_file,
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        sys.exit(1)
                # Display this "certutil" command
                config.pki_log.info(
                    log.PKIHELPER_CREATE_SECURITY_DATABASES_1,
                    command,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                # Execute this "certutil" command
                subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def verify_certificate_exists(self, path, pki_cert_database,
                                  pki_key_database, pki_secmod_database,
                                  token, nickname, password_file=None,
                                  silent=True):
        rv = 0
        try:
            # Compose this "certutil" command
            command = "certutil" + " " + "-L"
            #   Provide a path to the NSS security databases
            if path:
                command = command + " " + "-d" + " " + path
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_PATH,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the 'token'
            if token:
                command = command + " " + "-h" + " " + "'" + token + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_TOKEN,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the nickname of this self-signed certificate
            if nickname:
                command = command + " " + "-n" + " " + "'" + nickname + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_NICKNAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   OPTIONALLY specify a password file
            if password_file != None:
                command = command + " " + "-f" + " " + password_file
            #   By default, execute this command silently
            if silent != False:
                command = command + " > /dev/null 2>&1"
            if not os.path.exists(path):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, path,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if not os.path.exists(pki_cert_database) or\
               not os.path.exists(pki_key_database) or\
               not os.path.exists(pki_secmod_database):
                # NSS security databases MUST exist!
                config.pki_log.error(
                    log.PKI_SECURITY_DATABASES_DO_NOT_EXIST_3,
                    pki_cert_database,
                    pki_key_database,
                    pki_secmod_database,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if password_file != None:
                if not os.path.exists(password_file) or\
                   not os.path.isfile(password_file):
                    config.pki_log.error(
                        log.PKI_FILE_MISSING_OR_NOT_A_FILE_1,
                        password_file,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # Execute this "certutil" command
            subprocess.check_call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            return False
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return True

    def generate_self_signed_certificate(self, path, pki_cert_database,
                                         pki_key_database, pki_secmod_database,
                                         token, nickname,
                                         subject, serial_number,
                                         validity_period, issuer_name,
                                         trustargs, noise_file,
                                         password_file=None,
                                         critical_failure=True):
        try:
            # Compose this "certutil" command
            command = "certutil" + " " + "-S"
            #   Provide a path to the NSS security databases
            if path:
                command = command + " " + "-d" + " " + path
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_PATH,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the 'token'
            if token:
                command = command + " " + "-h" + " " + "'" + token + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_TOKEN,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the nickname of this self-signed certificate
            if nickname:
                command = command + " " + "-n" + " " + "'" + nickname + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_NICKNAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the subject name (RFC1485)
            if subject:
                command = command + " " + "-s" + " " + "'" + subject + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_SUBJECT,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the serial number
            if serial_number != None:
                command = command + " " + "-m" + " " + str(serial_number)
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_SERIAL_NUMBER,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the months valid
            if validity_period != None:
                command = command + " " + "-v" + " " + str(validity_period)
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_VALIDITY_PERIOD,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the nickname of the issuer certificate
            if issuer_name:
                command = command + " " + "-c" + " " +\
                      "'" + issuer_name + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_ISSUER_NAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify the certificate trust attributes
            if trustargs:
                command = command + " " + "-t" + " " + "'" + trustargs + "'"
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_TRUSTARGS,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   Specify a noise file to be used for key generation
            if noise_file:
                command = command + " " + "-z" + " " + noise_file
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_NOISE_FILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            #   OPTIONALLY specify a password file
            if password_file != None:
                command = command + " " + "-f" + " " + password_file
            #   ALWAYS self-sign this certificate
            command = command + " " + "-x"
            #   ALWAYS mask the command-line output of this command
            command = command + " " + "> /dev/null 2>&1"
            # Display this "certutil" command
            config.pki_log.info(
                log.PKIHELPER_CERTUTIL_SELF_SIGNED_CERTIFICATE_1, command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if not os.path.exists(path):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1, path,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if not os.path.exists(pki_cert_database) or\
               not os.path.exists(pki_key_database) or\
               not os.path.exists(pki_secmod_database):
                # NSS security databases MUST exist!
                config.pki_log.error(
                    log.PKI_SECURITY_DATABASES_DO_NOT_EXIST_3,
                    pki_cert_database,
                    pki_key_database,
                    pki_secmod_database,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if not os.path.exists(noise_file):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
                    noise_file,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if password_file != None:
                if not os.path.exists(password_file) or\
                   not os.path.isfile(password_file):
                    config.pki_log.error(
                        log.PKI_FILE_MISSING_OR_NOT_A_FILE_1,
                        password_file,
                        extra=config.PKI_INDENTATION_LEVEL_2)
                    sys.exit(1)
            # Execute this "certutil" command
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def import_cert(self, nickname, trust, input_file, password_file,
                    path=None, token=None, critical_failure=True):
        try:
            command = ["certutil","-A"]
            if path:
                command.extend(["-d", path])

            if token:
                command.extend(["-h", token])

            if nickname:
                command.extend(["-n", nickname ])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_NICKNAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if trust:
                command.extend(["-t", trust])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_TRUSTARGS,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if input_file:
                command.extend(["-i", input_file])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_INPUT_FILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if password_file:
                command.extend(["-f", password_file])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_PASSWORD_FILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            config.pki_log.info(command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            subprocess.call(command)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def generate_certificate_request(self, subject, key_size,
                                     password_file, noise_file,
                                     output_file = None, path = None,
                                     ascii_format = None, token = None,
                                     critical_failure=True):
        try:
            command = ["certutil", "-R"]
            if path:
                command.extend(["-d", path])
            else:
                command.extend(["-d", "."])

            if token:
                command.extend(["-h", token])

            if subject:
                command.extend(["-s", subject])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_SUBJECT,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if key_size:
                command.extend(["-g", str(key_size)])

            if noise_file:
                command.extend(["-z", noise_file])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_NOISE_FILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if password_file:
                command.extend(["-f", password_file])
            else:
                config.pki_log.error(
                    log.PKIHELPER_CERTUTIL_MISSING_PASSWORD_FILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            if output_file:
                command.extend(["-o", output_file])

            # set acsii output
            if ascii_format:
                command.append("-a")

            # Display this "certutil" command
            config.pki_log.info(
                log.PKIHELPER_CERTUTIL_GENERATE_CSR_1, command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if not os.path.exists(noise_file):
                config.pki_log.error(
                    log.PKI_DIRECTORY_MISSING_OR_NOT_A_DIRECTORY_1,
                    noise_file,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if not os.path.exists(password_file) or\
               not os.path.isfile(password_file):
                config.pki_log.error(
                    log.PKI_FILE_MISSING_OR_NOT_A_FILE_1,
                    password_file,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            # Execute this "certutil" command
            with open(os.devnull, "w") as fnull:
                subprocess.call(command, stdout=fnull, stderr=fnull)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

# pk12util class
class pk12util:
    def create_file(self, out_file, nickname, out_pwfile,
                    db_pwfile, path=None):
        try:
            command = ["pk12util"]
            if path:
                command.extend(["-d", path])
            if out_file:
                command.extend(["-o", out_file])
            else:
                config.pki_log.error(
                    log.PKIHELPER_PK12UTIL_MISSING_OUTFILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if nickname:
                command.extend(["-n", nickname])
            else:
                config.pki_log.error(
                    log.PKIHELPER_PK12UTIL_MISSING_NICKNAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if out_pwfile:
                command.extend(["-w", out_pwfile])
            else:
                config.pki_log.error(
                    log.PKIHELPER_PK12UTIL_MISSING_OUTPWFILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)
            if db_pwfile:
                command.extend(["-k", db_pwfile])
            else:
                config.pki_log.error(
                    log.PKIHELPER_PK12UTIL_MISSING_DBPWFILE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                sys.exit(1)

            config.pki_log.info(command,
                    extra=config.PKI_INDENTATION_LEVEL_2)
            with open(os.devnull, "w") as fnull:
                subprocess.call(command, stdout=fnull, stderr=fnull)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        except OSError as exc:
            config.pki_log.error(log.PKI_OSERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return


# KRA Connector Class
class kra_connector:
    def deregister(self, critical_failure=False):
        try:
            # this is applicable to KRAs only
            if master['pki_subsystem_type'] != "kra":
                return

            config.pki_log.info(
                log.PKIHELPER_KRACONNECTOR_UPDATE_CONTACT,
                extra=config.PKI_INDENTATION_LEVEL_2)

            cs_cfg = PKIConfigParser.read_simple_configuration_file(
                         master['pki_target_cs_cfg'])
            krahost = cs_cfg.get('service.machineName')
            kraport = cs_cfg.get('pkicreate.secure_port')
            cahost = cs_cfg.get('cloning.ca.hostname')
            caport = cs_cfg.get('cloning.ca.httpsport')
            if cahost is None or\
               caport is None:
                config.pki_log.warning(
                    log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_CA_HOST_PORT,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(-1)
                else:
                    return

            # retrieve subsystem nickname
            subsystemnick = cs_cfg.get('kra.cert.subsystem.nickname')
            if subsystemnick is None:
                config.pki_log.warning(
                    log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(-1)
                else:
                    return

            # retrieve name of token based upon type (hardware/software)
            if ':' in subsystemnick:
                token_name = subsystemnick.split(':')[0]
            else:
                token_name = "internal"

            token_pwd = password.get_password(
                            master['pki_shared_password_conf'],
                            token_name,
                            critical_failure)

            if token_pwd is None or token_pwd == '':
                config.pki_log.warning(
                    log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                config.pki_log.error(
                    log.PKIHELPER_UNDEFINED_TOKEN_PASSWD_1,
                    token_name,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(-1)
                else:
                    return

            self.execute_using_sslget(caport, cahost, subsystemnick,
                                 token_pwd, krahost, kraport)

        except subprocess.CalledProcessError as exc:
            config.pki_log.warning(
                log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2,
                str(krahost),
                str(kraport),
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def execute_using_pki(self, caport, cahost, subsystemnick,
      token_pwd, krahost, kraport, critical_failure=False):
        command = "/bin/pki -p '{}' -h '{}' -n '{}' -P https -d '{}' -c '{}' "\
                  "kraconnector-del {} {}".format(
                      caport, cahost, subsystemnick,
                      master['pki_database_path'],
                      token_pwd, krahost, kraport)

        output = subprocess.check_output(command,
                                         stderr=subprocess.STDOUT,
                                         shell=True)

        error = re.findall("ClientResponseFailure:(.*?)", output)
        if error:
            config.pki_log.warning(
                log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2,
                str(krahost),
                str(kraport),
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, output,
                extra=config.PKI_INDENTATION_LEVEL_2)
        if critical_failure == True:
            sys.exit(-1)

    def execute_using_sslget(self, caport, cahost, subsystemnick,
      token_pwd, krahost, kraport):
        urlheader = "https://{}:{}".format(cahost, caport)
        updateURL = "/ca/rest/admin/kraconnector/remove"

        params = "host=" + str(krahost) +\
                 "&port=" + str(kraport)

        command = "/usr/bin/sslget -n '{}' -p '{}' -d '{}' -e '{}' "\
                  "-v -r '{}' {}:{} 2>&1".format(
                      subsystemnick, token_pwd,
                      master['pki_database_path'],
                      params, updateURL,
                      cahost, caport)

        # update KRA connector
        # Execute this "sslget" command
        # Note that sslget will return non-zero value for HTTP code != 200
        # and this will raise an exception
        output = subprocess.check_output(command,
                                         stderr=subprocess.STDOUT,
                                         shell=True)

# PKI Deployment Security Domain Class
class security_domain:
    def deregister(self, install_token, critical_failure=False):
        # process this PKI subsystem instance's 'CS.cfg'
        cs_cfg = PKIConfigParser.read_simple_configuration_file(
            master['pki_target_cs_cfg'])

        # assign key name/value pairs
        machinename = cs_cfg.get('service.machineName')
        sport = cs_cfg.get('service.securityDomainPort')
        ncsport = cs_cfg.get('service.non_clientauth_securePort', '')
        sechost = cs_cfg.get('securitydomain.host')
        httpport = cs_cfg.get('securitydomain.httpport')
        seceeport = cs_cfg.get('securitydomain.httpseeport')
        secagentport = cs_cfg.get('securitydomain.httpsagentport')
        secadminport = cs_cfg.get('securitydomain.httpsadminport')
        secname = cs_cfg.get('securitydomain.name', 'unknown')
        secselect = cs_cfg.get('securitydomain.select')
        adminsport = cs_cfg.get('pkicreate.admin_secure_port', '')
        typeval = cs_cfg.get('cs.type', '')
        agentsport = cs_cfg.get('pkicreate.agent_secure_port', '')

        # NOTE:  Don't check for the existence of 'httpport', as this will
        #        be undefined for a Security Domain that has been migrated!
        if sechost is None or\
           seceeport is None or\
           secagentport is None or\
           secadminport is None:
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(
                log.PKIHELPER_SECURITY_DOMAIN_UNDEFINED,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
            else:
                return

        config.pki_log.info(log.PKIHELPER_SECURITY_DOMAIN_CONTACT_1,
                            secname,
                            extra=config.PKI_INDENTATION_LEVEL_2)
        listval = typeval.lower() + "List"
        urlheader = "https://{}:{}".format(sechost, seceeport)
        urlagentheader = "https://{}:{}".format(sechost, secagentport)
        urladminheader = "https://{}:{}".format(sechost, secadminport)
        updateURL = "/ca/agent/ca/updateDomainXML"

        params = "name=" + "\"" + master['pki_instance_path'] + "\"" +\
                 "&type=" + str(typeval) +\
                 "&list=" + str(listval) +\
                 "&host=" + str(machinename) +\
                 "&sport=" + str(sport) +\
                 "&ncsport=" + str(ncsport) +\
                 "&adminsport=" + str(adminsport) +\
                 "&agentsport=" + str(agentsport) +\
                 "&operation=remove"

        if install_token:
            try:
                # first try install token-based servlet
                params += "&sessionID=" + str(install_token)
                adminUpdateURL = "/ca/admin/ca/updateDomainXML"
                command = "/usr/bin/sslget -p 123456 -d '{}' -e '{}' "\
                          "-v -r '{}' {}:{} 2>&1".format(
                             master['pki_database_path'],
                             params, adminUpdateURL,
                             sechost, secadminport)
                output = subprocess.check_output(command,
                                             stderr=subprocess.STDOUT,
                                             shell=True)
            except subprocess.CalledProcessError as exc:
                config.pki_log.warning(
                    log.PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1,
                    secname,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                output = self.update_domain_using_agent_port(typeval,
                    secname, params, updateURL, sechost, secagentport,
                    critical_failure)
        else:
            output = self.update_domain_using_agent_port(typeval,
                secname, params, updateURL, sechost, secagentport,
                critical_failure)

        if not output:
            if critical_failure == True:
                sys.exit(-1)
            else:
                return

        config.pki_log.debug(log.PKIHELPER_SSLGET_OUTPUT_1,
                             output,
                             extra=config.PKI_INDENTATION_LEVEL_2)
        # Search the output for Status
        status = re.findall("\<Status\>(.*?)\<\/Status\>", output)
        if not status:
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
        elif status[0] != "0":
            error = re.findall("\<Error\>(.*?)\<\/Error\>", output)
            if not error:
                error = ""
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UNREGISTERED_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_3,
                typeval,
                secname,
                error,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
        else:
            config.pki_log.info(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_SUCCESS_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)

    def update_domain_using_agent_port(self, typeval, secname, params,
        updateURL, sechost, secagentport, critical_failure= False):
        token_pwd = None
        cs_cfg = PKIConfigParser.read_simple_configuration_file(
            master['pki_target_cs_cfg'])
        # retrieve subsystem nickname
        subsystemnick_param = typeval.lower() + ".cert.subsystem.nickname"
        subsystemnick = cs_cfg.get(subsystemnick_param)
        if subsystemnick is None:
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(
                log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
            else:
                return

        # retrieve name of token based upon type (hardware/software)
        if ':' in subsystemnick:
            token_name = subsystemnick.split(':')[0]
        else:
            token_name = "internal"

        token_pwd = password.get_password(
                        master['pki_shared_password_conf'],
                        token_name,
                        critical_failure)

        if token_pwd is None or token_pwd == '':
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                sys.exit(-1)
            else:
                return

        command = "/usr/bin/sslget -n '{}' -p '{}' -d '{}' -e '{}' "\
                  "-v -r '{}' {}:{} 2>&1".format(
                      subsystemnick, token_pwd,
                      master['pki_database_path'],
                      params, updateURL,
                      sechost, secagentport)
        try:
            output = subprocess.check_output(command,
                                             stderr=subprocess.STDOUT,
                                             shell=True)
            return output
        except subprocess.CalledProcessError as exc:
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
                typeval,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.warning(
                log.PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1,
                secname,
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise

        return None


    def get_installation_token(self, secuser, secpass, critical_failure=True):
        token = None

        if not secuser or not secpass:
            return None

        # process this PKI subsystem instance's 'CS.cfg'
        cs_cfg = PKIConfigParser.read_simple_configuration_file(
            master['pki_target_cs_cfg'])

        # assign key name/value pairs
        machinename = cs_cfg.get('service.machineName')
        cstype = cs_cfg.get('cs.type', '')
        sechost = cs_cfg.get('securitydomain.host')
        secadminport = cs_cfg.get('securitydomain.httpsadminport')
        secselect = cs_cfg.get('securitydomain.select')

        command = "/bin/pki -p '{}' -h '{}' -P https -u '{}' -w '{}' -d '{}' "\
                  "securitydomain-get-install-token --hostname {} "\
                  "--subsystem {}".format(
                      secadminport, sechost, secuser, secpass,
                      master['pki_database_path'],
                      machinename, cstype)
        try:
            output = subprocess.check_output(command,
                                         stderr=subprocess.STDOUT,
                                         shell=True)

            token_list = re.findall("Install token: \"(.*)\"", output)
            if not token_list:
                config.pki_log.error(
                    log.PKIHELPER_SECURITY_DOMAIN_GET_TOKEN_FAILURE_2,
                    str(sechost),
                    str(secadminport),
                    extra=config.PKI_INDENTATION_LEVEL_2)
                config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, output,
                    extra=config.PKI_INDENTATION_LEVEL_2)
                if critical_failure == True:
                    sys.exit(-1)
            else:
                token = token_list[0]
                return token
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(
                log.PKIHELPER_SECURITY_DOMAIN_GET_TOKEN_FAILURE_2,
                str(sechost),
                str(secadminport),
                extra=config.PKI_INDENTATION_LEVEL_2)
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return None

# PKI Deployment 'systemd' Execution Management Class
class systemd:
    def start(self, critical_failure=True):
        try:
            # Compose this "systemd" execution management command
            if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "start" + " " +\
                          "pki-apached" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "start" + " " +\
                          "pki-tomcatd" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            # Display this "systemd" execution managment command
            config.pki_log.info(
                log.PKIHELPER_SYSTEMD_COMMAND_1, command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            # Execute this "systemd" execution management command
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def stop(self, critical_failure=True):
        try:
            # Compose this "systemd" execution management command
            if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "stop" + " " +\
                          "pki-apached" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "stop" + " " +\
                          "pki-tomcatd" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            # Display this "systemd" execution managment command
            config.pki_log.info(
                log.PKIHELPER_SYSTEMD_COMMAND_1, command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            # Execute this "systemd" execution management command
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return

    def restart(self, critical_failure=True):
        try:
            # Compose this "systemd" execution management command
            if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "restart" + " " +\
                          "pki-apached" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            elif master['pki_subsystem'] in config.PKI_TOMCAT_SUBSYSTEMS:
                command = "systemctl" + " " +\
                          "restart" + " " +\
                          "pki-tomcatd" + "@" +\
                          master['pki_instance_name'] + "." + "service"
            # Display this "systemd" execution managment command
            config.pki_log.info(
                log.PKIHELPER_SYSTEMD_COMMAND_1, command,
                extra=config.PKI_INDENTATION_LEVEL_2)
            # Execute this "systemd" execution management command
            subprocess.call(command, shell=True)
        except subprocess.CalledProcessError as exc:
            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            if critical_failure == True:
                raise
        return


class config_client:

    def configure_pki_data(self, data):
        config.pki_log.info(log.PKI_CONFIG_CONFIGURING_PKI_DATA,
                             extra=config.PKI_INDENTATION_LEVEL_2)

        self.connection = pki.client.PKIConnection(
            protocol='https',
            hostname=master['pki_hostname'],
            port=master['pki_https_port'],
            subsystem=master['pki_subsystem_type'])

        try:
            client = pki.system.SystemConfigClient(self.connection)
            response = client.configure(data)

            config.pki_log.debug(log.PKI_CONFIG_RESPONSE_STATUS +\
                                   " " + str(response['status']),
                                 extra=config.PKI_INDENTATION_LEVEL_2)
            try:
                certs = response['systemCerts']
            except KeyError:
                # no system certs created
                config.pki_log.debug("No new system certificates generated.",
                                      extra=config.PKI_INDENTATION_LEVEL_2)
                certs = []
            if not isinstance(certs, types.ListType):
                certs = [certs]
            for cdata in certs:
                if master['pki_subsystem'] == "CA" and\
                   config.str2bool(master['pki_external']) and\
                   not config.str2bool(master['pki_external_step_two']):
                    # External CA Step 1
                    if cdata['tag'].lower() == "signing":
                        config.pki_log.info(log.PKI_CONFIG_CDATA_REQUEST +\
                            " " + cdata['request'],
                            extra=config.PKI_INDENTATION_LEVEL_2)

                        # Save 'External CA Signing Certificate' CSR (Step 1)
                        config.pki_log.info(log.PKI_CONFIG_EXTERNAL_CSR_SAVE +\
                            " '" + master['pki_external_csr_path'] + "'",
                            extra=config.PKI_INDENTATION_LEVEL_2)
                        directory.create(
                            os.path.dirname(master['pki_external_csr_path']))
                        with open(master['pki_external_csr_path'], "w") as f:
                            f.write(cdata['request'])
                        return
                else:
                    config.pki_log.debug(log.PKI_CONFIG_CDATA_TAG +\
                                           " " + cdata['tag'],
                                         extra=config.PKI_INDENTATION_LEVEL_2)
                    config.pki_log.debug(log.PKI_CONFIG_CDATA_CERT +\
                                           " " + cdata['cert'],
                                         extra=config.PKI_INDENTATION_LEVEL_2)
                    config.pki_log.debug(log.PKI_CONFIG_CDATA_REQUEST +\
                                           " " + cdata['request'],
                                         extra=config.PKI_INDENTATION_LEVEL_2)

            # Cloned PKI subsystems do not return an Admin Certificate
            if not config.str2bool(master['pki_clone']) and \
               not config.str2bool(master['pki_import_admin_cert']):
                admin_cert = response['adminCert']['cert']
                self.process_admin_cert(admin_cert)
                
        except Exception, e:
            if hasattr(e, 'response'):
                root = ET.fromstring(e.response.text)
                if root.tag == 'PKIException':
                    message = root.findall('.//Message')[0].text
                    if message is not None:
                        config.pki_log.error(log.PKI_CONFIG_JAVA_CONFIGURATION_EXCEPTION + " " + message,
                                         extra=config.PKI_INDENTATION_LEVEL_2)
                        raise
            config.pki_log.error(
                log.PKI_CONFIG_JAVA_CONFIGURATION_EXCEPTION + " " + str(e),
                extra=config.PKI_INDENTATION_LEVEL_2)
            raise
        return

    def process_admin_cert(self, admin_cert):
        config.pki_log.debug(log.PKI_CONFIG_RESPONSE_ADMIN_CERT +\
                             " " + admin_cert,
                             extra=config.PKI_INDENTATION_LEVEL_2)

        # Store the Administration Certificate in a file
        admin_cert_file = master['pki_client_admin_cert']
        admin_cert_bin_file = admin_cert_file + ".der"
        config.pki_log.debug(log.PKI_CONFIG_ADMIN_CERT_SAVE +\
                               " '" + admin_cert_file + "'",
                             extra=config.PKI_INDENTATION_LEVEL_2)
        with open(admin_cert_file, "w") as f:
            f.write(admin_cert)

        # convert the cert file to binary
        command = ["AtoB", admin_cert_file, admin_cert_bin_file]
        config.pki_log.info(command,
            extra=config.PKI_INDENTATION_LEVEL_2)
        subprocess.call(command)

        os.chmod(admin_cert_file,
                 config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS)

        os.chmod(admin_cert_bin_file,
                 config.PKI_DEPLOYMENT_DEFAULT_FILE_PERMISSIONS)

        # Import the Administration Certificate
        # into the client NSS security database
        certutil.import_cert(
            re.sub("&#39;", "'", master['pki_admin_nickname']),
            "u,u,u",
            admin_cert_bin_file,
            master['pki_client_password_conf'],
            master['pki_client_database_dir'],
            None,
            True)

        # create directory for p12 file if it does not exist
        directory.create(os.path.dirname(
            master['pki_client_admin_cert_p12']))

        # Export the Administration Certificate from the
        # client NSS security database into a PKCS #12 file
        pk12util.create_file(
            master['pki_client_admin_cert_p12'],
            re.sub("&#39;","'", master['pki_admin_nickname']),
            master['pki_client_pkcs12_password_conf'],
            master['pki_client_password_conf'],
            master['pki_client_database_dir'])

        os.chmod(master['pki_client_admin_cert_p12'],
            config.PKI_DEPLOYMENT_DEFAULT_SECURITY_DATABASE_PERMISSIONS)


    def construct_pki_configuration_data(self):
        config.pki_log.info(log.PKI_CONFIG_CONSTRUCTING_PKI_DATA,
                             extra=config.PKI_INDENTATION_LEVEL_2)

        data = pki.system.ConfigurationRequest()

        # Miscellaneous Configuration Information
        data.pin = master['pki_one_time_pin']
        data.subsystemName = master['pki_subsystem_name']

        # Cloning parameters
        if master['pki_instance_type'] == "Tomcat":
            if config.str2bool(master['pki_clone']):
                self.set_cloning_parameters(data)
            else:
                data.isClone = "false"

        # Hierarchy
        self.set_hierarchy_parameters(data)

        # Security Domain
        if master['pki_subsystem'] != "CA" or\
           config.str2bool(master['pki_clone']) or\
           config.str2bool(master['pki_subordinate']):
            # PKI KRA, PKI OCSP, PKI RA, PKI TKS, PKI TPS,
            # CA Clone, KRA Clone, OCSP Clone, TKS Clone, or
            # Subordinate CA
            self.set_existing_security_domain(data)
        else:
            # PKI CA or External CA
            self.set_new_security_domain(data)

        # database
        if master['pki_subsystem'] != "RA":
            self.set_database_parameters(data)

        # backup
        if master['pki_instance_type'] == "Tomcat":
            self.set_backup_parameters(data)

        # admin user
        if not config.str2bool(master['pki_clone']):
            self.set_admin_parameters(data)

        # Issuing CA Information
        self.set_issuing_ca_parameters(data)

        # Create system certs
        self.set_system_certs(data)

        return data

    def set_system_certs(self, data):
        systemCerts = []

        # Create 'CA Signing Certificate'
        if master['pki_subsystem'] == "CA":
            if not config.str2bool(master['pki_clone']):
                cert1 = self.create_system_cert("ca_signing")
                cert1.signingAlgorithm =\
                    master['pki_ca_signing_signing_algorithm']
                if config.str2bool(master['pki_external_step_two']):
                    # Load the 'External CA Signing Certificate' (Step 2)
                    print(
                        log.PKI_CONFIG_EXTERNAL_CA_LOAD + " " +\
                        "'" + master['pki_external_ca_cert_path'] + "'")
                    with open(master['pki_external_ca_cert_path']) as f:
                        external_cert = f.read()
                    cert1.cert = external_cert

                    # Load the 'External CA Signing Certificate Chain' (Step 2)
                    print(
                        log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD + " " +\
                        "'" + master['pki_external_ca_cert_chain_path'] +\
                        "'")
                    with open(master['pki_external_ca_cert_chain_path']) as f:
                        external_cert_chain = f.read()

                    cert1.certChain = external_cert_chain
                systemCerts.append(cert1)

        # Create 'OCSP Signing Certificate'
        if not config.str2bool(master['pki_clone']):
            if master['pki_subsystem'] == "CA" or\
               master['pki_subsystem'] == "OCSP":
                # External CA, Subordinate CA, PKI CA, or PKI OCSP
                cert2 = self.create_system_cert("ocsp_signing")
                cert2.signingAlgorithm =\
                    master['pki_ocsp_signing_signing_algorithm']
                systemCerts.append(cert2)

        # Create 'SSL Server Certificate'
        # all subsystems

        # create new sslserver cert only if this is a new instance
        cert3 = None
        system_list = instance.tomcat_instance_subsystems()
        if len(system_list) >= 2:
            data.generateServerCert = "false"
            for subsystem in system_list:
                dst = master['pki_instance_path'] + '/conf/' +\
                    subsystem.lower() + '/CS.cfg'
                if subsystem != master['pki_subsystem'] and \
                   os.path.exists(dst):
                    cert3 = self.retrieve_existing_server_cert(dst)
                    break
        else:
            cert3 = self.create_system_cert("ssl_server")
        systemCerts.append(cert3)

        # Create 'Subsystem Certificate'
        if not config.str2bool(master['pki_clone']):
            cert4 = self.create_system_cert("subsystem")
            systemCerts.append(cert4)

        # Create 'Audit Signing Certificate'
        if not config.str2bool(master['pki_clone']):
            if master['pki_subsystem'] != "RA":
                cert5 = self.create_system_cert("audit_signing")
                cert5.signingAlgorithm =\
                    master['pki_audit_signing_signing_algorithm']
                systemCerts.append(cert5)

        # Create DRM Transport and storage Certificates
        if not config.str2bool(master['pki_clone']):
            if master['pki_subsystem'] == "KRA":
                cert6 = self.create_system_cert("transport")
                systemCerts.append(cert6)

                cert7 = self.create_system_cert("storage")
                systemCerts.append(cert7)

        data.systemCerts = systemCerts

    def set_cloning_parameters(self, data):
        data.isClone = "true"
        data.cloneUri = master['pki_clone_uri']
        data.p12File = master['pki_clone_pkcs12_path']
        data.p12Password = master['pki_clone_pkcs12_password']
        data.replicateSchema = master['pki_clone_replicate_schema']
        data.replicationSecurity =\
            master['pki_clone_replication_security']
        if master['pki_clone_replication_master_port']:
            data.masterReplicationPort =\
                master['pki_clone_replication_master_port']
        if master['pki_clone_replication_clone_port']:
            data.cloneReplicationPort =\
                master['pki_clone_replication_clone_port']

    def set_hierarchy_parameters(self, data):
        if master['pki_subsystem'] == "CA":
            if config.str2bool(master['pki_clone']):
                # Cloned CA
                data.hierarchy = "root"
            elif config.str2bool(master['pki_external']):
                # External CA
                data.hierarchy = "join"
            elif config.str2bool(master['pki_subordinate']):
                # Subordinate CA
                data.hierarchy = "join"
            else:
                # PKI CA
                data.hierarchy = "root"

    def set_existing_security_domain(self, data):
        data.securityDomainType = "existingdomain"
        data.securityDomainUri = master['pki_security_domain_uri']
        data.securityDomainUser = master['pki_security_domain_user']
        data.securityDomainPassword = master['pki_security_domain_password']

    def set_new_security_domain(self, data):
        data.securityDomainType = "newdomain"
        data.securityDomainName = master['pki_security_domain_name']

    def set_database_parameters(self, data):
        data.dsHost = master['pki_ds_hostname']
        data.dsPort = master['pki_ds_ldap_port']
        data.baseDN = master['pki_ds_base_dn']
        data.bindDN = master['pki_ds_bind_dn']
        data.database = master['pki_ds_database']
        data.bindpwd = master['pki_ds_password']
        if config.str2bool(master['pki_ds_remove_data']):
            data.removeData = "true"
        else:
            data.removeData = "false"
        if config.str2bool(master['pki_ds_secure_connection']):
            data.secureConn = "true"
        else:
            data.secureConn = "false"

    def set_backup_parameters(self, data):
        if config.str2bool(master['pki_backup_keys']):
            data.backupKeys = "true"
            data.backupFile = master['pki_backup_keys_p12']
            data.backupPassword = master['pki_backup_password']
        else:
            data.backupKeys = "false"

    def set_admin_parameters(self, data):
        data.adminEmail = master['pki_admin_email']
        data.adminName = master['pki_admin_name']
        data.adminPassword = master['pki_admin_password']
        data.adminProfileID = master['pki_admin_profile_id']
        data.adminUID = master['pki_admin_uid']
        data.adminSubjectDN = master['pki_admin_subject_dn']
        if config.str2bool(master['pki_import_admin_cert']):
            data.importAdminCert = "true"
            # read config from file
            with open(master['pki_admin_cert_file']) as f:
                b64 = f.read().replace('\n','')
            data.adminCert = b64
        else:
            data.importAdminCert = "false"
            data.adminSubjectDN = master['pki_admin_subject_dn']
            if master['pki_admin_cert_request_type'] == "pkcs10":
                data.adminCertRequestType = "pkcs10"

                noise_file = os.path.join(
                    master['pki_client_database_dir'], "noise")

                output_file = os.path.join(
                    master['pki_client_database_dir'], "admin_pkcs10.bin")

                file.generate_noise_file(
                    noise_file, int(master['pki_admin_keysize']))

                certutil.generate_certificate_request(
                                     master['pki_admin_subject_dn'],
                                     master['pki_admin_keysize'],
                                     master['pki_client_password_conf'],
                                     noise_file,
                                     output_file,
                                     master['pki_client_database_dir'],
                                     None, None, True)

                # convert output to ascii
                command = ["BtoA", output_file, output_file + ".asc"]
                config.pki_log.info(command,
                                     extra=config.PKI_INDENTATION_LEVEL_2)
                subprocess.call(command)

                with open(output_file + ".asc") as f:
                    b64 = f.read().replace('\n','')

                data.adminCertRequest = b64
            else:
                print "log.PKI_CONFIG_PKCS10_SUPPORT_ONLY"
                sys.exit(1)

    def set_issuing_ca_parameters(self, data):
        if master['pki_subsystem'] != "CA" or\
           config.str2bool(master['pki_clone']) or\
           config.str2bool(master['pki_subordinate']) or\
           config.str2bool(master['pki_external']):
            # PKI KRA, PKI OCSP, PKI RA, PKI TKS, PKI TPS,
            # CA Clone, KRA Clone, OCSP Clone, TKS Clone,
            # Subordinate CA, or External CA
            data.issuingCA = master['pki_issuing_ca']
            if master['pki_subsystem'] == "CA"  and\
               config.str2bool(master['pki_external_step_two']):
                # External CA Step 2
                data.stepTwo = "true";

    def create_system_cert(self, tag):
        cert = pki.system.SystemCertData()
        cert.tag = master["pki_%s_tag" % tag]
        cert.keyAlgorithm = master["pki_%s_key_algorithm" % tag]
        cert.keySize = master["pki_%s_key_size" % tag]
        cert.keyType = master["pki_%s_key_type" % tag]
        cert.nickname = master["pki_%s_nickname" % tag]
        cert.subjectDN = master["pki_%s_subject_dn" % tag]
        cert.token = master["pki_%s_token" % tag]
        return cert

    def retrieve_existing_server_cert(self, cfg_file):
        cs_cfg = PKIConfigParser.read_simple_configuration_file(cfg_file)
        cstype = cs_cfg.get('cs.type').lower()
        cert = pki.system.SystemCertData()
        cert.tag = master["pki_ssl_server_tag"]
        cert.keyAlgorithm = master["pki_ssl_server_key_algorithm"]
        cert.keySize = master["pki_ssl_server_key_size"]
        cert.keyType = master["pki_ssl_server_key_type"]
        cert.nickname = cs_cfg.get(cstype + ".sslserver.nickname")
        cert.cert = cs_cfg.get(cstype + ".sslserver.cert")
        cert.request = cs_cfg.get(cstype + ".sslserver.certreq")
        cert.subjectDN = master["pki_ssl_server_subject_dn"]
        cert.token = cs_cfg.get(cstype + ".sslserver.tokenname")
        return cert

# PKI Deployment Helper Class Instances
identity = identity()
namespace = namespace()
configuration_file = configuration_file()
#xml_file = xml_file()
instance = instance()
directory = directory()
file = file()
symlink = symlink()
war = war()
password = password()
certutil = certutil()
pk12util = pk12util()
security_domain = security_domain()
kra_connector = kra_connector()
systemd = systemd()
