/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * pkix_policynode.c
 *
 * Policy Node Object Type Definition
 *
 */

#include "pkix_policynode.h"

/* --Private-PolicyNode-Functions---------------------------------- */

/*
 * FUNCTION: pkix_PolicyNode_GetChildrenMutable
 * DESCRIPTION:
 *
 *  Retrieves the List of PolicyNodes representing the child nodes of the
 *  Policy Node pointed to by "node" and stores it at "pChildren". If "node"
 *  has no List of child nodes, this function stores NULL at "pChildren".
 *
 *  Note that the List returned by this function may be mutable. This function
 *  differs from the public function PKIX_PolicyNode_GetChildren in that
 *  respect. (It also differs in that the public function creates an empty
 *  List, if necessary, rather than storing NULL.)
 *
 *  During certificate processing, children Lists are created and modified.
 *  Once the list is accessed using the public call, the List is set immutable.
 *
 * PARAMETERS:
 *  "node"
 *      Address of PolicyNode whose child nodes are to be stored.
 *      Must be non-NULL.
 *  "pChildren"
 *      Address where object pointer will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_PolicyNode_GetChildrenMutable(
        PKIX_PolicyNode *node,
        PKIX_List **pChildren,  /* list of PKIX_PolicyNode */
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_GetChildrenMutable");

        PKIX_NULLCHECK_TWO(node, pChildren);

        PKIX_INCREF(node->children);

        *pChildren = node->children;

cleanup:
        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Create
 * DESCRIPTION:
 *
 *  Creates a new PolicyNode using the OID pointed to by "validPolicy", the List
 *  of CertPolicyQualifiers pointed to by "qualifierSet", the criticality
 *  indicated by the Boolean value of "criticality", and the List of OIDs
 *  pointed to by "expectedPolicySet", and stores the result at "pObject". The
 *  criticality should be derived from whether the certificate policy extension
 *  was marked as critical in the certificate that led to creation of this
 *  PolicyNode. The "qualifierSet" and "expectedPolicySet" Lists are made
 *  immutable. The PolicyNode pointers to parent and to children are initialized
 *  to NULL, and the depth is set to zero; those values should be set by using
 *  the pkix_PolicyNode_AddToParent function.
 *
 * PARAMETERS
 *  "validPolicy"
 *      Address of OID of the valid policy for the path. Must be non-NULL
 *  "qualifierSet"
 *      Address of List of CertPolicyQualifiers associated with the validpolicy.
 *      May be NULL
 *  "criticality"
 *      Boolean indicator of whether the criticality should be set in this
 *      PolicyNode
 *  "expectedPolicySet"
 *      Address of List of OIDs that would satisfy this policy in the next
 *      certificate. Must be non-NULL
 *  "pObject"
 *      Address where the PolicyNode pointer will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a PolicyNode Error if the function fails  in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_PolicyNode_Create(
        PKIX_PL_OID *validPolicy,
        PKIX_List *qualifierSet,
        PKIX_Boolean criticality,
        PKIX_List *expectedPolicySet,
        PKIX_PolicyNode **pObject,
        void *plContext)
{
        PKIX_PolicyNode *node = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Create");

        PKIX_NULLCHECK_THREE(validPolicy, expectedPolicySet, pObject);

        PKIX_CHECK(PKIX_PL_Object_Alloc
                (PKIX_CERTPOLICYNODE_TYPE,
                sizeof (PKIX_PolicyNode),
                (PKIX_PL_Object **)&node,
                plContext),
                PKIX_COULDNOTCREATEPOLICYNODEOBJECT);

        PKIX_INCREF(validPolicy);
        node->validPolicy = validPolicy;

        PKIX_INCREF(qualifierSet);
        node->qualifierSet = qualifierSet;
        if (qualifierSet) {
                PKIX_CHECK(PKIX_List_SetImmutable(qualifierSet, plContext),
                        PKIX_LISTSETIMMUTABLEFAILED);
        }

        node->criticality = criticality;

        PKIX_INCREF(expectedPolicySet);
        node->expectedPolicySet = expectedPolicySet;
        PKIX_CHECK(PKIX_List_SetImmutable(expectedPolicySet, plContext),
                PKIX_LISTSETIMMUTABLEFAILED);

        node->parent = NULL;
        node->children = NULL;
        node->depth = 0;

        *pObject = node;
        node = NULL;

cleanup:

        PKIX_DECREF(node);

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_AddToParent
 * DESCRIPTION:
 *
 *  Adds the PolicyNode pointed to by "child" to the List of children of
 *  the PolicyNode pointed to by "parentNode". If "parentNode" had a
 *  NULL pointer for the List of children, a new List is created containing
 *  "child". Otherwise "child" is appended to the existing List. The
 *  parent field in "child" is set to "parent", and the depth field is
 *  set to one more than the corresponding value in "parent".
 *
 *  Depth, in this context, means distance from the root node, which
 *  is at depth zero.
 *
 * PARAMETERS:
 *  "parentNode"
 *      Address of PolicyNode whose List of child PolicyNodes is to be
 *      created or appended to. Must be non-NULL.
 *  "child"
 *      Address of PolicyNode to be added to parentNode's List. Must be
 *      non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Not Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_PolicyNode_AddToParent(
        PKIX_PolicyNode *parentNode,
        PKIX_PolicyNode *child,
        void *plContext)
{
        PKIX_List *listOfChildren = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_AddToParent");

        PKIX_NULLCHECK_TWO(parentNode, child);

        listOfChildren = parentNode->children;
        if (listOfChildren == NULL) {
                PKIX_CHECK(PKIX_List_Create(&listOfChildren, plContext),
                        PKIX_LISTCREATEFAILED);
                parentNode->children = listOfChildren;
        }

        /*
         * Note: this link is not reference-counted. The link from parent
         * to child is counted (actually, the parent "owns" a List which
         * "owns" children), but the children do not "own" the parent.
         * Otherwise, there would be loops.
         */
        child->parent = parentNode;

        child->depth = 1 + (parentNode->depth);

        PKIX_CHECK(PKIX_List_AppendItem
                (listOfChildren, (PKIX_PL_Object *)child, plContext),
                PKIX_COULDNOTAPPENDCHILDTOPARENTSPOLICYNODELIST);

        PKIX_CHECK(PKIX_PL_Object_InvalidateCache
                ((PKIX_PL_Object *)parentNode, plContext),
                PKIX_OBJECTINVALIDATECACHEFAILED);

        PKIX_CHECK(PKIX_PL_Object_InvalidateCache
                ((PKIX_PL_Object *)child, plContext),
                PKIX_OBJECTINVALIDATECACHEFAILED);

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Prune
 * DESCRIPTION:
 *
 *  Prunes a tree below the PolicyNode whose address is pointed to by "node",
 *  using the UInt32 value of "height" as the distance from the leaf level,
 *  and storing at "pDelete" the Boolean value of whether this PolicyNode is,
 *  after pruning, childless and should be pruned.
 *
 *  Any PolicyNode at height 0 is allowed to survive. If the height is greater
 *  than zero, pkix_PolicyNode_Prune is called recursively for each child of
 *  the current PolicyNode. After this process, a node with no children
 *  stores PKIX_TRUE in "pDelete" to indicate that it should be deleted.
 *
 * PARAMETERS:
 *  "node"
 *      Address of the PolicyNode to be pruned. Must be non-NULL.
 *  "height"
 *      UInt32 value for the distance from the leaf level
 *  "pDelete"
 *      Address to store the Boolean return value of PKIX_TRUE if this node
 *      should be pruned, or PKIX_FALSE if there remains at least one
 *      branch of the required height. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Not Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_PolicyNode_Prune(
        PKIX_PolicyNode *node,
        PKIX_UInt32 height,
        PKIX_Boolean *pDelete,
        void *plContext)
{
        PKIX_Boolean childless = PKIX_FALSE;
        PKIX_Boolean shouldBePruned = PKIX_FALSE;
        PKIX_UInt32 listSize = 0;
        PKIX_UInt32 listIndex = 0;
        PKIX_PolicyNode *candidate = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Prune");

        PKIX_NULLCHECK_TWO(node, pDelete);

        /* Don't prune at the leaf */
        if (height == 0) {
                goto cleanup;
        }

        /* Above the bottom level, childless nodes get pruned */
        if (!(node->children)) {
                childless = PKIX_TRUE;
                goto cleanup;
        }

        /*
         * This node has children. If they are leaf nodes,
         * we know they will live. Otherwise, check them out.
         */
        if (height > 1) {
                PKIX_CHECK(PKIX_List_GetLength
                        (node->children, &listSize, plContext),
                        PKIX_LISTGETLENGTHFAILED);
                /*
                 * By working backwards from the end of the list,
                 * we avoid having to worry about possible
                 * decreases in the size of the list, as we
                 * delete items. The only nuisance is that since the
                 * index is UInt32, we can't check for it to reach -1;
                 * we have to use the 1-based index, rather than the
                 * 0-based index that PKIX_List functions require.
                 */
                for (listIndex = listSize; listIndex > 0; listIndex--) {
                        PKIX_CHECK(PKIX_List_GetItem
                                (node->children,
                                (listIndex - 1),
                                (PKIX_PL_Object **)&candidate,
                                plContext),
                                PKIX_LISTGETITEMFAILED);

                        PKIX_CHECK(pkix_PolicyNode_Prune
                                (candidate,
                                height - 1,
                                &shouldBePruned,
                                plContext),
                                PKIX_POLICYNODEPRUNEFAILED);

                        if (shouldBePruned == PKIX_TRUE) {
                                PKIX_CHECK(PKIX_List_DeleteItem
                                        (node->children,
                                        (listIndex - 1),
                                        plContext),
                                        PKIX_LISTDELETEITEMFAILED);
                        }

                        PKIX_DECREF(candidate);
                }
        }

        /* Prune if this node has *become* childless */
        PKIX_CHECK(PKIX_List_GetLength
                (node->children, &listSize, plContext),
                PKIX_LISTGETLENGTHFAILED);
        if (listSize == 0) {
                childless = PKIX_TRUE;
        }

        /*
         * Even if we did not change this node, or any of its children,
         * maybe a [great-]*grandchild was pruned.
         */
        PKIX_CHECK(PKIX_PL_Object_InvalidateCache
                ((PKIX_PL_Object *)node, plContext),
                PKIX_OBJECTINVALIDATECACHEFAILED);

cleanup:
        *pDelete = childless;

        PKIX_DECREF(candidate);

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_SinglePolicyNode_ToString
 * DESCRIPTION:
 *
 *  Creates a String representation of the attributes of the PolicyNode
 *  pointed to by "node", other than its parents or children, and
 *  stores the result at "pString".
 *
 * PARAMETERS:
 *  "node"
 *      Address of PolicyNode to be described by the string. Must be non-NULL.
 *  "pString"
 *      Address where object pointer will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if function succeeds
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in a fatal way
 */
PKIX_Error *
pkix_SinglePolicyNode_ToString(
        PKIX_PolicyNode *node,
        PKIX_PL_String **pString,
        void *plContext)
{
        PKIX_PL_String *fmtString = NULL;
        PKIX_PL_String *validString = NULL;
        PKIX_PL_String *qualifierString = NULL;
        PKIX_PL_String *criticalityString = NULL;
        PKIX_PL_String *expectedString = NULL;
        PKIX_PL_String *outString = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_SinglePolicyNode_ToString");
        PKIX_NULLCHECK_TWO(node, pString);
        PKIX_NULLCHECK_TWO(node->validPolicy, node->expectedPolicySet);

        PKIX_CHECK(PKIX_PL_String_Create
                (PKIX_ESCASCII,
                "{%s,%s,%s,%s,%d}",
                0,
                &fmtString,
                plContext),
                PKIX_CANTCREATESTRING);

        PKIX_CHECK(PKIX_PL_Object_ToString
                ((PKIX_PL_Object *)(node->validPolicy),
                &validString,
                plContext),
                PKIX_OIDTOSTRINGFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                ((PKIX_PL_Object *)(node->expectedPolicySet),
                &expectedString,
                plContext),
                PKIX_LISTTOSTRINGFAILED);

        if (node->qualifierSet) {
                PKIX_CHECK(PKIX_PL_Object_ToString
                        ((PKIX_PL_Object *)(node->qualifierSet),
                        &qualifierString,
                        plContext),
                        PKIX_LISTTOSTRINGFAILED);
        } else {
                PKIX_CHECK(PKIX_PL_String_Create
                        (PKIX_ESCASCII,
                        "{}",
                        0,
                        &qualifierString,
                        plContext),
                        PKIX_CANTCREATESTRING);
        }

        PKIX_CHECK(PKIX_PL_String_Create
                (PKIX_ESCASCII,
                (node->criticality)?"Critical":"Not Critical",
                0,
                &criticalityString,
                plContext),
                PKIX_CANTCREATESTRING);

        PKIX_CHECK(PKIX_PL_Sprintf
                (&outString,
                plContext,
                fmtString,
                validString,
                qualifierString,
                criticalityString,
                expectedString,
                node->depth),
                PKIX_SPRINTFFAILED);

        *pString = outString;

cleanup:

        PKIX_DECREF(fmtString);
        PKIX_DECREF(validString);
        PKIX_DECREF(qualifierString);
        PKIX_DECREF(criticalityString);
        PKIX_DECREF(expectedString);
        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_ToString_Helper
 * DESCRIPTION:
 *
 *  Produces a String representation of a PolicyNode tree below the PolicyNode
 *  pointed to by "rootNode", with each line of output prefixed by the String
 *  pointed to by "indent", and stores the result at "pTreeString". It is
 *  called recursively, with ever-increasing indentation, for successively
 *  lower nodes on the tree.
 *
 * PARAMETERS:
 *  "rootNode"
 *      Address of PolicyNode subtree. Must be non-NULL.
 *  "indent"
 *      Address of String to be prefixed to each line of output. May be NULL
 *      if no indentation is desired
 *  "pTreeString"
 *      Address where the resulting String will be stored; must be non-NULL
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_PolicyNode_ToString_Helper(
        PKIX_PolicyNode *rootNode,
        PKIX_PL_String *indent,
        PKIX_PL_String **pTreeString,
        void *plContext)
{
        PKIX_PL_String *nextIndentFormat = NULL;
        PKIX_PL_String *thisNodeFormat = NULL;
        PKIX_PL_String *childrenFormat = NULL;
        PKIX_PL_String *nextIndentString = NULL;
        PKIX_PL_String *resultString = NULL;
        PKIX_PL_String *thisItemString = NULL;
        PKIX_PL_String *childString = NULL;
        PKIX_PolicyNode *childNode = NULL;
        PKIX_UInt32 numberOfChildren = 0;
        PKIX_UInt32 childIndex = 0;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_ToString_Helper");

        PKIX_NULLCHECK_TWO(rootNode, pTreeString);

        /* Create a string for this node */
        PKIX_CHECK(pkix_SinglePolicyNode_ToString
                (rootNode, &thisItemString, plContext),
                PKIX_ERRORINSINGLEPOLICYNODETOSTRING);

        if (indent) {
                PKIX_CHECK(PKIX_PL_String_Create
                        (PKIX_ESCASCII,
                        "%s%s",
                        0,
                        &thisNodeFormat,
                        plContext),
                        PKIX_ERRORCREATINGFORMATSTRING);

                PKIX_CHECK(PKIX_PL_Sprintf
                        (&resultString,
                        plContext,
                        thisNodeFormat,
                        indent,
                        thisItemString),
                        PKIX_ERRORINSPRINTF);
        } else {
                PKIX_CHECK(PKIX_PL_String_Create
                        (PKIX_ESCASCII,
                        "%s",
                        0,
                        &thisNodeFormat,
                        plContext),
                        PKIX_ERRORCREATINGFORMATSTRING);

                PKIX_CHECK(PKIX_PL_Sprintf
                        (&resultString,
                        plContext,
                        thisNodeFormat,
                        thisItemString),
                        PKIX_ERRORINSPRINTF);
        }

        PKIX_DECREF(thisItemString);
        thisItemString = resultString;

        /* if no children, we are done */
        if (rootNode->children) {
                PKIX_CHECK(PKIX_List_GetLength
                        (rootNode->children, &numberOfChildren, plContext),
                        PKIX_LISTGETLENGTHFAILED);
        }

        if (numberOfChildren != 0) {
                /*
                 * We create a string for each child in turn,
                 * concatenating them to thisItemString.
                 */

                /* Prepare an indent string for each child */
                if (indent) {
                        PKIX_CHECK(PKIX_PL_String_Create
                                (PKIX_ESCASCII,
                                "%s. ",
                                0,
                                &nextIndentFormat,
                                plContext),
                                PKIX_ERRORCREATINGFORMATSTRING);

                        PKIX_CHECK(PKIX_PL_Sprintf
                                (&nextIndentString,
                                plContext,
                                nextIndentFormat,
                                indent),
                                PKIX_ERRORINSPRINTF);
                } else {
                        PKIX_CHECK(PKIX_PL_String_Create
                                (PKIX_ESCASCII,
                                ". ",
                                0,
                                &nextIndentString,
                                plContext),
                                PKIX_ERRORCREATINGINDENTSTRING);
                }

                /* Prepare the format for concatenation. */
                PKIX_CHECK(PKIX_PL_String_Create
                        (PKIX_ESCASCII,
                        "%s\n%s",
                        0,
                        &childrenFormat,
                        plContext),
                        PKIX_ERRORCREATINGFORMATSTRING);

                for (childIndex = 0;
                        childIndex < numberOfChildren;
                        childIndex++) {
                        PKIX_CHECK(PKIX_List_GetItem
                                (rootNode->children,
                                childIndex,
                                (PKIX_PL_Object **)&childNode,
                                plContext),
                                PKIX_LISTGETITEMFAILED);

                        PKIX_CHECK(pkix_PolicyNode_ToString_Helper
                                (childNode,
                                nextIndentString,
                                &childString,
                                plContext),
                                PKIX_ERRORCREATINGCHILDSTRING);


                        PKIX_CHECK(PKIX_PL_Sprintf
                                (&resultString,
                                plContext,
                                childrenFormat,
                                thisItemString,
                                childString),
                        PKIX_ERRORINSPRINTF);

                        PKIX_DECREF(childNode);
                        PKIX_DECREF(childString);
                        PKIX_DECREF(thisItemString);

                        thisItemString = resultString;
                }
        }

        *pTreeString = thisItemString;

cleanup:
        if (PKIX_ERROR_RECEIVED) {
                PKIX_DECREF(thisItemString);
        }

        PKIX_DECREF(nextIndentFormat);
        PKIX_DECREF(thisNodeFormat);
        PKIX_DECREF(childrenFormat);
        PKIX_DECREF(nextIndentString);
        PKIX_DECREF(childString);
        PKIX_DECREF(childNode);

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_ToString
 * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_PolicyNode_ToString(
        PKIX_PL_Object *object,
        PKIX_PL_String **pTreeString,
        void *plContext)
{
        PKIX_PolicyNode *rootNode = NULL;
        PKIX_PL_String *resultString = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_ToString");

        PKIX_NULLCHECK_TWO(object, pTreeString);

        PKIX_CHECK(pkix_CheckType(object, PKIX_CERTPOLICYNODE_TYPE, plContext),
                PKIX_OBJECTNOTPOLICYNODE);

        rootNode = (PKIX_PolicyNode *)object;

        PKIX_CHECK(pkix_PolicyNode_ToString_Helper
                (rootNode, NULL, &resultString, plContext),
                PKIX_ERRORCREATINGSUBTREESTRING);

        *pTreeString = resultString;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Destroy
 * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_PolicyNode_Destroy(
        PKIX_PL_Object *object,
        void *plContext)
{
        PKIX_PolicyNode *node = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Destroy");

        PKIX_NULLCHECK_ONE(object);

        PKIX_CHECK(pkix_CheckType(object, PKIX_CERTPOLICYNODE_TYPE, plContext),
                PKIX_OBJECTNOTPOLICYNODE);

        node = (PKIX_PolicyNode*)object;

        node->criticality = PKIX_FALSE;
        PKIX_DECREF(node->validPolicy);
        PKIX_DECREF(node->qualifierSet);
        PKIX_DECREF(node->expectedPolicySet);
        PKIX_DECREF(node->children);

        /*
         * Note: the link to parent is not reference-counted. See comment
         * in pkix_PolicyNode_AddToParent for more details.
         */
        node->parent = NULL;
        node->depth = 0;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_SinglePolicyNode_Hashcode
 * DESCRIPTION:
 *
 *  Computes the hashcode of the attributes of the PolicyNode pointed to by
 *  "node", other than its parents and children, and stores the result at
 *  "pHashcode".
 *
 * PARAMETERS:
 *  "node"
 *      Address of PolicyNode to be hashcoded; must be non-NULL
 *  "pHashcode"
 *      Address where UInt32 result will be stored; must be non-NULL
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if function succeeds
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in a fatal way
 */
static PKIX_Error *
pkix_SinglePolicyNode_Hashcode(
        PKIX_PolicyNode *node,
        PKIX_UInt32 *pHashcode,
        void *plContext)
{
        PKIX_UInt32 componentHash = 0;
        PKIX_UInt32 nodeHash = 0;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_SinglePolicyNode_Hashcode");
        PKIX_NULLCHECK_TWO(node, pHashcode);
        PKIX_NULLCHECK_TWO(node->validPolicy, node->expectedPolicySet);

        PKIX_HASHCODE
                (node->qualifierSet,
                &nodeHash,
                plContext,
                PKIX_FAILUREHASHINGLISTQUALIFIERSET);

        if (PKIX_TRUE == (node->criticality)) {
                nodeHash = 31*nodeHash + 0xff;
        } else {
                nodeHash = 31*nodeHash + 0x00;
        }

        PKIX_CHECK(PKIX_PL_Object_Hashcode
                ((PKIX_PL_Object *)node->validPolicy,
                &componentHash,
                plContext),
                PKIX_FAILUREHASHINGOIDVALIDPOLICY);

        nodeHash = 31*nodeHash + componentHash;

        PKIX_CHECK(PKIX_PL_Object_Hashcode
                ((PKIX_PL_Object *)node->expectedPolicySet,
                &componentHash,
                plContext),
                PKIX_FAILUREHASHINGLISTEXPECTEDPOLICYSET);

        nodeHash = 31*nodeHash + componentHash;

        *pHashcode = nodeHash;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Hashcode
 * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_PolicyNode_Hashcode(
        PKIX_PL_Object *object,
        PKIX_UInt32 *pHashcode,
        void *plContext)
{
        PKIX_PolicyNode *node = NULL;
        PKIX_UInt32 childrenHash = 0;
        PKIX_UInt32 nodeHash = 0;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Hashcode");
        PKIX_NULLCHECK_TWO(object, pHashcode);

        PKIX_CHECK(pkix_CheckType
                (object, PKIX_CERTPOLICYNODE_TYPE, plContext),
                PKIX_OBJECTNOTPOLICYNODE);

        node = (PKIX_PolicyNode *)object;

        PKIX_CHECK(pkix_SinglePolicyNode_Hashcode
                (node, &nodeHash, plContext),
                PKIX_SINGLEPOLICYNODEHASHCODEFAILED);

        nodeHash = 31*nodeHash + (PKIX_UInt32)((char *)node->parent - (char *)NULL);

        PKIX_HASHCODE
                (node->children,
                &childrenHash,
                plContext,
                PKIX_OBJECTHASHCODEFAILED);

        nodeHash = 31*nodeHash + childrenHash;

        *pHashcode = nodeHash;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_SinglePolicyNode_Equals
 * DESCRIPTION:
 *
 *  Compares for equality the components of the PolicyNode pointed to by
 *  "firstPN", other than its parents and children, with those of the
 *  PolicyNode pointed to by "secondPN" and stores the result at "pResult"
 *  (PKIX_TRUE if equal; PKIX_FALSE if not).
 *
 * PARAMETERS:
 *  "firstPN"
 *      Address of first of the PolicyNodes to be compared; must be non-NULL
 *  "secondPN"
 *      Address of second of the PolicyNodes to be compared; must be non-NULL
 *  "pResult"
 *      Address where Boolean will be stored; must be non-NULL
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if function succeeds
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in a fatal way
 */
static PKIX_Error *
pkix_SinglePolicyNode_Equals(
        PKIX_PolicyNode *firstPN,
        PKIX_PolicyNode *secondPN,
        PKIX_Boolean *pResult,
        void *plContext)
{
        PKIX_Boolean compResult = PKIX_FALSE;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_SinglePolicyNode_Equals");
        PKIX_NULLCHECK_THREE(firstPN, secondPN, pResult);

        /* If both references are identical, they must be equal */
        if (firstPN == secondPN) {
                compResult = PKIX_TRUE;
                goto cleanup;
        }

        /*
         * It seems we have to do the comparisons. Do
         * the easiest ones first.
         */
        if ((firstPN->criticality) != (secondPN->criticality)) {
                goto cleanup;
        }
        if ((firstPN->depth) != (secondPN->depth)) {
                goto cleanup;
        }

        PKIX_EQUALS
                (firstPN->qualifierSet,
                secondPN->qualifierSet,
                &compResult,
                plContext,
                PKIX_OBJECTEQUALSFAILED);

        if (compResult == PKIX_FALSE) {
                goto cleanup;
        }

        /* These fields must be non-NULL */
        PKIX_NULLCHECK_TWO(firstPN->validPolicy, secondPN->validPolicy);

        PKIX_EQUALS
                (firstPN->validPolicy,
                secondPN->validPolicy,
                &compResult,
                plContext,
                PKIX_OBJECTEQUALSFAILED);

        if (compResult == PKIX_FALSE) {
                goto cleanup;
        }

        /* These fields must be non-NULL */
        PKIX_NULLCHECK_TWO
                (firstPN->expectedPolicySet, secondPN->expectedPolicySet);

        PKIX_EQUALS
                (firstPN->expectedPolicySet,
                secondPN->expectedPolicySet,
                &compResult,
                plContext,
                PKIX_OBJECTEQUALSFAILEDONEXPECTEDPOLICYSETS);

cleanup:

        *pResult = compResult;

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Equals
 * (see comments for PKIX_PL_Equals_Callback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_PolicyNode_Equals(
        PKIX_PL_Object *firstObject,
        PKIX_PL_Object *secondObject,
        PKIX_Boolean *pResult,
        void *plContext)
{
        PKIX_PolicyNode *firstPN = NULL;
        PKIX_PolicyNode *secondPN = NULL;
        PKIX_UInt32 secondType;
        PKIX_Boolean compResult = PKIX_FALSE;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Equals");
        PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult);

        /* test that firstObject is a PolicyNode */
        PKIX_CHECK(pkix_CheckType
                (firstObject, PKIX_CERTPOLICYNODE_TYPE, plContext),
                PKIX_FIRSTOBJECTNOTPOLICYNODE);

        /*
         * Since we know firstObject is a PolicyNode,
         * if both references are identical, they must be equal
         */
        if (firstObject == secondObject){
                compResult = PKIX_TRUE;
                goto cleanup;
        }

        /*
         * If secondObject isn't a PolicyNode, we
         * don't throw an error. We simply return FALSE.
         */
        PKIX_CHECK(PKIX_PL_Object_GetType
                    (secondObject, &secondType, plContext),
                    PKIX_COULDNOTGETTYPEOFSECONDARGUMENT);

        if (secondType != PKIX_CERTPOLICYNODE_TYPE) {
                goto cleanup;
        }

        /*
         * Oh, well, we have to do the comparisons. Do
         * the easiest ones first.
         */
        firstPN = (PKIX_PolicyNode *)firstObject;
        secondPN = (PKIX_PolicyNode *)secondObject;

        /*
         * We don't require the parents to be identical. In the
         * course of traversing the tree, we will have checked the
         * attributes of the parent nodes, and checking the lists
         * of children will determine whether they match.
         */

        PKIX_EQUALS
                (firstPN->children,
                secondPN->children,
                &compResult,
                plContext,
                PKIX_OBJECTEQUALSFAILEDONCHILDREN);

        if (compResult == PKIX_FALSE) {
                goto cleanup;
        }

        PKIX_CHECK(pkix_SinglePolicyNode_Equals
                (firstPN, secondPN, &compResult, plContext),
                PKIX_SINGLEPOLICYNODEEQUALSFAILED);

cleanup:

        *pResult = compResult;

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_DuplicateHelper
 * DESCRIPTION:
 *
 *  Duplicates the PolicyNode whose address is pointed to by "original",
 *  and stores the result at "pNewNode", if a non-NULL pointer is provided
 *  for "pNewNode". In addition, the created PolicyNode is added as a child
 *  to "parent", if a non-NULL pointer is provided for "parent". Then this
 *  function is called recursively to duplicate each of the children of
 *  "original". At the top level this function is called with a null
 *  "parent" and a non-NULL "pNewNode". Below the top level "parent" will
 *  be non-NULL and "pNewNode" will be NULL.
 *
 * PARAMETERS:
 *  "original"
 *      Address of PolicyNode to be copied; must be non-NULL
 *  "parent"
 *      Address of PolicyNode to which the created node is to be added as a
 *      child; NULL for the top-level call and non-NULL below the top level
 *  "pNewNode"
 *      Address to store the node created; should be NULL if "parent" is
 *      non-NULL and vice versa
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Conditionally Thread Safe
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if function succeeds
 *  Returns a PolicyNode Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in a fatal way
 */
static PKIX_Error *
pkix_PolicyNode_DuplicateHelper(
        PKIX_PolicyNode *original,
        PKIX_PolicyNode *parent,
        PKIX_PolicyNode **pNewNode,
        void *plContext)
{
        PKIX_UInt32 numChildren = 0;
        PKIX_UInt32 childIndex = 0;
        PKIX_List *children = NULL; /* List of PKIX_PolicyNode */
        PKIX_PolicyNode *copy = NULL;
        PKIX_PolicyNode *child = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_DuplicateHelper");

        PKIX_NULLCHECK_THREE
                (original, original->validPolicy, original->expectedPolicySet);

        /*
         * These components are immutable, so copying the pointers
         * is sufficient. The create function increments the reference
         * counts as it stores the pointers into the new object.
         */
        PKIX_CHECK(pkix_PolicyNode_Create
                (original->validPolicy,
                original->qualifierSet,
                original->criticality,
                original->expectedPolicySet,
                &copy,
                plContext),
                PKIX_POLICYNODECREATEFAILED);

        if (parent) {
                PKIX_CHECK(pkix_PolicyNode_AddToParent(parent, copy, plContext),
                        PKIX_POLICYNODEADDTOPARENTFAILED);
        }

        /* Are there any children to duplicate? */
        children = original->children;

        if (children) {
            PKIX_CHECK(PKIX_List_GetLength(children, &numChildren, plContext),
                PKIX_LISTGETLENGTHFAILED);
        }

        for (childIndex = 0; childIndex < numChildren; childIndex++) {
                PKIX_CHECK(PKIX_List_GetItem
                        (children,
                        childIndex,
                        (PKIX_PL_Object **)&child,
                        plContext),
                        PKIX_LISTGETITEMFAILED);

                PKIX_CHECK(pkix_PolicyNode_DuplicateHelper
                        (child, copy, NULL, plContext),
                        PKIX_POLICYNODEDUPLICATEHELPERFAILED);

                PKIX_DECREF(child);
        }

        if (pNewNode) {
                *pNewNode = copy;
                copy = NULL; /* no DecRef if we give our handle away */
        }

cleanup:
        PKIX_DECREF(copy);
        PKIX_DECREF(child);

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_Duplicate
 * (see comments for PKIX_PL_Duplicate_Callback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_PolicyNode_Duplicate(
        PKIX_PL_Object *object,
        PKIX_PL_Object **pNewObject,
        void *plContext)
{
        PKIX_PolicyNode *original = NULL;
        PKIX_PolicyNode *copy = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_Duplicate");

        PKIX_NULLCHECK_TWO(object, pNewObject);

        PKIX_CHECK(pkix_CheckType
                (object, PKIX_CERTPOLICYNODE_TYPE, plContext),
                PKIX_OBJECTNOTPOLICYNODE);

        original = (PKIX_PolicyNode *)object;

        PKIX_CHECK(pkix_PolicyNode_DuplicateHelper
                (original, NULL, &copy, plContext),
                PKIX_POLICYNODEDUPLICATEHELPERFAILED);

        *pNewObject = (PKIX_PL_Object *)copy;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: pkix_PolicyNode_RegisterSelf
 * DESCRIPTION:
 *
 *  Registers PKIX_CERTPOLICYNODE_TYPE and its related
 *  functions with systemClasses[]
 *
 * THREAD SAFETY:
 *  Not Thread Safe - for performance and complexity reasons
 *
 *  Since this function is only called by PKIX_PL_Initialize,
 *  which should only be called once, it is acceptable that
 *  this function is not thread-safe.
 */
PKIX_Error *
pkix_PolicyNode_RegisterSelf(void *plContext)
{

        extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
        pkix_ClassTable_Entry entry;

        PKIX_ENTER(CERTPOLICYNODE, "pkix_PolicyNode_RegisterSelf");

        entry.description = "PolicyNode";
        entry.objCounter = 0;
        entry.typeObjectSize = sizeof(PKIX_PolicyNode);
        entry.destructor = pkix_PolicyNode_Destroy;
        entry.equalsFunction = pkix_PolicyNode_Equals;
        entry.hashcodeFunction = pkix_PolicyNode_Hashcode;
        entry.toStringFunction = pkix_PolicyNode_ToString;
        entry.comparator = NULL;
        entry.duplicateFunction = pkix_PolicyNode_Duplicate;

        systemClasses[PKIX_CERTPOLICYNODE_TYPE] = entry;

        PKIX_RETURN(CERTPOLICYNODE);
}


/* --Public-PolicyNode-Functions----------------------------------- */

/*
 * FUNCTION: PKIX_PolicyNode_GetChildren
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetChildren(
        PKIX_PolicyNode *node,
        PKIX_List **pChildren,  /* list of PKIX_PolicyNode */
        void *plContext)
{
        PKIX_List *children = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetChildren");

        PKIX_NULLCHECK_TWO(node, pChildren);

        PKIX_INCREF(node->children);
        children = node->children;

        if (!children) {
                PKIX_CHECK(PKIX_List_Create(&children, plContext),
                        PKIX_LISTCREATEFAILED);
        }

        PKIX_CHECK(PKIX_List_SetImmutable(children, plContext),
                PKIX_LISTSETIMMUTABLEFAILED);

        *pChildren = children;

cleanup:
        if (PKIX_ERROR_RECEIVED) {
                PKIX_DECREF(children);
        }

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_GetParent
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetParent(
        PKIX_PolicyNode *node,
        PKIX_PolicyNode **pParent,
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetParent");

        PKIX_NULLCHECK_TWO(node, pParent);

        PKIX_INCREF(node->parent);
        *pParent = node->parent;

cleanup:
        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_GetValidPolicy
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetValidPolicy(
        PKIX_PolicyNode *node,
        PKIX_PL_OID **pValidPolicy,
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetValidPolicy");

        PKIX_NULLCHECK_TWO(node, pValidPolicy);

        PKIX_INCREF(node->validPolicy);
        *pValidPolicy = node->validPolicy;

cleanup:
        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_GetPolicyQualifiers
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetPolicyQualifiers(
        PKIX_PolicyNode *node,
        PKIX_List **pQualifiers,  /* list of PKIX_PL_CertPolicyQualifier */
        void *plContext)
{
        PKIX_List *qualifiers = NULL;

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetPolicyQualifiers");

        PKIX_NULLCHECK_TWO(node, pQualifiers);

        PKIX_INCREF(node->qualifierSet);
        qualifiers = node->qualifierSet;

        if (!qualifiers) {
                PKIX_CHECK(PKIX_List_Create(&qualifiers, plContext),
                        PKIX_LISTCREATEFAILED);
        }

        PKIX_CHECK(PKIX_List_SetImmutable(qualifiers, plContext),
                PKIX_LISTSETIMMUTABLEFAILED);

        *pQualifiers = qualifiers;

cleanup:

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_GetExpectedPolicies
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetExpectedPolicies(
        PKIX_PolicyNode *node,
        PKIX_List **pExpPolicies,  /* list of PKIX_PL_OID */
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetExpectedPolicies");

        PKIX_NULLCHECK_TWO(node, pExpPolicies);

        PKIX_INCREF(node->expectedPolicySet);
        *pExpPolicies = node->expectedPolicySet;

cleanup:
        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_IsCritical
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_IsCritical(
        PKIX_PolicyNode *node,
        PKIX_Boolean *pCritical,
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_IsCritical");

        PKIX_NULLCHECK_TWO(node, pCritical);

        *pCritical = node->criticality;

        PKIX_RETURN(CERTPOLICYNODE);
}

/*
 * FUNCTION: PKIX_PolicyNode_GetDepth
 * (see description of this function in pkix_results.h)
 */
PKIX_Error *
PKIX_PolicyNode_GetDepth(
        PKIX_PolicyNode *node,
        PKIX_UInt32 *pDepth,
        void *plContext)
{

        PKIX_ENTER(CERTPOLICYNODE, "PKIX_PolicyNode_GetDepth");

        PKIX_NULLCHECK_TWO(node, pDepth);

        *pDepth = node->depth;

        PKIX_RETURN(CERTPOLICYNODE);
}
