/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the PKIX-C library.
 *
 * The Initial Developer of the Original Code is
 * Sun Microsystems, Inc.
 * Portions created by the Initial Developer are
 * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Contributor(s):
 *   Sun Microsystems, Inc.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/*
 * pkix_pl_generalname.c
 *
 * GeneralName Object Definitions
 *
 */

#include "pkix_pl_generalname.h"

/* --Private-GeneralName-Functions------------------------------------- */

/*
 * FUNCTION: pkix_pl_GeneralName_GetNssGeneralName
 * DESCRIPTION:
 *
 *  Retrieves the NSS representation of the PKIX_PL_GeneralName pointed by
 *  "genName" and stores it at "pNssGenName". The NSS data type CERTGeneralName
 *  is stored in this object when the object was created.
 *
 * PARAMETERS:
 *  "genName"
 *      Address of PKIX_PL_GeneralName. Must be non-NULL.
 *  "pNssGenName"
 *      Address where CERTGeneralName 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 GeneralName 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_pl_GeneralName_GetNssGeneralName(
        PKIX_PL_GeneralName *genName,
        CERTGeneralName **pNssGenName,
        void *plContext)
{
        CERTGeneralName *nssGenName = NULL;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_GetNssGeneralName");
        PKIX_NULLCHECK_THREE(genName, pNssGenName, genName->nssGeneralNameList);

        nssGenName = genName->nssGeneralNameList->name;

        *pNssGenName = nssGenName;

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_OtherName_Create
 * DESCRIPTION:
 *
 *  Creates new OtherName which represents the CERTGeneralName pointed to by
 *  "nssAltName" and stores it at "pOtherName".
 *
 * PARAMETERS:
 *  "nssAltName"
 *      Address of CERTGeneralName. Must be non-NULL.
 *  "pOtherName"
 *      Address where object 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 GeneralName 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_pl_OtherName_Create(
        CERTGeneralName *nssAltName,
        OtherName **pOtherName,
        void *plContext)
{
        OtherName *otherName = NULL;
        SECItem secItemName;
        SECItem secItemOID;
        SECStatus rv;

        PKIX_ENTER(GENERALNAME, "pkix_pl_OtherName_Create");
        PKIX_NULLCHECK_TWO(nssAltName, pOtherName);

        PKIX_CHECK(PKIX_PL_Malloc
                    (sizeof (OtherName), (void **)&otherName, plContext),
                    PKIX_MALLOCFAILED);

        /* make a copy of the name field */
        PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_CopyItem).\n");
        rv = SECITEM_CopyItem
                (NULL, &otherName->name, &nssAltName->name.OthName.name);
        if (rv != SECSuccess) {
                PKIX_ERROR(PKIX_OUTOFMEMORY);
        }

        /* make a copy of the oid field */
        PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_CopyItem).\n");
        rv = SECITEM_CopyItem
                (NULL, &otherName->oid, &nssAltName->name.OthName.oid);
        if (rv != SECSuccess) {
                PKIX_ERROR(PKIX_OUTOFMEMORY);
        }

        *pOtherName = otherName;

cleanup:

        if (otherName && PKIX_ERROR_RECEIVED){
                secItemName = otherName->name;
                secItemOID = otherName->oid;

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
                SECITEM_FreeItem(&secItemName, PR_FALSE);

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
                SECITEM_FreeItem(&secItemOID, PR_FALSE);

                PKIX_FREE(otherName);
                otherName = NULL;
        }

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_DirectoryName_Create
 * DESCRIPTION:
 *
 *  Creates a new X500Name which represents the directoryName component of the
 *  CERTGeneralName pointed to by "nssAltName" and stores it at "pX500Name".
 *
 * PARAMETERS:
 *  "nssAltName"
 *      Address of CERTGeneralName. Must be non-NULL.
 *  "pX500Name"
 *      Address where object 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 GeneralName 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_pl_DirectoryName_Create(
        CERTGeneralName *nssAltName,
        PKIX_PL_X500Name **pX500Name,
        void *plContext)
{
        PKIX_PL_X500Name *pkixDN = NULL;
        CERTName *dirName = NULL;
        PKIX_PL_String *pkixDNString = NULL;
        char *utf8String = NULL;
        PKIX_UInt32 utf8Length;

        PKIX_ENTER(GENERALNAME, "pkix_pl_DirectoryName_Create");
        PKIX_NULLCHECK_TWO(nssAltName, pX500Name);

        dirName = &nssAltName->name.directoryName;

        PKIX_CHECK(PKIX_PL_X500Name_CreateFromCERTName(NULL, dirName, 
                                                       &pkixDN, plContext),
                   PKIX_X500NAMECREATEFROMCERTNAMEFAILED);

        *pX500Name = pkixDN;

cleanup:

        PR_Free(utf8String);
        PKIX_DECREF(pkixDNString);

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_Create
 * DESCRIPTION:
 *
 *  Creates new GeneralName which represents the CERTGeneralName pointed to by
 *  "nssAltName" and stores it at "pGenName".
 *
 * PARAMETERS:
 *  "nssAltName"
 *      Address of CERTGeneralName. Must be non-NULL.
 *  "pGenName"
 *      Address where object 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 GeneralName 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_pl_GeneralName_Create(
        CERTGeneralName *nssAltName,
        PKIX_PL_GeneralName **pGenName,
        void *plContext)
{
        PKIX_PL_GeneralName *genName = NULL;
        PKIX_PL_X500Name *pkixDN = NULL;
        PKIX_PL_OID *pkixOID = NULL;
        OtherName *otherName = NULL;
        CERTGeneralNameList *nssGenNameList = NULL;
        CERTGeneralNameType nameType;
        SECItem *secItem = NULL;
        char *asciiName = NULL;
        SECStatus rv;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_Create");
        PKIX_NULLCHECK_TWO(nssAltName, pGenName);

        /* create a PKIX_PL_GeneralName object */
        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_GENERALNAME_TYPE,
                    sizeof (PKIX_PL_GeneralName),
                    (PKIX_PL_Object **)&genName,
                    plContext),
                    PKIX_COULDNOTCREATEOBJECT);

        nameType = nssAltName->type;

        /*
         * We use CERT_CreateGeneralNameList to create just one CERTGeneralName
         * item for memory allocation reason. If we want to just create one
         * item, we have to use the calling path CERT_NewGeneralName, then
         * CERT_CopyOneGeneralName. With this calling path, if we pass
         * the arena argument as NULL, in CERT_CopyOneGeneralName's subsequent
         * call to CERT_CopyName, it assumes arena should be valid, hence
         * segmentation error (not sure this is a NSS bug, certainly it is
         * not consistent). But on the other hand, we don't want to keep an
         * arena record here explicitely for every PKIX_PL_GeneralName.
         * So I concluded it is better to use CERT_CreateGeneralNameList,
         * which keeps an arena pointer in its data structure and also masks
         * out details calls from this libpkix level.
         */

        PKIX_GENERALNAME_DEBUG("\t\tCalling CERT_CreateGeneralNameList).\n");
        nssGenNameList = CERT_CreateGeneralNameList(nssAltName);

        if (nssGenNameList == NULL) {
                PKIX_ERROR(PKIX_CERTCREATEGENERALNAMELISTFAILED);
        }

        genName->nssGeneralNameList = nssGenNameList;

        /* initialize fields */
        genName->type = nameType;
        genName->directoryName = NULL;
        genName->OthName = NULL;
        genName->other = NULL;
        genName->oid = NULL;

        switch (nameType){
        case certOtherName:

                PKIX_CHECK(pkix_pl_OtherName_Create
                            (nssAltName, &otherName, plContext),
                            PKIX_OTHERNAMECREATEFAILED);

                genName->OthName = otherName;
                break;

        case certDirectoryName:

                PKIX_CHECK(pkix_pl_DirectoryName_Create
                            (nssAltName, &pkixDN, plContext),
                            PKIX_DIRECTORYNAMECREATEFAILED);

                genName->directoryName = pkixDN;
                break;
        case certRegisterID:

                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                            (&nssAltName->name.other, &asciiName, plContext),
                            PKIX_OIDBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_OID_Create(asciiName, &pkixOID, plContext),
                            PKIX_OIDCREATEFAILED);

                genName->oid = pkixOID;
                break;
        case certDNSName:
        case certEDIPartyName:
        case certIPAddress:
        case certRFC822Name:
        case certX400Address:
        case certURI:

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_AllocItem).\n");
                secItem = SECITEM_AllocItem(NULL, NULL, 0);
                if (secItem == NULL){
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                }

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_CopyItem).\n");
                rv = SECITEM_CopyItem(NULL, secItem, &nssAltName->name.other);
                if (rv != SECSuccess) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                }

                genName->other = secItem;
                break;
        default:
                PKIX_ERROR(PKIX_NAMETYPENOTSUPPORTED);
        }

        *pGenName = genName;
cleanup:

        PKIX_FREE(asciiName);

        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(genName);
                if (secItem){
                        PKIX_GENERALNAME_DEBUG
                                ("\t\tCalling SECITEM_FreeItem).\n");
                        SECITEM_FreeItem(secItem, PR_TRUE);
                        secItem = NULL;
                }
        }

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_ToString_Helper
 * DESCRIPTION:
 *
 *  Helper function that creates a string representation of the GeneralName
 *  pointed to by "name" and stores it at "pString" Different mechanisms are
 *  used to create the string, depending on the type of the GeneralName.
 *
 * PARAMETERS
 *  "name"
 *      Address of GeneralName whose string representation is desired.
 *      Must be non-NULL.
 *  "pString"
 *      Address where object 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 GeneralName 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_pl_GeneralName_ToString_Helper(
        PKIX_PL_GeneralName *name,
        PKIX_PL_String **pString,
        void *plContext)
{
        PKIX_PL_X500Name *pkixDN = NULL;
        PKIX_PL_OID *pkixOID = NULL;
        char *x400AsciiName = NULL;
        char *ediPartyName = NULL;
        char *asciiName = NULL;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_ToString_Helper");
        PKIX_NULLCHECK_TWO(name, pString);

        switch (name->type) {
        case certRFC822Name:
        case certDNSName:
        case certURI:
                /*
                 * Note that we can't use PKIX_ESCASCII here because
                 * name->other->data is not guaranteed to be null-terminated.
                 */

                PKIX_NULLCHECK_ONE(name->other);

                PKIX_CHECK(PKIX_PL_String_Create(PKIX_UTF8,
                                                (name->other)->data,
                                                (name->other)->len,
                                                pString,
                                                plContext),
                            PKIX_STRINGCREATEFAILED);
                break;
        case certEDIPartyName:
                /* XXX print out the actual bytes */
                ediPartyName = "EDIPartyName: <DER-encoded value>";
                PKIX_CHECK(PKIX_PL_String_Create(PKIX_ESCASCII,
                                                ediPartyName,
                                                0,
                                                pString,
                                                plContext),
                            PKIX_STRINGCREATEFAILED);
                break;
        case certX400Address:
                /* XXX print out the actual bytes */
                x400AsciiName = "X400Address: <DER-encoded value>";
                PKIX_CHECK(PKIX_PL_String_Create(PKIX_ESCASCII,
                                                x400AsciiName,
                                                0,
                                                pString,
                                                plContext),
                            PKIX_STRINGCREATEFAILED);
                break;
        case certIPAddress:
                PKIX_CHECK(pkix_pl_ipAddrBytes2Ascii
                            (name->other, &asciiName, plContext),
                            PKIX_IPADDRBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_String_Create(PKIX_ESCASCII,
                                                asciiName,
                                                0,
                                                pString,
                                                plContext),
                            PKIX_STRINGCREATEFAILED);
                break;
        case certOtherName:
                PKIX_NULLCHECK_ONE(name->OthName);

                /* we only print type-id - don't know how to print value */
                /* XXX print out the bytes of the value */
                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                            (&name->OthName->oid, &asciiName, plContext),
                            PKIX_OIDBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_String_Create
                            (PKIX_ESCASCII,
                            asciiName,
                            0,
                            pString,
                            plContext),
                            PKIX_STRINGCREATEFAILED);
                break;
        case certRegisterID:
                pkixOID = name->oid;
                PKIX_CHECK(PKIX_PL_Object_ToString
                            ((PKIX_PL_Object *)pkixOID, pString, plContext),
                            PKIX_OIDTOSTRINGFAILED);
                break;
        case certDirectoryName:
                pkixDN = name->directoryName;
                PKIX_CHECK(PKIX_PL_Object_ToString
                            ((PKIX_PL_Object *)pkixDN, pString, plContext),
                            PKIX_X500NAMETOSTRINGFAILED);
                break;
        default:
                PKIX_ERROR
                        (PKIX_TOSTRINGFORTHISGENERALNAMETYPENOTSUPPORTED);
        }

cleanup:

        PKIX_FREE(asciiName);

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_Destroy
 * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_GeneralName_Destroy(
        PKIX_PL_Object *object,
        void *plContext)
{
        PKIX_PL_GeneralName *name = NULL;
        SECItem secItemName;
        SECItem secItemOID;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_Destroy");
        PKIX_NULLCHECK_ONE(object);

        PKIX_CHECK(pkix_CheckType(object, PKIX_GENERALNAME_TYPE, plContext),
                    PKIX_OBJECTNOTGENERALNAME);

        name = (PKIX_PL_GeneralName *)object;

        PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
        SECITEM_FreeItem(name->other, PR_TRUE);
        name->other = NULL;

        if (name->OthName){
                secItemName = name->OthName->name;
                secItemOID = name->OthName->oid;

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
                SECITEM_FreeItem(&secItemName, PR_FALSE);

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
                SECITEM_FreeItem(&secItemOID, PR_FALSE);

                PKIX_FREE(name->OthName);
                name->OthName = NULL;
        }

        if (name->nssGeneralNameList != NULL) {
                PKIX_GENERALNAME_DEBUG
                        ("\t\tCalling CERT_DestroyGeneralNameList).\n");
                CERT_DestroyGeneralNameList(name->nssGeneralNameList);
        }

        PKIX_DECREF(name->directoryName);
        PKIX_DECREF(name->oid);

cleanup:

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_ToString
 * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_GeneralName_ToString(
        PKIX_PL_Object *object,
        PKIX_PL_String **pString,
        void *plContext)
{
        PKIX_PL_String *nameString = NULL;
        PKIX_PL_GeneralName *name = NULL;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_toString");
        PKIX_NULLCHECK_TWO(object, pString);

        PKIX_CHECK(pkix_CheckType(object, PKIX_GENERALNAME_TYPE, plContext),
                    PKIX_OBJECTNOTGENERALNAME);

        name = (PKIX_PL_GeneralName *)object;

        PKIX_CHECK(pkix_pl_GeneralName_ToString_Helper
                    (name, &nameString, plContext),
                    PKIX_GENERALNAMETOSTRINGHELPERFAILED);

        *pString = nameString;

cleanup:



        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_Hashcode
 * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_GeneralName_Hashcode(
        PKIX_PL_Object *object,
        PKIX_UInt32 *pHashcode,
        void *plContext)
{
        PKIX_PL_GeneralName *name = NULL;
        PKIX_UInt32 firstHash, secondHash, nameHash;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_Hashcode");
        PKIX_NULLCHECK_TWO(object, pHashcode);

        PKIX_CHECK(pkix_CheckType(object, PKIX_GENERALNAME_TYPE, plContext),
                    PKIX_OBJECTNOTGENERALNAME);

        name = (PKIX_PL_GeneralName *)object;

        switch (name->type) {
        case certRFC822Name:
        case certDNSName:
        case certX400Address:
        case certEDIPartyName:
        case certURI:
        case certIPAddress:
                PKIX_NULLCHECK_ONE(name->other);
                PKIX_CHECK(pkix_hash
                            ((const unsigned char *)
                            name->other->data,
                            name->other->len,
                            &nameHash,
                            plContext),
                            PKIX_HASHFAILED);
                break;
        case certRegisterID:
                PKIX_CHECK(PKIX_PL_Object_Hashcode
                            ((PKIX_PL_Object *)name->oid,
                            &nameHash,
                            plContext),
                            PKIX_OIDHASHCODEFAILED);
                break;
        case certOtherName:
                PKIX_NULLCHECK_ONE(name->OthName);
                PKIX_CHECK(pkix_hash
                            ((const unsigned char *)
                            name->OthName->oid.data,
                            name->OthName->oid.len,
                            &firstHash,
                            plContext),
                            PKIX_HASHFAILED);

                PKIX_CHECK(pkix_hash
                            ((const unsigned char *)
                            name->OthName->name.data,
                            name->OthName->name.len,
                            &secondHash,
                            plContext),
                            PKIX_HASHFAILED);

                nameHash = firstHash + secondHash;
                break;
        case certDirectoryName:
                PKIX_CHECK(PKIX_PL_Object_Hashcode
                            ((PKIX_PL_Object *)
                            name->directoryName,
                            &nameHash,
                            plContext),
                            PKIX_X500NAMEHASHCODEFAILED);
                break;
        }

        *pHashcode = nameHash;

cleanup:

        PKIX_RETURN(GENERALNAME);

}

/*
 * FUNCTION: pkix_pl_GeneralName_Equals
 * (see comments for PKIX_PL_Equals_Callback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_GeneralName_Equals(
        PKIX_PL_Object *firstObject,
        PKIX_PL_Object *secondObject,
        PKIX_Boolean *pResult,
        void *plContext)
{
        PKIX_PL_GeneralName *firstName = NULL;
        PKIX_PL_GeneralName *secondName = NULL;
        PKIX_UInt32 secondType;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_Equals");
        PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult);

        /* test that firstObject is a GeneralName */
        PKIX_CHECK(pkix_CheckType
                    (firstObject, PKIX_GENERALNAME_TYPE, plContext),
                    PKIX_FIRSTOBJECTNOTGENERALNAME);

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

        /*
         * If secondObject isn't a GeneralName, we don't throw an error.
         * We simply return a Boolean result of FALSE
         */
        *pResult = PKIX_FALSE;
        PKIX_CHECK(PKIX_PL_Object_GetType
                    (secondObject, &secondType, plContext),
                    PKIX_COULDNOTGETTYPEOFSECONDARGUMENT);
        if (secondType != PKIX_GENERALNAME_TYPE){
                goto cleanup;
        }

        firstName = (PKIX_PL_GeneralName *)firstObject;
        secondName = (PKIX_PL_GeneralName *)secondObject;

        if (firstName->type != secondName->type){
                goto cleanup;
        }

        switch (firstName->type) {
        case certRFC822Name:
        case certDNSName:
        case certX400Address:
        case certEDIPartyName:
        case certURI:
        case certIPAddress:
                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_CompareItem).\n");
                if (SECITEM_CompareItem(firstName->other,
                                        secondName->other) != SECEqual) {
                        goto cleanup;
                }
                break;
        case certRegisterID:
                PKIX_CHECK(PKIX_PL_Object_Equals
                            ((PKIX_PL_Object *)firstName->oid,
                            (PKIX_PL_Object *)secondName->oid,
                            pResult,
                            plContext),
                            PKIX_OIDEQUALSFAILED);
                goto cleanup;
        case certOtherName:
                PKIX_NULLCHECK_TWO(firstName->OthName, secondName->OthName);
                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_CompareItem).\n");
                if (SECITEM_CompareItem(&firstName->OthName->oid,
                                        &secondName->OthName->oid)
                    != SECEqual ||
                    SECITEM_CompareItem(&firstName->OthName->name,
                                        &secondName->OthName->name)
                    != SECEqual) {
                        goto cleanup;
                }
                break;
        case certDirectoryName:
                PKIX_CHECK(PKIX_PL_Object_Equals
                            ((PKIX_PL_Object *)firstName->directoryName,
                            (PKIX_PL_Object *)secondName->directoryName,
                            pResult,
                            plContext),
                            PKIX_X500NAMEEQUALSFAILED);
                goto cleanup;
        }

        *pResult = PKIX_TRUE;

cleanup:

        PKIX_RETURN(GENERALNAME);
}

/*
 * FUNCTION: pkix_pl_GeneralName_RegisterSelf
 * DESCRIPTION:
 *  Registers PKIX_GENERALNAME_TYPE and 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_pl_GeneralName_RegisterSelf(void *plContext)
{

        extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
        pkix_ClassTable_Entry entry;

        PKIX_ENTER(GENERALNAME, "pkix_pl_GeneralName_RegisterSelf");

        entry.description = "GeneralName";
        entry.objCounter = 0;
        entry.typeObjectSize = sizeof(PKIX_PL_GeneralName);
        entry.destructor = pkix_pl_GeneralName_Destroy;
        entry.equalsFunction = pkix_pl_GeneralName_Equals;
        entry.hashcodeFunction = pkix_pl_GeneralName_Hashcode;
        entry.toStringFunction = pkix_pl_GeneralName_ToString;
        entry.comparator = NULL;
        entry.duplicateFunction = pkix_duplicateImmutable;

        systemClasses[PKIX_GENERALNAME_TYPE] = entry;

        PKIX_RETURN(GENERALNAME);
}

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

#ifdef BUILD_LIBPKIX_TESTS
/*
 * FUNCTION: PKIX_PL_GeneralName_Create (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_GeneralName_Create(
        PKIX_UInt32 nameType,
        PKIX_PL_String *stringRep,
        PKIX_PL_GeneralName **pGName,
        void *plContext)
{
        PKIX_PL_X500Name *pkixDN = NULL;
        PKIX_PL_OID *pkixOID = NULL;
        SECItem *secItem = NULL;
        char *asciiString = NULL;
        PKIX_UInt32 length = 0;
        PKIX_PL_GeneralName *genName = NULL;
        CERTGeneralName *nssGenName = NULL;
        CERTGeneralNameList *nssGenNameList = NULL;
        CERTName *nssCertName = NULL;
        PLArenaPool *arena = NULL;

        PKIX_ENTER(GENERALNAME, "PKIX_PL_GeneralName_Create");
        PKIX_NULLCHECK_TWO(pGName, stringRep);

        PKIX_CHECK(PKIX_PL_String_GetEncoded
                    (stringRep,
                    PKIX_ESCASCII,
                    (void **)&asciiString,
                    &length,
                    plContext),
                    PKIX_STRINGGETENCODEDFAILED);

        /* Create a temporary CERTGeneralName */
        PKIX_GENERALNAME_DEBUG("\t\tCalling PL_strlen).\n");
        length = PL_strlen(asciiString);
        PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_AllocItem).\n");
        secItem = SECITEM_AllocItem(NULL, NULL, length);
        PKIX_GENERALNAME_DEBUG("\t\tCalling PORT_Memcpy).\n");
        (void) PORT_Memcpy(secItem->data, asciiString, length);
        PKIX_CERT_DEBUG("\t\tCalling PORT_NewArena).\n");
        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
        if (arena == NULL) {
                PKIX_ERROR(PKIX_OUTOFMEMORY);
        }
        PKIX_GENERALNAME_DEBUG("\t\tCalling CERT_NewGeneralName).\n");
        nssGenName = CERT_NewGeneralName(arena, nameType);
        if (nssGenName == NULL) {
                PKIX_ERROR(PKIX_ALLOCATENEWCERTGENERALNAMEFAILED);
        }

        switch (nameType) {
        case certRFC822Name:
        case certDNSName:
        case certURI:
                nssGenName->name.other = *secItem;
                break;

        case certDirectoryName:

                PKIX_CHECK(PKIX_PL_X500Name_Create
                            (stringRep, &pkixDN, plContext),
                            PKIX_X500NAMECREATEFAILED);

                PKIX_GENERALNAME_DEBUG("\t\tCalling CERT_AsciiToName).\n");
                nssCertName = CERT_AsciiToName(asciiString);
                nssGenName->name.directoryName = *nssCertName;
                break;

        case certRegisterID:
                PKIX_CHECK(PKIX_PL_OID_Create
                            (asciiString, &pkixOID, plContext),
                            PKIX_OIDCREATEFAILED);
                nssGenName->name.other = *secItem;
                break;
        default:
                /* including IPAddress, EDIPartyName, OtherName, X400Address */
                PKIX_ERROR(PKIX_UNABLETOCREATEGENERALNAMEOFTHISTYPE);
        }

        /* create a PKIX_PL_GeneralName object */
        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_GENERALNAME_TYPE,
                    sizeof (PKIX_PL_GeneralName),
                    (PKIX_PL_Object **)&genName,
                    plContext),
                    PKIX_COULDNOTCREATEOBJECT);

        /* create a CERTGeneralNameList */
        nssGenName->type = nameType;
        PKIX_GENERALNAME_DEBUG("\t\tCalling CERT_CreateGeneralNameList).\n");
        nssGenNameList = CERT_CreateGeneralNameList(nssGenName);
        if (nssGenNameList == NULL) {
                PKIX_ERROR(PKIX_CERTCREATEGENERALNAMELISTFAILED);
        }
        genName->nssGeneralNameList = nssGenNameList;

        /* initialize fields */
        genName->type = nameType;
        genName->directoryName = pkixDN;
        genName->OthName = NULL;
        genName->other = secItem;
        genName->oid = pkixOID;

        *pGName = genName;
cleanup:

        PKIX_FREE(asciiString);

        if (nssCertName != NULL) {
                PKIX_CERT_DEBUG("\t\tCalling CERT_DestroyName).\n");
                CERT_DestroyName(nssCertName);
        }

        if (arena){ /* will free nssGenName */
                PKIX_CERT_DEBUG("\t\tCalling PORT_FreeArena).\n");
                PORT_FreeArena(arena, PR_FALSE);
        }

        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(pkixDN);
                PKIX_DECREF(pkixOID);

                PKIX_GENERALNAME_DEBUG("\t\tCalling SECITEM_FreeItem).\n");
                if (secItem){
                        SECITEM_FreeItem(secItem, PR_TRUE);
                        secItem = NULL;
                }
        }

        PKIX_RETURN(GENERALNAME);
}

#endif /* BUILD_LIBPKIX_TESTS */
