# Copyright (c) 2015 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import six

from kmip.core import enums
from kmip.core import exceptions
from kmip.core import misc
from kmip.core import objects
from kmip.core import primitives
from kmip.core import utils

class QueryRequestPayload(primitives.Struct):
    """
    A request payload for the Query operation.

    The payload contains a list of query functions that the KMIP server should
    respond to. See Section 4.25 of the KMIP 1.1 specification for more
    information.

    Attributes:
        query_functions: A list of QueryFunction enumerations.
    """
    def __init__(self, query_functions=None):
        """
        Construct a QueryRequestPayload object.

        Args:
            query_functions (list): A list of QueryFunction enumerations.
        """
        super(QueryRequestPayload, self).__init__(enums.Tags.REQUEST_PAYLOAD)

        self._query_functions = None

        self.query_functions = query_functions

    @property
    def query_functions(self):
        if self._query_functions:
            return [x.value for x in self._query_functions]
        return None

    @query_functions.setter
    def query_functions(self, value):
        if value is None:
            self._query_functions = None
        elif isinstance(value, list):
            query_functions = []
            for v in value:
                if isinstance(v, enums.QueryFunction):
                    query_functions.append(
                        primitives.Enumeration(
                            enums.QueryFunction,
                            value=v,
                            tag=enums.Tags.QUERY_FUNCTION
                        )
                    )
                else:
                    raise TypeError(
                        "The query functions must be a list of QueryFunction "
                        "enumerations."
                    )
            self._query_functions = query_functions
        else:
            raise TypeError(
                "The query functions must be a list of QueryFunction "
                "enumerations."
            )

    def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Read the data encoding the QueryRequestPayload object and decode it
        into its constituent parts.

        Args:
            input_buffer (Stream): A data stream containing encoded object
                data, supporting a read method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be decoded. Optional,
                defaults to KMIP 1.0.

        Raises:
            InvalidKmipEncoding: Raised if the query functions are missing
                from the encoded payload.
        """
        super(QueryRequestPayload, self).read(
            input_buffer,
            kmip_version=kmip_version
        )
        local_buffer = utils.BytearrayStream(input_buffer.read(self.length))

        query_functions = []
        while(self.is_tag_next(enums.Tags.QUERY_FUNCTION, local_buffer)):
            query_function = primitives.Enumeration(
                enums.QueryFunction,
                tag=enums.Tags.QUERY_FUNCTION
            )
            query_function.read(local_buffer, kmip_version=kmip_version)
            query_functions.append(query_function)

        if query_functions:
            self._query_functions = query_functions
        else:
            raise exceptions.InvalidKmipEncoding(
                "The Query request payload encoding is missing the query "
                "functions."
            )

        self.is_oversized(local_buffer)

    def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Write the data encoding the QueryRequestPayload object to a stream.

        Args:
            output_buffer (Stream): A data stream in which to encode object
                data, supporting a write method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be encoded. Optional,
                defaults to KMIP 1.0.

        Raises:
            InvalidField: Raised if the query functions are not defined.
        """
        local_buffer = utils.BytearrayStream()

        if self._query_functions:
            for query_function in self._query_functions:
                query_function.write(local_buffer, kmip_version=kmip_version)
        else:
            raise exceptions.InvalidField(
                "The Query request payload is missing the query functions "
                "field."
            )

        self.length = local_buffer.length()
        super(QueryRequestPayload, self).write(
            output_buffer,
            kmip_version=kmip_version
        )
        output_buffer.write(local_buffer.buffer)

    def __repr__(self):
        d = "query_functions={}".format(
            "[{}]".format(
                ", ".join(["{}".format(x) for x in self.query_functions])
            ) if self.query_functions else None
        )
        return "QueryRequestPayload({})".format(d)

    def __str__(self):
        d = '"query_functions": {}'.format(
            "[{}]".format(
                ", ".join(["{}".format(x) for x in self.query_functions])
            ) if self.query_functions else None
        )
        return "{" + d + "}"

    def __eq__(self, other):
        if isinstance(other, QueryRequestPayload):
            if self.query_functions == other.query_functions:
                return True
            else:
                return False
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, QueryRequestPayload):
            return not (self == other)
        else:
            return NotImplemented

class QueryResponsePayload(primitives.Struct):
    """
    A response payload for the Query operation.

    The payload contains different sets of responses that the KMIP server
    provides in response to the initial Query request.

    Attributes:
        operations: A list of Operations supported by the server.
        object_types: A list of ObjectTypes supported by the server.
        vendor_identification: A string identifying the server vendor.
        server_information: A structure containing vendor-specific fields and
            substructures.
        application_namespaces: A list of application namespaces supported by
            the server.
        extension_information: A list of ExtensionInformation objects
            detailing Objects supported by the server with ItemTag values in
            the Extensions range. Added in KMIP 1.1.
        attestation_types: A list of AttestationType enumerations detailing
            the attestation methods supported by the server. Added in KMIP 1.2.
        rng_parameters: A list of RNGParameters structures detailing the types
            of random number generators supported by the server. Added in
            KMIP 1.3.
        profile_information: A list of ProfileInformation structures detailing
            the different profiles supported by the server. Added in KMIP 1.3.
        validation_information: A list of ValidationInformation structures
            detailing the types of formal validation supported by the server.
            Added in KMIP 1.3.
        capability_information: A list of CapabilityInformation structures
            detailing the different capabilities supported by the server.
            Added in KMIP 1.3.
        client_registration_methods: A list of ClientRegistrationMethod
            enumerations detailing the different client registration methods
            supported by the server. Added in KMIP 1.3.
        defaults_information: A DefaultsInformation structure detailing the
            default attribute values used by the server for new managed
            objects. Added in KMIP 2.0.
        storage_protection_mask: A list of integers representing combined sets
            of ProtectionStorageMask enumerations detailing the storage
            protections supported by the server. Added in KMIP 2.0.
    """
    def __init__(self,
                 operations=None,
                 object_types=None,
                 vendor_identification=None,
                 server_information=None,
                 application_namespaces=None,
                 extension_information=None,
                 attestation_types=None,
                 rng_parameters=None,
                 profile_information=None,
                 validation_information=None,
                 capability_information=None,
                 client_registration_methods=None,
                 defaults_information=None,
                 storage_protection_masks=None):
        """
        Construct a QueryResponsePayload object.

        Args:
            operations (list): A list of Operations supported by the server.
                Optional, defaults to None.
            object_types (list): A list of ObjectTypes supported by the
                server. Optional, defaults to None.
            vendor_identification (string): A string identifying the server
                vendor. Optional, defaults to None.
            server_information (structure): A ServerInformation structure
                containing vendor-specific fields and substructures. Optional,
                defaults to None.
            application_namespaces (list): A list of application namespaces
                supported by the server. Optional, defaults to None.
            extension_information (list): A list of ExtensionInformation
                objects detailing Objects supported by the server with ItemTag
                values in the Extensions range. Optional, defaults to None.
                Added in KMIP 1.1.
            attestation_types (list): A list of AttestationType enumerations
                detailing the attestation methods supported by the server.
                Optional, defaults to None. Added in KMIP 1.2.
            rng_parameters (list): A list of RNGParameters structures detailing
                the types of random number generators supported by the server.
                Optional, defaults to None. Added in KMIP 1.3.
            profile_information (list): A list of ProfileInformation structures
                detailing the different profiles supported by the server.
                Optional, defaults to None. Added in KMIP 1.3.
            validation_information (list): A list of ValidationInformation
                structures detailing the types of formal validation supported
                by the server. Optional, defaults to None. Added in KMIP 1.3.
            capability_information (list): A list of CapabilityInformation
                structures detailing the different capabilities supported by
                the server. Optional, defaults to None. Added in KMIP 1.3.
            client_registration_methods (list): A list of
                ClientRegistrationMethod enumerations detailing the different
                client registration methods supported by the server. Optional,
                defaults to None. Added in KMIP 1.3.
            defaults_information (structure): A DefaultsInformation structure
                detailing the default attribute values used by the server for
                new managed objects. Optional, defaults to None. Added in
                KMIP 2.0.
            storage_protection_mask (list): A list of integers representing
                combined sets of ProtectionStorageMask enumerations detailing
                the storage protections supported by the server. Optional,
                defaults to None. Added in KMIP 2.0.
        """
        super(QueryResponsePayload, self).__init__(
            enums.Tags.RESPONSE_PAYLOAD
        )

        self._operations = None
        self._object_types = None
        self._vendor_identification = None
        self._server_information = None
        self._application_namespaces = None
        self._extension_information = None
        self._attestation_types = None
        self._rng_parameters = None
        self._profile_information = None
        self._validation_information = None
        self._capability_information = None
        self._client_registration_methods = None
        self._defaults_information = None
        self._storage_protection_masks = None

        self.operations = operations
        self.object_types = object_types
        self.vendor_identification = vendor_identification
        self.server_information = server_information
        self.application_namespaces = application_namespaces
        self.extension_information = extension_information
        self.attestation_types = attestation_types
        self.rng_parameters = rng_parameters
        self.profile_information = profile_information
        self.validation_information = validation_information
        self.capability_information = capability_information
        self.client_registration_methods = client_registration_methods
        self.defaults_information = defaults_information
        self.storage_protection_masks = storage_protection_masks

    @property
    def operations(self):
        if self._operations:
            return [x.value for x in self._operations]
        return None

    @operations.setter
    def operations(self, value):
        if value is None:
            self._operations = None
        elif isinstance(value, list):
            operations = []
            for i in value:
                if isinstance(i, enums.Operation):
                    operations.append(
                        primitives.Enumeration(
                            enums.Operation,
                            value=i,
                            tag=enums.Tags.OPERATION
                        )
                    )
                else:
                    raise TypeError(
                        "The operations must be a list of Operation "
                        "enumerations."
                    )
            self._operations = operations
        else:
            raise TypeError(
                "The operations must be a list of Operation enumerations."
            )

    @property
    def object_types(self):
        if self._object_types:
            return [x.value for x in self._object_types]
        return None

    @object_types.setter
    def object_types(self, value):
        if value is None:
            self._object_types = None
        elif isinstance(value, list):
            object_types = []
            for i in value:
                if isinstance(i, enums.ObjectType):
                    object_types.append(
                        primitives.Enumeration(
                            enums.ObjectType,
                            value=i,
                            tag=enums.Tags.OBJECT_TYPE
                        )
                    )
                else:
                    raise TypeError(
                        "The object types must be a list of ObjectType "
                        "enumerations."
                    )
            self._object_types = object_types
        else:
            raise TypeError(
                "The object types must be a list of ObjectType enumerations."
            )

    @property
    def vendor_identification(self):
        if self._vendor_identification:
            return self._vendor_identification.value
        return None

    @vendor_identification.setter
    def vendor_identification(self, value):
        if value is None:
            self._vendor_identification = None
        elif isinstance(value, six.string_types):
            self._vendor_identification = primitives.TextString(
                value=value,
                tag=enums.Tags.VENDOR_IDENTIFICATION
            )
        else:
            raise TypeError("The vendor identification must be a string.")

    @property
    def server_information(self):
        return self._server_information

    @server_information.setter
    def server_information(self, value):
        if value is None:
            self._server_information = None
        elif isinstance(value, misc.ServerInformation):
            self._server_information = value
        else:
            raise TypeError(
                "The server information must be a ServerInformation structure."
            )

    @property
    def application_namespaces(self):
        if self._application_namespaces:
            return [x.value for x in self._application_namespaces]
        return None

    @application_namespaces.setter
    def application_namespaces(self, value):
        if value is None:
            self._application_namespaces = None
        elif isinstance(value, list):
            application_namespaces = []
            for i in value:
                if isinstance(i, six.string_types):
                    application_namespaces.append(
                        primitives.TextString(
                            value=i,
                            tag=enums.Tags.APPLICATION_NAMESPACE
                        )
                    )
                else:
                    raise TypeError(
                        "The application namespaces must be a list of strings."
                    )
            self._application_namespaces = application_namespaces
        else:
            raise TypeError(
                "The application namespaces must be a list of strings."
            )

    @property
    def extension_information(self):
        if self._extension_information:
            return [x for x in self._extension_information]
        return None

    @extension_information.setter
    def extension_information(self, value):
        if value is None:
            self._extension_information = None
        elif isinstance(value, list):
            extension_information = []
            for i in value:
                if isinstance(i, objects.ExtensionInformation):
                    extension_information.append(i)
                else:
                    raise TypeError(
                        "The extension information must be a list of "
                        "ExtensionInformation structures."
                    )
            self._extension_information = extension_information
        else:
            raise TypeError(
                "The extension information must be a list of "
                "ExtensionInformation structures."
            )

    @property
    def attestation_types(self):
        if self._attestation_types:
            return [x.value for x in self._attestation_types]
        return None

    @attestation_types.setter
    def attestation_types(self, value):
        if value is None:
            self._attestation_types = None
        elif isinstance(value, list):
            attestation_types = []
            for i in value:
                if isinstance(i, enums.AttestationType):
                    attestation_types.append(
                        primitives.Enumeration(
                            enums.AttestationType,
                            value=i,
                            tag=enums.Tags.ATTESTATION_TYPE
                        )
                    )
                else:
                    raise TypeError(
                        "The attestation types must be a list of "
                        "AttestationType enumerations."
                    )
            self._attestation_types = attestation_types
        else:
            raise TypeError(
                "The attestation types must be a list of AttestationType "
                "enumerations."
            )

    @property
    def rng_parameters(self):
        return self._rng_parameters

    @rng_parameters.setter
    def rng_parameters(self, value):
        if value is None:
            self._rng_parameters = None
        elif isinstance(value, list):
            rng_parameters = []
            for i in value:
                if isinstance(i, objects.RNGParameters):
                    rng_parameters.append(i)
                else:
                    raise TypeError(
                        "The RNG parameters must be a list of RNGParameters "
                        "structures."
                    )
            self._rng_parameters = rng_parameters
        else:
            raise TypeError(
                "The RNG parameters must be a list of RNGParameters "
                "structures."
            )

    @property
    def profile_information(self):
        return self._profile_information

    @profile_information.setter
    def profile_information(self, value):
        if value is None:
            self._profile_information = None
        elif isinstance(value, list):
            profile_information = []
            for i in value:
                if isinstance(i, objects.ProfileInformation):
                    profile_information.append(i)
                else:
                    raise TypeError(
                        "The profile information must be a list of "
                        "ProfileInformation structures."
                    )
            self._profile_information = profile_information
        else:
            raise TypeError(
                "The profile information must be a list of "
                "ProfileInformation structures."
            )

    @property
    def validation_information(self):
        return self._validation_information

    @validation_information.setter
    def validation_information(self, value):
        if value is None:
            self._validation_information = None
        elif isinstance(value, list):
            validation_information = []
            for i in value:
                if isinstance(i, objects.ValidationInformation):
                    validation_information.append(i)
                else:
                    raise TypeError(
                        "The validation information must be a list of "
                        "ValidationInformation structures."
                    )
            self._validation_information = validation_information
        else:
            raise TypeError(
                "The validation information must be a list of "
                "ValidationInformation structures."
            )

    @property
    def capability_information(self):
        return self._capability_information

    @capability_information.setter
    def capability_information(self, value):
        if value is None:
            self._capability_information = None
        elif isinstance(value, list):
            capability_information = []
            for i in value:
                if isinstance(i, objects.CapabilityInformation):
                    capability_information.append(i)
                else:
                    raise TypeError(
                        "The capability information must be a list of "
                        "CapabilityInformation structures."
                    )
            self._capability_information = capability_information
        else:
            raise TypeError(
                "The capability information must be a list of "
                "CapabilityInformation structures."
            )

    @property
    def client_registration_methods(self):
        if self._client_registration_methods:
            return [x.value for x in self._client_registration_methods]
        return None

    @client_registration_methods.setter
    def client_registration_methods(self, value):
        if value is None:
            self._client_registration_methods = None
        elif isinstance(value, list):
            client_registration_methods = []
            for i in value:
                if isinstance(i, enums.ClientRegistrationMethod):
                    client_registration_methods.append(
                        primitives.Enumeration(
                            enums.ClientRegistrationMethod,
                            value=i,
                            tag=enums.Tags.CLIENT_REGISTRATION_METHOD
                        )
                    )
                else:
                    raise TypeError(
                        "The client registration methods must be a list of "
                        "ClientRegistrationMethod enumerations."
                    )
            self._client_registration_methods = client_registration_methods
        else:
            raise TypeError(
                "The client registration methods must be a list of "
                "ClientRegistrationMethod enumerations."
            )

    @property
    def defaults_information(self):
        return self._defaults_information

    @defaults_information.setter
    def defaults_information(self, value):
        if value is None:
            self._defaults_information = None
        elif isinstance(value, objects.DefaultsInformation):
            self._defaults_information = value
        else:
            raise TypeError(
                "The defaults information must be a DefaultsInformation "
                "structure."
            )

    @property
    def storage_protection_masks(self):
        if self._storage_protection_masks:
            return [x.value for x in self._storage_protection_masks]
        return None

    @storage_protection_masks.setter
    def storage_protection_masks(self, value):
        if value is None:
            self._storage_protection_masks = None
        elif isinstance(value, list):
            storage_protection_masks = []
            for i in value:
                if isinstance(i, six.integer_types):
                    storage_protection_masks.append(
                        primitives.Integer(
                            value=i,
                            tag=enums.Tags.PROTECTION_STORAGE_MASK
                        )
                    )
                else:
                    raise TypeError(
                        "The storage protection masks must be a list of "
                        "integers."
                    )
            self._storage_protection_masks = storage_protection_masks
        else:
            raise TypeError(
                "The storage protection masks must be a list of integers."
            )

    def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Read the data encoding the QueryResponsePayload object and decode it
        into its constituent parts.

        Args:
            input_buffer (Stream): A data stream containing encoded object
                data, supporting a read method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be decoded. Optional,
                defaults to KMIP 1.0.
        """
        super(QueryResponsePayload, self).read(
            input_buffer,
            kmip_version=kmip_version
        )
        local_buffer = utils.BytearrayStream(input_buffer.read(self.length))

        operations = []
        while(self.is_tag_next(enums.Tags.OPERATION, local_buffer)):
            operation = primitives.Enumeration(
                enums.Operation,
                tag=enums.Tags.OPERATION
            )
            operation.read(local_buffer, kmip_version=kmip_version)
            operations.append(operation)
        self._operations = operations

        object_types = []
        while(self.is_tag_next(enums.Tags.OBJECT_TYPE, local_buffer)):
            object_type = primitives.Enumeration(
                enums.ObjectType,
                tag=enums.Tags.OBJECT_TYPE
            )
            object_type.read(local_buffer, kmip_version=kmip_version)
            object_types.append(object_type)
        self._object_types = object_types

        if self.is_tag_next(enums.Tags.VENDOR_IDENTIFICATION, local_buffer):
            vendor_identification = primitives.TextString(
                tag=enums.Tags.VENDOR_IDENTIFICATION
            )
            vendor_identification.read(
                local_buffer,
                kmip_version=kmip_version
            )
            self._vendor_identification = vendor_identification

        if self.is_tag_next(enums.Tags.SERVER_INFORMATION, local_buffer):
            server_information = misc.ServerInformation()
            server_information.read(
                local_buffer,
                kmip_version=kmip_version
            )
            self._server_information = server_information

        application_namespaces = []
        while(self.is_tag_next(
                enums.Tags.APPLICATION_NAMESPACE,
                local_buffer
            )
        ):
            application_namespace = primitives.TextString(
                tag=enums.Tags.APPLICATION_NAMESPACE
            )
            application_namespace.read(local_buffer, kmip_version=kmip_version)
            application_namespaces.append(application_namespace)
        self._application_namespaces = application_namespaces

        if kmip_version >= enums.KMIPVersion.KMIP_1_1:
            extensions_information = []
            while(self.is_tag_next(
                    enums.Tags.EXTENSION_INFORMATION,
                    local_buffer
                )
            ):
                extension_information = objects.ExtensionInformation()
                extension_information.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                extensions_information.append(extension_information)
            self._extension_information = extensions_information

        if kmip_version >= enums.KMIPVersion.KMIP_1_2:
            attestation_types = []
            while(self.is_tag_next(enums.Tags.ATTESTATION_TYPE, local_buffer)):
                attestation_type = primitives.Enumeration(
                    enums.AttestationType,
                    tag=enums.Tags.ATTESTATION_TYPE
                )
                attestation_type.read(local_buffer, kmip_version=kmip_version)
                attestation_types.append(attestation_type)
            self._attestation_types = attestation_types

        if kmip_version >= enums.KMIPVersion.KMIP_1_3:
            rngs_parameters = []
            while(self.is_tag_next(enums.Tags.RNG_PARAMETERS, local_buffer)):
                rng_parameters = objects.RNGParameters()
                rng_parameters.read(local_buffer, kmip_version=kmip_version)
                rngs_parameters.append(rng_parameters)
            self._rng_parameters = rngs_parameters

            profiles_information = []
            while(self.is_tag_next(
                    enums.Tags.PROFILE_INFORMATION,
                    local_buffer
                )
            ):
                profile_information = objects.ProfileInformation()
                profile_information.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                profiles_information.append(profile_information)
            self._profile_information = profiles_information

            validations_information = []
            while(self.is_tag_next(
                    enums.Tags.VALIDATION_INFORMATION,
                    local_buffer
                )
            ):
                validation_information = objects.ValidationInformation()
                validation_information.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                validations_information.append(validation_information)
            self._validation_information = validations_information

            capabilities_information = []
            while(self.is_tag_next(
                    enums.Tags.CAPABILITY_INFORMATION,
                    local_buffer
                )
            ):
                capability_information = objects.CapabilityInformation()
                capability_information.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                capabilities_information.append(capability_information)
            self._capability_information = capabilities_information

            client_registration_methods = []
            while(self.is_tag_next(
                    enums.Tags.CLIENT_REGISTRATION_METHOD,
                    local_buffer
                )
            ):
                client_registration_method = primitives.Enumeration(
                    enums.ClientRegistrationMethod,
                    tag=enums.Tags.CLIENT_REGISTRATION_METHOD
                )
                client_registration_method.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                client_registration_methods.append(client_registration_method)
            self._client_registration_methods = client_registration_methods

        if kmip_version >= enums.KMIPVersion.KMIP_2_0:
            if self.is_tag_next(enums.Tags.DEFAULTS_INFORMATION, local_buffer):
                defaults_information = objects.DefaultsInformation()
                defaults_information.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                self._defaults_information = defaults_information

            storage_protection_masks = []
            while(self.is_tag_next(
                    enums.Tags.PROTECTION_STORAGE_MASK,
                    local_buffer
                )
            ):
                storage_protection_mask = primitives.Integer(
                    tag=enums.Tags.PROTECTION_STORAGE_MASK
                )
                storage_protection_mask.read(
                    local_buffer,
                    kmip_version=kmip_version
                )
                storage_protection_masks.append(storage_protection_mask)
            self._storage_protection_masks = storage_protection_masks

        self.is_oversized(local_buffer)

    def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Write the data encoding the QueryResponsePayload object to a stream.

        Args:
            output_buffer (Stream): A data stream in which to encode object
                data, supporting a write method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be encoded. Optional,
                defaults to KMIP 1.0.
        """
        local_buffer = utils.BytearrayStream()

        if self._operations:
            for operation in self._operations:
                operation.write(local_buffer, kmip_version=kmip_version)

        if self._object_types:
            for object_type in self._object_types:
                object_type.write(local_buffer, kmip_version=kmip_version)

        if self._vendor_identification:
            self._vendor_identification.write(
                local_buffer,
                kmip_version=kmip_version
            )

        if self._server_information:
            self._server_information.write(
                local_buffer,
                kmip_version=kmip_version
            )

        if self._application_namespaces:
            for application_namespace in self._application_namespaces:
                application_namespace.write(
                    local_buffer,
                    kmip_version=kmip_version
                )

        if kmip_version >= enums.KMIPVersion.KMIP_1_1:
            if self._extension_information:
                for extension_information in self._extension_information:
                    extension_information.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )

        if kmip_version >= enums.KMIPVersion.KMIP_1_2:
            if self._attestation_types:
                for attestation_type in self._attestation_types:
                    attestation_type.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )

        if kmip_version >= enums.KMIPVersion.KMIP_1_3:
            if self._rng_parameters:
                for rng_parameters in self._rng_parameters:
                    rng_parameters.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )
            if self._profile_information:
                for profile_information in self._profile_information:
                    profile_information.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )
            if self._validation_information:
                for validation_information in self._validation_information:
                    validation_information.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )
            if self._capability_information:
                for capability_information in self._capability_information:
                    capability_information.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )
            if self._client_registration_methods:
                for client_reg_method in self._client_registration_methods:
                    client_reg_method.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )

        if kmip_version >= enums.KMIPVersion.KMIP_2_0:
            if self._defaults_information:
                self._defaults_information.write(
                    local_buffer,
                    kmip_version=kmip_version
                )
            if self._storage_protection_masks:
                for storage_protection_mask in self._storage_protection_masks:
                    storage_protection_mask.write(
                        local_buffer,
                        kmip_version=kmip_version
                    )

        self.length = local_buffer.length()
        super(QueryResponsePayload, self).write(
            output_buffer,
            kmip_version=kmip_version
        )
        output_buffer.write(local_buffer.buffer)

    def __repr__(self):
        o = "operations={}".format(
            "[{}]".format(
                ", ".join([str(x) for x in self.operations])
            ) if self.operations else None
        )
        ot = "object_types={}".format(
            "[{}]".format(
                ", ".join([str(x) for x in self.object_types])
            ) if self.object_types else None
        )
        vi = 'vendor_identification="{}"'.format(self.vendor_identification)
        si = "server_information={}".format(repr(self.server_information))
        an = "application_namespaces={}".format(
            "[{}]".format(
                ", ".join(
                    ['"{}"'.format(x) for x in self.application_namespaces]
                )
            ) if self.application_namespaces else None
        )
        ei = "extension_information={}".format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.extension_information])
            ) if self.extension_information else None
        )
        at = "attestation_types={}".format(
            "[{}]".format(
                ", ".join([str(x) for x in self.attestation_types])
            ) if self.attestation_types else None
        )
        rp = "rng_parameters={}".format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.rng_parameters])
            ) if self.rng_parameters else None
        )
        pi = "profile_information={}".format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.profile_information])
            ) if self.profile_information else None
        )
        vai = "validation_information={}".format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.validation_information])
            ) if self.validation_information else None
        )
        ci = "capability_information={}".format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.capability_information])
            ) if self.capability_information else None
        )
        crm = "client_registration_methods={}".format(
            "[{}]".format(
                ", ".join([str(x) for x in self.client_registration_methods])
            ) if self.client_registration_methods else None
        )
        di = "defaults_information={}".format(
            "{}".format(
                repr(self._defaults_information)
            ) if self._defaults_information else None
        )
        spm = "storage_protection_masks={}".format(
            "[{}]".format(
                ", ".join([str(x) for x in self.storage_protection_masks])
            ) if self._storage_protection_masks else None
        )

        v = ", ".join(
            [o, ot, vi, si, an, ei, at, rp, pi, vai, ci, crm, di, spm]
        )

        return "QueryResponsePayload({})".format(v)

    def __str__(self):
        o = '"operations": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.operations])
            ) if self.operations else None
        )
        ot = '"object_types": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.object_types])
            ) if self.object_types else None
        )
        vi = '"vendor_identification": "{}"'.format(self.vendor_identification)
        si = '"server_information": {}'.format(repr(self.server_information))
        an = '"application_namespaces": {}'.format(
            "[{}]".format(
                ", ".join(
                    ['"{}"'.format(x) for x in self.application_namespaces]
                )
            ) if self.application_namespaces else None
        )
        ei = '"extension_information": {}'.format(
            "[{}]".format(
                ", ".join([repr(x) for x in self.extension_information])
            ) if self.extension_information else None
        )
        at = '"attestation_types": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.attestation_types])
            ) if self.attestation_types else None
        )
        rp = '"rng_parameters": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.rng_parameters])
            ) if self.rng_parameters else None
        )
        pi = '"profile_information": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.profile_information])
            ) if self.profile_information else None
        )
        vai = '"validation_information": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.validation_information])
            ) if self.validation_information else None
        )
        ci = '"capability_information": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.capability_information])
            ) if self.capability_information else None
        )
        crm = '"client_registration_methods": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.client_registration_methods])
            ) if self.client_registration_methods else None
        )
        di = '"defaults_information": {}'.format(
            "{}".format(
                str(self.defaults_information)
            ) if self._defaults_information else None
        )
        spm = '"storage_protection_masks": {}'.format(
            "[{}]".format(
                ", ".join([str(x) for x in self.storage_protection_masks])
            ) if self._storage_protection_masks else None
        )

        v = ", ".join(
            [o, ot, vi, si, an, ei, at, rp, pi, vai, ci, crm, di, spm]
        )

        return '{' + v + '}'

    def __eq__(self, other):
        if isinstance(other, QueryResponsePayload):
            if self.operations != other.operations:
                return False
            elif self.object_types != other.object_types:
                return False
            elif self.vendor_identification != other.vendor_identification:
                return False
            elif self.server_information != other.server_information:
                return False
            elif self.application_namespaces != other.application_namespaces:
                return False
            elif self.extension_information != other.extension_information:
                return False
            elif self.attestation_types != other.attestation_types:
                return False
            elif self.rng_parameters != other.rng_parameters:
                return False
            elif self.profile_information != other.profile_information:
                return False
            elif self.validation_information != other.validation_information:
                return False
            elif self.capability_information != other.capability_information:
                return False
            elif self.client_registration_methods != \
                    other.client_registration_methods:
                return False
            elif self.defaults_information != other.defaults_information:
                return False
            elif self.storage_protection_masks != \
                    other.storage_protection_masks:
                return False
            else:
                return True
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, QueryResponsePayload):
            return not (self == other)
        else:
            return NotImplemented


# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import six

from kmip.core import enums
from kmip.core import exceptions
from kmip.core import objects
from kmip.core import primitives
from kmip.core import utils

class GetAttributesRequestPayload(primitives.Struct):
    """
    A request payload for the GetAttributes operation.

    The payload contains the ID of the managed object the attributes should
    belong to, along with a list of attribute names for the attributes that
    should be returned in the response. If the ID is omitted, the server will
    use the ID placeholder by default. If the list of attribute names is
    omitted, all object attributes will be returned. There should be no
    duplicates in the attribute name list.

    Attributes:
        unique_identifier: The unique ID of the managed object with which the
            retrieved attributes should be associated.
        attribute_names: A list of strings identifying the names of the
            attributes associated with the managed object.
    """
    def __init__(self, unique_identifier=None, attribute_names=None):
        """
        Construct a GetAttributes request payload.

        Args:
            unique_identifier (string): The ID of the managed object with
                which the retrieved attributes should be associated. Optional,
                defaults to None.
            attribute_names: A list of strings identifying the names of the
                attributes associated with the managed object. Optional,
                defaults to None.
        """
        super(GetAttributesRequestPayload, self).__init__(
            enums.Tags.REQUEST_PAYLOAD)

        self._unique_identifier = None
        self._attribute_names = list()

        self.unique_identifier = unique_identifier
        self.attribute_names = attribute_names

    @property
    def unique_identifier(self):
        if self._unique_identifier:
            return self._unique_identifier.value
        else:
            return self._unique_identifier

    @unique_identifier.setter
    def unique_identifier(self, value):
        if value is None:
            self._unique_identifier = None
        elif isinstance(value, six.string_types):
            self._unique_identifier = primitives.TextString(
                value=value,
                tag=enums.Tags.UNIQUE_IDENTIFIER
            )
        else:
            raise TypeError("Unique identifier must be a string.")

    @property
    def attribute_names(self):
        if self._attribute_names:
            names = list()
            for attribute_name in self._attribute_names:
                names.append(attribute_name.value)
            return names
        else:
            return self._attribute_names

    @attribute_names.setter
    def attribute_names(self, value):
        if value is None:
            self._attribute_names = list()
        elif isinstance(value, list):
            names = list()
            for i in range(len(value)):
                name = value[i]
                if not isinstance(name, six.string_types):
                    raise TypeError(
                        "Attribute names must be a list of strings; "
                        "item {0} has type {1}.".format(i + 1, type(name))
                    )
                if name not in names:
                    names.append(name)
            self._attribute_names = list()
            for name in names:
                self._attribute_names.append(
                    primitives.TextString(
                        value=name,
                        tag=enums.Tags.ATTRIBUTE_NAME
                    )
                )
        else:
            raise TypeError("Attribute names must be a list of strings.")

    def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Read the data encoding the GetAttributes request payload and decode
        it into its constituent parts.

        Args:
            input_buffer (stream): A data stream containing encoded object
                data, supporting a read method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be decoded. Optional,
                defaults to KMIP 1.0.

        Raises:
            InvalidKmipEncoding: Raised if an invalid type is found for the
                AttributeReference encoding for KMIP 2.0+ encodings.
        """
        super(GetAttributesRequestPayload, self).read(
            input_buffer,
            kmip_version=kmip_version
        )
        local_buffer = utils.BytearrayStream(input_buffer.read(self.length))

        if self.is_tag_next(enums.Tags.UNIQUE_IDENTIFIER, local_buffer):
            self._unique_identifier = primitives.TextString(
                tag=enums.Tags.UNIQUE_IDENTIFIER
            )
            self._unique_identifier.read(
                local_buffer,
                kmip_version=kmip_version
            )
        else:
            self._unique_identifier = None

        names = list()
        if kmip_version < enums.KMIPVersion.KMIP_2_0:
            while self.is_tag_next(enums.Tags.ATTRIBUTE_NAME, local_buffer):
                name = primitives.TextString(tag=enums.Tags.ATTRIBUTE_NAME)
                name.read(local_buffer, kmip_version=kmip_version)
                names.append(name)
            self._attribute_names = names
        else:
            while self.is_tag_next(
                    enums.Tags.ATTRIBUTE_REFERENCE,
                    local_buffer
            ):
                if self.is_type_next(enums.Types.STRUCTURE, local_buffer):
                    reference = objects.AttributeReference()
                    reference.read(local_buffer, kmip_version=kmip_version)
                    names.append(
                        primitives.TextString(
                            value=reference.attribute_name,
                            tag=enums.Tags.ATTRIBUTE_NAME
                        )
                    )
                elif self.is_type_next(enums.Types.ENUMERATION, local_buffer):
                    reference = primitives.Enumeration(
                        enums.Tags,
                        tag=enums.Tags.ATTRIBUTE_REFERENCE
                    )
                    reference.read(local_buffer, kmip_version=kmip_version)
                    name = enums.convert_attribute_tag_to_name(reference.value)
                    names.append(
                        primitives.TextString(
                            value=name,
                            tag=enums.Tags.ATTRIBUTE_NAME
                        )
                    )
                else:
                    raise exceptions.InvalidKmipEncoding(
                        "The GetAttributes request payload encoding contains "
                        "an invalid AttributeReference type."
                    )
            self._attribute_names = names

        self.is_oversized(local_buffer)

    def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Write the data encoding the GetAttributes request payload to a
        stream.

        Args:
            output_buffer (stream): A data stream in which to encode object
                data, supporting a write method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be encoded. Optional,
                defaults to KMIP 1.0.
        """
        local_buffer = utils.BytearrayStream()

        if self._unique_identifier:
            self._unique_identifier.write(
                local_buffer,
                kmip_version=kmip_version
            )

        if kmip_version < enums.KMIPVersion.KMIP_2_0:
            for attribute_name in self._attribute_names:
                attribute_name.write(local_buffer, kmip_version=kmip_version)
        else:
            # NOTE (ph) This approach simplifies backwards compatible issues
            #           but limits easy support for using AttributeReference
            #           structures going forward, specifically limiting the
            #           use of VendorIdentification for custom attributes.
            #           If custom attributes need to be retrieved using
            #           the GetAttributes operation for KMIP 2.0 applications
            #           this code will need to change.
            for attribute_name in self._attribute_names:
                t = enums.convert_attribute_name_to_tag(attribute_name.value)
                e = primitives.Enumeration(
                    enums.Tags,
                    value=t,
                    tag=enums.Tags.ATTRIBUTE_REFERENCE
                )
                e.write(local_buffer, kmip_version=kmip_version)

        self.length = local_buffer.length()
        super(GetAttributesRequestPayload, self).write(
            output_buffer,
            kmip_version=kmip_version
        )
        output_buffer.write(local_buffer.buffer)

    def __repr__(self):
        unique_identifier = "unique_identifier={0}".format(
            self.unique_identifier
        )
        attribute_names = "attribute_names={0}".format(self.attribute_names)
        return "GetAttributesRequestPayload({0}, {1})".format(
            unique_identifier,
            attribute_names
        )

    def __str__(self):
        return str({
            'unique_identifier': self.unique_identifier,
            'attribute_names': self.attribute_names
        })

    def __eq__(self, other):
        if isinstance(other, GetAttributesRequestPayload):
            if self.unique_identifier == other.unique_identifier:
                if set(self.attribute_names) == set(other.attribute_names):
                    return True
                else:
                    return False
            else:
                return False
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, GetAttributesRequestPayload):
            return not self.__eq__(other)
        else:
            return NotImplemented

class GetAttributesResponsePayload(primitives.Struct):
    """
    A response payload for the GetAttributes operation.

    The payload will contain the ID of the managed object with which the
    attributes are associated. It will also contain a list of attributes
    associated with the aforementioned managed object.

    Attributes:
        unique_identifier: The unique ID of the managed object with which
            the retrieved attributes should be associated.
        attributes: The list of attributes associated with managed object
            identified by the unique identifier above.
    """
    def __init__(self, unique_identifier=None, attributes=None):
        """
        Construct a GetAttributes response payload.

        Args:
            unique_identifier (string): The ID of the managed object with
                which the retrieved attributes should be associated. Optional,
                defaults to None.
            attributes (list): A list of attribute structures associated with
                the managed object. Optional, defaults to None.
        """
        super(GetAttributesResponsePayload, self).__init__(
            enums.Tags.RESPONSE_PAYLOAD)

        self._unique_identifier = None
        self._attributes = list()

        self.unique_identifier = unique_identifier
        self.attributes = attributes

    @property
    def unique_identifier(self):
        if self._unique_identifier:
            return self._unique_identifier.value
        else:
            return self._unique_identifier

    @unique_identifier.setter
    def unique_identifier(self, value):
        if value is None:
            self._unique_identifier = None
        elif isinstance(value, six.string_types):
            self._unique_identifier = primitives.TextString(
                value=value,
                tag=enums.Tags.UNIQUE_IDENTIFIER
            )
        else:
            raise TypeError("Unique identifier must be a string.")

    @property
    def attributes(self):
        return self._attributes

    @attributes.setter
    def attributes(self, value):
        if value is None:
            self._attributes = list()
        elif isinstance(value, list):
            for i in range(len(value)):
                attribute = value[i]
                if not isinstance(attribute, objects.Attribute):
                    raise TypeError(
                        "Attributes must be a list of attribute objects; "
                        "item {0} has type {1}.".format(i + 1, type(attribute))
                    )
            self._attributes = value
        else:
            raise TypeError("Attributes must be a list of attribute objects.")

    def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Read the data encoding the GetAttributes response payload and decode
        it into its constituent parts.

        Args:
            input_buffer (stream): A data stream containing encoded object
                data, supporting a read method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be decoded. Optional,
                defaults to KMIP 1.0.
        """
        super(GetAttributesResponsePayload, self).read(
            input_buffer,
            kmip_version=kmip_version
        )
        local_buffer = utils.BytearrayStream(input_buffer.read(self.length))

        if self.is_tag_next(enums.Tags.UNIQUE_IDENTIFIER, local_buffer):
            unique_identifier = primitives.TextString(
                tag=enums.Tags.UNIQUE_IDENTIFIER
            )
            unique_identifier.read(local_buffer, kmip_version=kmip_version)
            self.unique_identifier = unique_identifier.value
        else:
            raise exceptions.InvalidKmipEncoding(
                "The GetAttributes response payload encoding is missing the "
                "unique identifier."
            )

        if kmip_version < enums.KMIPVersion.KMIP_2_0:
            self._attributes = list()
            while self.is_tag_next(enums.Tags.ATTRIBUTE, local_buffer):
                attribute = objects.Attribute()
                attribute.read(local_buffer, kmip_version=kmip_version)
                self._attributes.append(attribute)
        else:
            if self.is_tag_next(enums.Tags.ATTRIBUTES, local_buffer):
                attributes = objects.Attributes()
                attributes.read(local_buffer, kmip_version=kmip_version)
                # TODO (ph) Add a new utility to avoid using TemplateAttributes
                temp_attr = objects.convert_attributes_to_template_attribute(
                    attributes
                )
                self._attributes = temp_attr.attributes
            else:
                raise exceptions.InvalidKmipEncoding(
                    "The GetAttributes response payload encoding is missing "
                    "the attributes structure."
                )

        self.is_oversized(local_buffer)

    def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
        """
        Write the data encoding the GetAttributes response payload to a
        stream.

        Args:
            output_buffer (stream): A data stream in which to encode object
                data, supporting a write method; usually a BytearrayStream
                object.
            kmip_version (KMIPVersion): An enumeration defining the KMIP
                version with which the object will be encoded. Optional,
                defaults to KMIP 1.0.
        """
        local_buffer = utils.BytearrayStream()

        if self._unique_identifier:
            self._unique_identifier.write(
                local_buffer,
                kmip_version=kmip_version
            )
        else:
            raise exceptions.InvalidField(
                "The GetAttributes response payload is missing the unique "
                "identifier field."
            )

        if kmip_version < enums.KMIPVersion.KMIP_2_0:
            for attribute in self._attributes:
                attribute.write(local_buffer, kmip_version=kmip_version)
        else:
            if self._attributes:
                # TODO (ph) Add a new utility to avoid using TemplateAttributes
                template_attribute = objects.TemplateAttribute(
                    attributes=self.attributes
                )
                attributes = objects.convert_template_attribute_to_attributes(
                    template_attribute
                )
                attributes.write(local_buffer, kmip_version=kmip_version)
            else:
                raise exceptions.InvalidField(
                    "The GetAttributes response payload is missing the "
                    "attributes list."
                )

        self.length = local_buffer.length()
        super(GetAttributesResponsePayload, self).write(
            output_buffer,
            kmip_version=kmip_version
        )
        output_buffer.write(local_buffer.buffer)

    def __repr__(self):
        unique_identifier = "unique_identifier={0}".format(
            self.unique_identifier
        )
        names = "attributes={0}".format(self.attributes)
        return "GetAttributesResponsePayload({0}, {1})".format(
            unique_identifier,
            names
        )

    def __str__(self):
        return str({
            'unique_identifier': self.unique_identifier,
            'attributes': self.attributes
        })

    def __eq__(self, other):
        if isinstance(other, GetAttributesResponsePayload):
            if self.unique_identifier != other.unique_identifier:
                return False
            if len(self._attributes) != len(other._attributes):
                return False
            for i in range(len(self._attributes)):
                a = self._attributes[i]
                b = other._attributes[i]
                if a != b:
                    return False
            return True
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, GetAttributesResponsePayload):
            return not self.__eq__(other)
        else:
            return NotImplemented


"""Discover and load entry points from installed packages."""
# Copyright (c) Thomas Kluyver and contributors
# Distributed under the terms of the MIT license; see LICENSE file.

from contextlib import contextmanager
import glob
from importlib import import_module
import io
import itertools
import os.path as osp
import re
import sys
import warnings
import zipfile

if sys.version_info[0] >= 3:
    import configparser
else:
    from backports import configparser

entry_point_pattern = re.compile(r"""
(?P<modulename>\w+(\.\w+)*)
(:(?P<objectname>\w+(\.\w+)*))?
\s*
(\[(?P<extras>.+)\])?
$
""", re.VERBOSE)

file_in_zip_pattern = re.compile(r"""
(?P<dist_version>[^/\\]+)\.(dist|egg)-info
[/\\]entry_points.txt$
""", re.VERBOSE)

__version__ = '0.3'

class BadEntryPoint(Exception):
    """Raised when an entry point can't be parsed.
    """
    def __init__(self, epstr):
        self.epstr = epstr

    def __str__(self):
        return "Couldn't parse entry point spec: %r" % self.epstr

    @staticmethod
    @contextmanager
    def err_to_warnings():
        try:
            yield
        except BadEntryPoint as e:
            warnings.warn(str(e))

class NoSuchEntryPoint(Exception):
    """Raised by :func:`get_single` when no matching entry point is found."""
    def __init__(self, group, name):
        self.group = group
        self.name = name

    def __str__(self):
        return "No {!r} entry point found in group {!r}".format(self.name, self.group)

class CaseSensitiveConfigParser(configparser.ConfigParser):
    optionxform = staticmethod(str)

class EntryPoint(object):
    def __init__(self, name, module_name, object_name, extras=None, distro=None):
        self.name = name
        self.module_name = module_name
        self.object_name = object_name
        self.extras = extras
        self.distro = distro

    def __repr__(self):
        return "EntryPoint(%r, %r, %r, %r)" % \
            (self.name, self.module_name, self.object_name, self.distro)

    def load(self):
        """Load the object to which this entry point refers.
        """
        mod = import_module(self.module_name)
        obj = mod
        if self.object_name:
            for attr in self.object_name.split('.'):
                obj = getattr(obj, attr)
        return obj

    @classmethod
    def from_string(cls, epstr, name, distro=None):
        """Parse an entry point from the syntax in entry_points.txt

        :param str epstr: The entry point string (not including 'name =')
        :param str name: The name of this entry point
        :param Distribution distro: The distribution in which the entry point was found
        :rtype: EntryPoint
        :raises BadEntryPoint: if *epstr* can't be parsed as an entry point.
        """
        m = entry_point_pattern.match(epstr)
        if m:
            mod, obj, extras = m.group('modulename', 'objectname', 'extras')
            if extras is not None:
                extras = re.split(r',\s*', extras)
            return cls(name, mod, obj, extras, distro)
        else:
            raise BadEntryPoint(epstr)

class Distribution(object):
    def __init__(self, name, version):
        self.name = name
        self.version = version

    def __repr__(self):
        return "Distribution(%r, %r)" % (self.name, self.version)

def iter_files_distros(path=None, repeated_distro='first'):
    if path is None:
        path = sys.path

    # Distributions found earlier in path will shadow those with the same name
    # found later. If these distributions used different module names, it may
    # actually be possible to import both, but in most cases this shadowing
    # will be correct.
    distro_names_seen = set()

    for folder in path:
        if folder.rstrip('/\\').endswith('.egg'):
            # Gah, eggs
            egg_name = osp.basename(folder)
            if '-' in egg_name:
                distro = Distribution(*egg_name.split('-')[:2])

                if (repeated_distro == 'first') \
                        and (distro.name in distro_names_seen):
                    continue
                distro_names_seen.add(distro.name)
            else:
                distro = None

            if osp.isdir(folder):
                ep_path = osp.join(folder, 'EGG-INFO', 'entry_points.txt')
                if osp.isfile(ep_path):
                    cp = CaseSensitiveConfigParser(delimiters=('=',))
                    cp.read([ep_path])
                    yield cp, distro

            elif zipfile.is_zipfile(folder):
                z = zipfile.ZipFile(folder)
                try:
                    info = z.getinfo('EGG-INFO/entry_points.txt')
                except KeyError:
                    continue
                cp = CaseSensitiveConfigParser(delimiters=('=',))
                with z.open(info) as f:
                    fu = io.TextIOWrapper(f)
                    cp.read_file(fu,
                        source=osp.join(folder, 'EGG-INFO', 'entry_points.txt'))
                yield cp, distro

        # zip imports, not egg
        elif zipfile.is_zipfile(folder):
            with zipfile.ZipFile(folder) as zf:
                for info in zf.infolist():
                    m = file_in_zip_pattern.match(info.filename)
                    if not m:
                        continue

                    distro_name_version = m.group('dist_version')
                    if '-' in distro_name_version:
                        distro = Distribution(*distro_name_version.split('-', 1))

                        if (repeated_distro == 'first') \
                                and (distro.name in distro_names_seen):
                            continue
                        distro_names_seen.add(distro.name)
                    else:
                        distro = None

                    cp = CaseSensitiveConfigParser(delimiters=('=',))
                    with zf.open(info) as f:
                        fu = io.TextIOWrapper(f)
                        cp.read_file(fu, source=osp.join(folder, info.filename))
                    yield cp, distro

        # Regular file imports (not egg, not zip file)
        for path in itertools.chain(
            glob.iglob(osp.join(folder, '*.dist-info', 'entry_points.txt')),
            glob.iglob(osp.join(folder, '*.egg-info', 'entry_points.txt'))
        ):
            distro_name_version = osp.splitext(osp.basename(osp.dirname(path)))[0]
            if '-' in distro_name_version:
                distro = Distribution(*distro_name_version.split('-', 1))

                if (repeated_distro == 'first') \
                        and (distro.name in distro_names_seen):
                    continue
                distro_names_seen.add(distro.name)
            else:
                distro = None
            cp = CaseSensitiveConfigParser(delimiters=('=',))
            cp.read([path])
            yield cp, distro

def get_single(group, name, path=None):
    """Find a single entry point.

    Returns an :class:`EntryPoint` object, or raises :exc:`NoSuchEntryPoint`
    if no match is found.
    """
    for config, distro in iter_files_distros(path=path):
        if (group in config) and (name in config[group]):
            epstr = config[group][name]
            with BadEntryPoint.err_to_warnings():
                return EntryPoint.from_string(epstr, name, distro)

    raise NoSuchEntryPoint(group, name)

def get_group_named(group, path=None):
    """Find a group of entry points with unique names.

    Returns a dictionary of names to :class:`EntryPoint` objects.
    """
    result = {}
    for ep in get_group_all(group, path=path):
        if ep.name not in result:
            result[ep.name] = ep
    return result

def get_group_all(group, path=None):
    """Find all entry points in a group.

    Returns a list of :class:`EntryPoint` objects.
    """
    result = []
    for config, distro in iter_files_distros(path=path):
        if group in config:
            for name, epstr in config[group].items():
                with BadEntryPoint.err_to_warnings():
                    result.append(EntryPoint.from_string(epstr, name, distro))

    return result

if __name__ == '__main__':
    import pprint
    pprint.pprint(get_group_all('console_scripts'))


# coding: utf-8
import os
import glob2
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand
from application import create_app
from application.models import db

# Used by app debug & livereload
PORT = 5000

app = create_app()
manager = Manager(app)

# db migrate commands
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)

@manager.command
def run():
    """Run app."""
    app.run(port=PORT)

@manager.command
def live():
    """Run livereload server"""
    from livereload import Server

    server = Server(app)

    map(server.watch, glob2.glob("application/pages/**/*.*"))  # pages
    map(server.watch, glob2.glob("application/macros/**/*.html"))  # macros
    map(server.watch, glob2.glob("application/static/**/*.*"))  # public assets

    server.serve(port=PORT)

@manager.command
def build():
    """Use FIS to compile assets."""
    os.system('gulp')
    os.chdir('application')
    os.system('fis release -d ../output -opmD')

if __name__ == "__main__":
    manager.run()


#!/usr/bin/env python
# coding: utf-8

"""
Flask Boost

Usage:
  boost new <project>
  boost new controller <controller>
  boost new action <controller> <action> [-t]
  boost new form <form>
  boost new model <model>
  boost new macro <macro>
  boost new macro <category> <macro>
  boost -v | --version
  boost -h | --help

Options:
  -h, --help          Help information.
  -v, --version       Show version.
"""

import sys
import os

# Insert project root path to sys.path
project_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_path not in sys.path:
    sys.path.insert(0, project_path)

import logging
import io
from logging import StreamHandler, DEBUG
from os.path import dirname, abspath
from tempfile import mkstemp
from docopt import docopt
import shutil
import errno
from flask_boost import __version__

# If you add #{project} in a file, add the file ext here
REWRITE_FILE_EXTS = ('.html', '.conf', '.py', '.json', '.md')

logger = logging.getLogger(__name__)
logger.setLevel(DEBUG)
logger.addHandler(StreamHandler())

def generate_project(args):
    """New project."""
    # Project templates path
    src = os.path.join(dirname(abspath(__file__)), 'project')

    project_name = args.get('<project>')

    if not project_name:
        logger.warning('Project name cannot be empty.')
        return

    # Destination project path
    dst = os.path.join(os.getcwd(), project_name)

    if os.path.isdir(dst):
        logger.warning('Project directory already exists.')
        return

    logger.info('Start generating project files.')

    _mkdir_p(dst)

    for src_dir, sub_dirs, filenames in os.walk(src):
        # Build and create destination directory path
        relative_path = src_dir.split(src)[1].lstrip(os.path.sep)
        dst_dir = os.path.join(dst, relative_path)

        if src != src_dir:
            _mkdir_p(dst_dir)

        # Copy, rewrite and move project files
        for filename in filenames:
            if filename in ['development.py', 'production.py']:
                continue

            src_file = os.path.join(src_dir, filename)
            dst_file = os.path.join(dst_dir, filename)

            if filename.endswith(REWRITE_FILE_EXTS):
                _rewrite_and_copy(src_file, dst_file, project_name)
            else:
                shutil.copy(src_file, dst_file)
            logger.info("New: %s" % dst_file)

            if filename in ['development_sample.py', 'production_sample.py']:
                dst_file = os.path.join(dst_dir, "%s.py" % filename.split('_')[0])
                _rewrite_and_copy(src_file, dst_file, project_name)
                logger.info("New: %s" % dst_file)

    logger.info('Finish generating project files.')

def generate_controller(args):
    """Generate controller, include the controller file, template & css & js directories."""
    controller_template = os.path.join(dirname(abspath(__file__)), 'templates/controller.py')
    test_template = os.path.join(dirname(abspath(__file__)), 'templates/unittest.py')
    controller_name = args.get('<controller>')
    current_path = os.getcwd()

    logger.info('Start generating controller.')

    if not controller_name:
        logger.warning('Controller name cannot be empty.')
        return

    # controller file
    with open(controller_template, 'r') as template_file:
        controller_file_path = os.path.join(current_path, 'application/controllers',
                                            controller_name + '.py')
        with open(controller_file_path, 'w+') as controller_file:
            for line in template_file:
                new_line = line.replace('#{controller}', controller_name)
                controller_file.write(new_line)
    logger.info("New: %s" % _relative_path(controller_file_path))

    # test file
    with open(test_template, 'r') as template_file:
        test_file_path = os.path.join(current_path, 'tests',
                                      'test_%s.py' % controller_name)
        with open(test_file_path, 'w+') as test_file:
            for line in template_file:
                new_line = line.replace('#{controller}', controller_name) \
                    .replace('#{controller|title}', controller_name.title())
                test_file.write(new_line)
    logger.info("New: %s" % _relative_path(test_file_path))

    # assets dir
    assets_dir_path = os.path.join(current_path, 'application/pages/%s' % controller_name)
    _mkdir_p(assets_dir_path)

    # form file
    _generate_form(controller_name)

    logger.info('Finish generating controller.')

def generate_action(args):
    """Generate action."""
    controller = args.get('<controller>')
    action = args.get('<action>')
    with_template = args.get('-t')
    current_path = os.getcwd()

    logger.info('Start generating action.')

    controller_file_path = os.path.join(current_path, 'application/controllers', controller + '.py')
    if not os.path.exists(controller_file_path):
        logger.warning("The controller %s does't exist." % controller)
        return

    if with_template:
        action_source_path = os.path.join(dirname(abspath(__file__)), 'templates/action.py')
    else:
        action_source_path = os.path.join(dirname(abspath(__file__)), 'templates/action_without_template.py')

    # Add action source codes
    with open(action_source_path, 'r') as action_source_file:
        with open(controller_file_path, 'a') as controller_file:
            for action_line in action_source_file:
                new_line = action_line.replace('#{controller}', controller). \
                    replace('#{action}', action)
                controller_file.write(new_line)
    logger.info("Updated: %s" % _relative_path(controller_file_path))

    if with_template:
        # assets dir
        assets_dir_path = os.path.join(current_path, 'application/pages/%s/%s' % (controller, action))
        _mkdir_p(assets_dir_path)

        # html
        action_html_template_path = os.path.join(dirname(abspath(__file__)), 'templates/action.html')
        action_html_path = os.path.join(assets_dir_path, '%s.html' % action)
        with open(action_html_template_path, 'r') as action_html_template_file:
            with open(action_html_path, 'w') as action_html_file:
                for line in action_html_template_file:
                    new_line = line.replace('#{action}', action) \
                        .replace('#{action|title}', action.title()) \
                        .replace('#{controller}', controller)
                    action_html_file.write(new_line)
        logger.info("New: %s" % _relative_path(action_html_path))

        # js
        action_js_template_path = os.path.join(dirname(abspath(__file__)), 'templates/action.js')
        action_js_path = os.path.join(assets_dir_path, '%s.js' % action)
        shutil.copy(action_js_template_path, action_js_path)
        logger.info("New: %s" % _relative_path(action_js_path))

        # less
        action_less_template_path = os.path.join(dirname(abspath(__file__)), 'templates/action.less')
        action_less_path = os.path.join(assets_dir_path, '%s.less' % action)
        shutil.copy(action_less_template_path, action_less_path)
        logger.info("New: %s" % _relative_path(action_less_path))

    logger.info('Finish generating action.')

def generate_form(args):
    """Generate form."""
    form_name = args.get('<form>')
    logger.info('Start generating form.')
    _generate_form(form_name)
    logger.info('Finish generating form.')

def generate_model(args):
    """Generate model."""
    model_name = args.get('<model>')
    if not model_name:
        logger.warning('Model name cannot be empty.')
        return

    logger.info('Start generating model.')

    model_template = os.path.join(dirname(abspath(__file__)), 'templates/model.py')
    current_path = os.getcwd()

    with open(model_template, 'r') as template_file:
        model_file_path = os.path.join(current_path, 'application/models',
                                       model_name + '.py')
        with open(model_file_path, 'w+') as model_file:
            for line in template_file:
                new_line = line.replace('#{model|title}', model_name.title())
                model_file.write(new_line)
    logger.info("New: %s" % _relative_path(model_file_path))

    with open(os.path.join(current_path, 'application/models/__init__.py'), 'a') as package_file:
        package_file.write('\nfrom .%s import *' % model_name)

    logger.info('Finish generating model.')

def generate_macro(args):
    """Genarate macro."""
    macro = args.get('<macro>').replace('-', '_')
    category = args.get('<category>')

    if not macro:
        logger.warning('Macro name cannot be empty.')
        return

    logger.info('Start generating macro.')

    current_path = os.getcwd()

    if category:
        macro_root_path = os.path.join(current_path, 'application/macros', category, macro)
    else:
        macro_root_path = os.path.join(current_path, 'application/macros', macro)

    _mkdir_p(macro_root_path)

    macro_html_path = os.path.join(macro_root_path, '_%s.html' % macro)
    macro_css_path = os.path.join(macro_root_path, '_%s.less' % macro)
    macro_js_path = os.path.join(macro_root_path, '_%s.js' % macro)

    # html
    macro_html_template_path = os.path.join(dirname(abspath(__file__)), 'templates/macro.html')
    with open(macro_html_template_path, 'r') as template_file:
        with open(macro_html_path, 'w+') as html_file:
            for line in template_file:
                new_line = line.replace('#{macro}', macro)
                html_file.write(new_line)
    logger.info("New: %s" % _relative_path(macro_html_path))

    # css
    open(macro_css_path, 'a').close()
    logger.info("New: %s" % _relative_path(macro_css_path))

    # js
    open(macro_js_path, 'a').close()
    logger.info("New: %s" % _relative_path(macro_js_path))

    logger.info('Finish generating macro.')

def main():
    args = docopt(__doc__, version="Flask-Boost {0}".format(__version__))
    if args.get('new'):
        if args.get('controller'):
            generate_controller(args)
        elif args.get('form'):
            generate_form(args)
        elif args.get('model'):
            generate_model(args)
        elif args.get('action'):
            generate_action(args)
        elif args.get('macro'):
            generate_macro(args)
        else:
            generate_project(args)
    else:
        print(args)

def _mkdir_p(path):
    """mkdir -p path"""
    try:
        os.makedirs(path)
    except OSError as exc:
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise
    else:
        logger.info("New: %s%s", path, os.path.sep)

def _rewrite_and_copy(src_file, dst_file, project_name):
    """Replace vars and copy."""
    # Create temp file
    fh, abs_path = mkstemp()

    with io.open(abs_path, 'w', encoding='utf-8') as new_file:
        with io.open(src_file, 'r', encoding='utf-8') as old_file:
            for line in old_file:
                new_line = line.replace('#{project}', project_name). \
                    replace('#{project|title}', project_name.title())
                new_file.write(new_line)

    # Copy to new file
    shutil.copy(abs_path, dst_file)
    os.close(fh)

def _generate_form(form_name):
    form_template = os.path.join(dirname(abspath(__file__)), 'templates/form.py')
    current_path = os.getcwd()

    if not form_name:
        logger.warning('Form name cannot be empty.')
        return

    form_file_path = os.path.join(current_path, 'application/forms', form_name + '.py')
    shutil.copy(form_template, form_file_path)
    logger.info("New: %s" % _relative_path(form_file_path))

    with open(os.path.join(current_path, 'application/forms/__init__.py'), 'a') as package_file:
        package_file.write('\nfrom .%s import *' % form_name)

def _relative_path(absolute_path):
    current_path = os.getcwd()
    return absolute_path.split(current_path)[1][1:]

if __name__ == "__main__":
    main()


# coding: utf-8
import datetime

def timesince(value):
    """Friendly time gap"""
    if not value:
        return ""

    if not isinstance(value, datetime.date):
        return value

    now = datetime.datetime.now()
    delta = now - value

    if value > now:
        return "right now"
    elif delta.days > 365:
        return '%d years ago' % (delta.days / 365)
    elif delta.days > 30:
        return '%d months ago' % (delta.days / 30)
    elif delta.days > 0:
        return '%d days ago' % delta.days
    elif delta.seconds > 3600:
        return '%d hours ago' % (delta.seconds / 3600)
    elif delta.seconds > 60:
        return '%d minutes ago' % (delta.seconds / 60)
    else:
        return 'right now'


# coding: utf-8
import re
from urlparse import urlparse

def check_url(form, field):
    """Check url schema."""
    url = field.data.strip()
    if not url:
        return
    result = urlparse(url)
    if result.scheme == "":
        field.data = "http://%s" % re.sub(r'^:?/*', '', url)


# coding: utf-8
from itsdangerous import URLSafeSerializer, BadSignature
from flask import current_app

def encode(something):
    """Encode something with SECRET_KEY."""
    secret_key = current_app.config.get('SECRET_KEY')
    s = URLSafeSerializer(secret_key)
    return s.dumps(something)

def decode(something):
    """Decode something with SECRET_KEY."""
    secret_key = current_app.config.get('SECRET_KEY')
    s = URLSafeSerializer(secret_key)
    try:
        return s.loads(something)
    except BadSignature:
        return None


# coding: utf-8
import functools
from flask import Response, json

def jsonify(func):
    """JSON decorator."""

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        r = func(*args, **kwargs)
        if isinstance(r, tuple):
            code, data = r
        else:
            code, data = 200, r
        return Response(json.dumps(data), status=code, mimetype='application/json')

    return wrapper


# coding: utf-8
import os
import errno
from flask import current_app, url_for

def absolute_url_for(endpoint, **values):
    """Absolute url for endpoint."""
    config = current_app.config
    site_domain = config.get('SITE_DOMAIN')
    relative_url = url_for(endpoint, **values)
    return join_url(site_domain, relative_url)

def join_url(pre_url, pro_url):
    return "%s/%s" % (pre_url.rstrip('/'), pro_url.lstrip('/'))

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc:  # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise


# coding: UTF-8
import os

def load_config():
    """Load config."""
    mode = os.environ.get('MODE')
    try:
        if mode == 'PRODUCTION':
            from .production import ProductionConfig
            return ProductionConfig
        elif mode == 'TESTING':
            from .testing import TestingConfig
            return TestingConfig
        else:
            from .development import DevelopmentConfig
            return DevelopmentConfig
    except ImportError:
        from .default import Config
        return Config


# coding: utf-8
from flask import session
from ..models import User

def signin_user(user, permenent=True):
    """Sign in user."""
    session.permanent = permenent
    session['user_id'] = user.id

def signout_user():
    """Sign out user."""
    session.pop('user_id', None)

def get_current_user():
    """Get current user."""
    if not 'user_id' in session:
        return None
    user = User.query.filter(User.id == session['user_id']).first()
    if not user:
        signout_user()
        return None
    return user


# coding: utf-8
from flask import render_template, Blueprint, redirect, request, url_for
from ..forms import SigninForm, SignupForm
from ..utils.account import signin_user, signout_user
from ..utils.permissions import VisitorPermission, UserPermission
from ..models import db, User

bp = Blueprint('account', __name__)

@bp.route('/signin', methods=['GET', 'POST'])
@VisitorPermission()
def signin():
    """Signin"""
    form = SigninForm()
    if form.validate_on_submit():
        signin_user(form.user)
        return redirect(url_for('site.index'))
    return render_template('account/signin/signin.html', form=form)

@bp.route('/signup', methods=['GET', 'POST'])
@VisitorPermission()
def signup():
    """Signup"""
    form = SignupForm()
    if form.validate_on_submit():
        params = form.data.copy()
        params.pop('repassword')
        user = User(**params)
        db.session.add(user)
        db.session.commit()
        signin_user(user)
        return redirect(url_for('site.index'))
    return render_template('account/signup/signup.html', form=form)

@bp.route('/signout')
def signout():
    """Signout"""
    signout_user()
    return redirect(request.referrer or url_for('site.index'))


# coding: utf-8
import sys
import os

# Insert project root path to sys.path
project_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_path not in sys.path:
    sys.path.insert(0, project_path)

import time
import logging
from flask import Flask, request, url_for, g, render_template
from flask_wtf.csrf import CsrfProtect
from flask_debugtoolbar import DebugToolbarExtension
from werkzeug.wsgi import SharedDataMiddleware
from werkzeug.contrib.fixers import ProxyFix
from six import iteritems
from .utils.account import get_current_user
from config import load_config

# convert python's encoding to utf8
try:
    from imp import reload

    reload(sys)
    sys.setdefaultencoding('utf8')
except (AttributeError, NameError):
    pass

def create_app():
    """Create Flask app."""
    config = load_config()

    app = Flask(__name__)
    app.config.from_object(config)

    # Proxy fix
    app.wsgi_app = ProxyFix(app.wsgi_app)

    # CSRF protect
    CsrfProtect(app)

    if app.debug or app.testing:
        DebugToolbarExtension(app)

        # Serve static files
        app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
            '/pages': os.path.join(app.config.get('PROJECT_PATH'), 'application/pages')
        })
    else:
        # Log errors to stderr in production mode
        app.logger.addHandler(logging.StreamHandler())
        app.logger.setLevel(logging.ERROR)

        # Enable Sentry
        if app.config.get('SENTRY_DSN'):
            from .utils.sentry import sentry

            sentry.init_app(app, dsn=app.config.get('SENTRY_DSN'))

        # Serve static files
        app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
            '/static': os.path.join(app.config.get('PROJECT_PATH'), 'output/static'),
            '/pkg': os.path.join(app.config.get('PROJECT_PATH'), 'output/pkg'),
            '/pages': os.path.join(app.config.get('PROJECT_PATH'), 'output/pages')
        })

    # Register components
    register_db(app)
    register_routes(app)
    register_jinja(app)
    register_error_handle(app)
    register_hooks(app)

    return app

def register_jinja(app):
    """Register jinja filters, vars, functions."""
    import jinja2
    from .utils import filters, permissions, helpers

    if app.debug or app.testing:
        my_loader = jinja2.ChoiceLoader([
            app.jinja_loader,
            jinja2.FileSystemLoader([
                os.path.join(app.config.get('PROJECT_PATH'), 'application/macros'),
                os.path.join(app.config.get('PROJECT_PATH'), 'application/pages')
            ])
        ])
    else:
        my_loader = jinja2.ChoiceLoader([
            app.jinja_loader,
            jinja2.FileSystemLoader([
                os.path.join(app.config.get('PROJECT_PATH'), 'output/macros'),
                os.path.join(app.config.get('PROJECT_PATH'), 'output/pages')
            ])
        ])
    app.jinja_loader = my_loader

    app.jinja_env.filters.update({
        'timesince': filters.timesince
    })

    def url_for_other_page(page):
        """Generate url for pagination."""
        view_args = request.view_args.copy()
        args = request.args.copy().to_dict()
        combined_args = dict(view_args.items() + args.items())
        combined_args['page'] = page
        return url_for(request.endpoint, **combined_args)

    rules = {}
    for endpoint, _rules in iteritems(app.url_map._rules_by_endpoint):
        if any(item in endpoint for item in ['_debug_toolbar', 'debugtoolbar', 'static']):
            continue
        rules[endpoint] = [{'rule': rule.rule} for rule in _rules]

    app.jinja_env.globals.update({
        'absolute_url_for': helpers.absolute_url_for,
        'url_for_other_page': url_for_other_page,
        'rules': rules,
        'permissions': permissions
    })

def register_db(app):
    """Register models."""
    from .models import db

    db.init_app(app)

def register_routes(app):
    """Register routes."""
    from . import controllers
    from flask.blueprints import Blueprint

    for module in _import_submodules_from_package(controllers):
        bp = getattr(module, 'bp')
        if bp and isinstance(bp, Blueprint):
            app.register_blueprint(bp)

def register_error_handle(app):
    """Register HTTP error pages."""

    @app.errorhandler(403)
    def page_403(error):
        return render_template('site/403/403.html'), 403

    @app.errorhandler(404)
    def page_404(error):
        return render_template('site/404/404.html'), 404

    @app.errorhandler(500)
    def page_500(error):
        return render_template('site/500/500.html'), 500

def register_hooks(app):
    """Register hooks."""

    @app.before_request
    def before_request():
        g.user = get_current_user()
        if g.user and g.user.is_admin:
            g._before_request_time = time.time()

    @app.after_request
    def after_request(response):
        if hasattr(g, '_before_request_time'):
            delta = time.time() - g._before_request_time
            response.headers['X-Render-Time'] = delta * 1000
        return response

def _get_template_name(template_reference):
    """Get current template name."""
    return template_reference._TemplateReference__context.name

def _import_submodules_from_package(package):
    import pkgutil

    modules = []
    for importer, modname, ispkg in pkgutil.iter_modules(package.__path__,
                                                         prefix=package.__name__ + "."):
        modules.append(__import__(modname, fromlist="dummy"))
    return modules