/* ***** 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 Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Sun Microsystems
 *
 * 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_x500name.c
 *
 * X500Name Object Functions
 *
 */

#include "pkix_pl_x500name.h"

/* --Private-X500Name-Functions------------------------------------- */

/*
 * FUNCTION: pkix_pl_X500Name_CompareDERBytes
 * DESCRIPTION:
 *
 *  Checks whether the DER encoding of the X500Name pointed to by
 *  "firstX500Name" is byte-for-byte equal with the DER encoding of the
 *  X500Name pointed to by "secondX500Name" and stores the Boolean result at
 *  "pResult".
 *
 * PARAMETERS
 *  "firstX500Name"
 *      Address of first X500Name. Must be non-NULL.
 *  "secondX500Name"
 *      Address of second X500Name. Must be non-NULL.
 *  "pResult"
 *      Address where Boolean 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 X500Name 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_X500Name_CompareDERBytes(
        PKIX_PL_X500Name *firstX500Name,
        PKIX_PL_X500Name *secondX500Name,
        PKIX_Boolean *pResult,
        void *plContext)
{
        CERTName *firstName = NULL;
        CERTName *secondName = NULL;
        CERTRDN **ardns = NULL;
        CERTRDN **brdns = NULL;
        CERTRDN *ardn = NULL;
        CERTRDN *brdn = NULL;
        CERTAVA **aavas = NULL;
        CERTAVA **bavas = NULL;
        CERTAVA *aava = NULL;
        CERTAVA *bava = NULL;
        PKIX_UInt32 ac, bc;
        SECComparison rv;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_CompareDERBytes");
        PKIX_NULLCHECK_THREE(firstX500Name, secondX500Name, pResult);

        firstName = firstX500Name->nssDN;
        secondName = secondX500Name->nssDN;

        PKIX_NULLCHECK_TWO(firstName, secondName);

        ardns = firstName->rdns;
        brdns = secondName->rdns;

        /* if array of rdn's not same length, names can't be equal */
        ac = pkix_countArray((void**) ardns);
        bc = pkix_countArray((void**) brdns);
        if (ac != bc){
                *pResult = PKIX_FALSE;
                goto cleanup;
        }

        for (;;) {
                PKIX_NULLCHECK_TWO(ardns, brdns);
                ardn = *ardns++;
                brdn = *brdns++;
                if (!ardn) {
                        break;
                }

                PKIX_NULLCHECK_TWO(ardn, brdn);
                aavas = ardn->avas;
                bavas = brdn->avas;

                /* if array of ava's not same length, names can't be equal */
                ac = pkix_countArray((void**) aavas);
                bc = pkix_countArray((void**) bavas);
                if (ac != bc){
                        *pResult = PKIX_FALSE;
                        goto cleanup;
                }

                for (;;) {
                        PKIX_NULLCHECK_TWO(aavas, bavas);
                        aava = *aavas++;
                        bava = *bavas++;
                        if (!aava) {
                                break;
                        }

                        PKIX_X500NAME_DEBUG("\t\tCalling "
                                            "SECITEM_CompareItem).\n");
                        rv = SECITEM_CompareItem(&aava->type, &bava->type);
                        if (rv != SECEqual){
                                *pResult = PKIX_FALSE;
                                goto cleanup;
                        }

                        PKIX_X500NAME_DEBUG("\t\tCalling "
                                            "SECITEM_CompareItem).\n");
                        rv = SECITEM_CompareItem(&aava->value, &bava->value);
                        if (rv != SECEqual){
                                *pResult = PKIX_FALSE;
                                goto cleanup;
                        }
                }
        }

        *pResult = (rv == SECEqual);

cleanup:

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_ToString_Helper
 * DESCRIPTION:
 *
 *  Helper function that creates a string representation of the X500Name
 *  pointed to by "name" and stores it at "pString".
 *
 * PARAMETERS
 *  "name"
 *      Address of X500Name 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 X500Name 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_X500Name_ToString_Helper(
        PKIX_PL_X500Name *name,
        PKIX_PL_String **pString,
        void *plContext)
{
        CERTName *nssDN = NULL;
        char *utf8String = NULL;
        PKIX_UInt32 utf8Length;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_ToString_Helper");
        PKIX_NULLCHECK_TWO(name, pString);
        nssDN = name->nssDN;

        PKIX_X500NAME_DEBUG("\t\tCalling CERT_NameToAscii).\n");
        /* this should really be called CERT_NameToUTF8 */
        utf8String = CERT_NameToAscii(nssDN);
        if (!utf8String){
                PKIX_ERROR(PKIX_CERTNAMETOASCIIFAILED);
        }

        PKIX_X500NAME_DEBUG("\t\tCalling PL_strlen).\n");
        utf8Length = PL_strlen(utf8String);

        PKIX_CHECK(PKIX_PL_String_Create
                    (PKIX_UTF8, utf8String, utf8Length, pString, plContext),
                    PKIX_STRINGCREATEFAILED);

cleanup:

        PR_Free(utf8String);

        PKIX_RETURN(X500NAME);
}

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

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_Destroy");
        PKIX_NULLCHECK_ONE(object);

        PKIX_CHECK(pkix_CheckType(object, PKIX_X500NAME_TYPE, plContext),
                    PKIX_OBJECTNOTANX500NAME);

        name = (PKIX_PL_X500Name *)object;

        PKIX_X500NAME_DEBUG("\t\tCalling CERT_DestroyName).\n");
        CERT_DestroyName(name->nssDN);
        name->nssDN = NULL;

cleanup:

        PKIX_RETURN(X500NAME);
}

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

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_toString");
        PKIX_NULLCHECK_TWO(object, pString);

        PKIX_CHECK(pkix_CheckType(object, PKIX_X500NAME_TYPE, plContext),
                    PKIX_OBJECTNOTANX500NAME);

        name = (PKIX_PL_X500Name *)object;

        PKIX_CHECK(pkix_pl_X500Name_ToString_Helper
                    (name, &nameString, plContext),
                    PKIX_X500NAMETOSTRINGHELPERFAILED);

        *pString = nameString;

cleanup:

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_Hashcode
 * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_X500Name_Hashcode(
        PKIX_PL_Object *object,
        PKIX_UInt32 *pHashcode,
        void *plContext)
{
        PKIX_PL_X500Name *name = NULL;
        SECItem *resultSecItem = NULL;
        PRArenaPool *arena = NULL;
        CERTName *nssDN = NULL;
        PKIX_UInt32 nameHash;
        SECItem *derBytes;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_Hashcode");
        PKIX_NULLCHECK_TWO(object, pHashcode);

        PKIX_CHECK(pkix_CheckType(object, PKIX_X500NAME_TYPE, plContext),
                    PKIX_OBJECTNOTANX500NAME);

        name = (PKIX_PL_X500Name *)object;

        /* we hash over the bytes in the DER encoding */

        nssDN = name->nssDN;

        PKIX_X500NAME_DEBUG("\t\tCalling PORT_NewArena).\n");
        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
        if (arena == NULL) {
                PKIX_ERROR(PKIX_PORTNEWARENAFAILED);
        }

        PKIX_X500NAME_DEBUG("\t\tCalling PORT_ArenaZNew).\n");
        derBytes = PORT_ArenaZNew(arena, SECItem);
        if (derBytes == NULL) {
                PKIX_ERROR(PKIX_PORTARENAZNEWFAILED);
        }

        PKIX_X500NAME_DEBUG("\t\tCalling SEC_ASN1EncodeItem).\n");
        resultSecItem =
                SEC_ASN1EncodeItem(arena, derBytes, nssDN, CERT_NameTemplate);

        if (resultSecItem == NULL){
                PKIX_ERROR(PKIX_SECASN1ENCODEITEMFAILED);
        }

        PKIX_CHECK(pkix_hash
                    (derBytes->data, derBytes->len, &nameHash, plContext),
                    PKIX_HASHFAILED);

        *pHashcode = nameHash;

cleanup:

        if (arena){
                /* Note that freeing the arena also frees derBytes */
                PKIX_X500NAME_DEBUG("\t\tCalling PORT_FreeArena).\n");
                PORT_FreeArena(arena, PR_FALSE);
        }

        PKIX_RETURN(X500NAME);
}


/*
 * FUNCTION: pkix_pl_X500Name_Equals
 * (see comments for PKIX_PL_Equals_Callback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_X500Name_Equals(
        PKIX_PL_Object *firstObject,
        PKIX_PL_Object *secondObject,
        PKIX_Boolean *pResult,
        void *plContext)
{
        PKIX_PL_X500Name *firstX500Name = NULL;
        PKIX_PL_X500Name *secondX500Name = NULL;
        PKIX_UInt32 secondType;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_Equals");
        PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult);

        /* test that firstObject is an X500Name */
        PKIX_CHECK(pkix_CheckType(firstObject, PKIX_X500NAME_TYPE, plContext),
                    PKIX_FIRSTOBJECTARGUMENTNOTANX500NAME);

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

        /*
         * If secondObject isn't an X500Name, 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_X500NAME_TYPE) goto cleanup;

        firstX500Name = (PKIX_PL_X500Name *)firstObject;
        secondX500Name = (PKIX_PL_X500Name *)secondObject;

        /* we simply do byte comparison on DER encodings of DN's */
        PKIX_CHECK(pkix_pl_X500Name_CompareDERBytes
                    (firstX500Name, secondX500Name, pResult, plContext),
                    PKIX_X500NAMECOMPAREDERBYTESFAILED);

cleanup:

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_RegisterSelf
 * DESCRIPTION:
 *  Registers PKIX_X500NAME_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_pl_X500Name_RegisterSelf(void *plContext)
{

        extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
        pkix_ClassTable_Entry entry;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_RegisterSelf");

        entry.description = "X500Name";
        entry.destructor = pkix_pl_X500Name_Destroy;
        entry.equalsFunction = pkix_pl_X500Name_Equals;
        entry.hashcodeFunction = pkix_pl_X500Name_Hashcode;
        entry.toStringFunction = pkix_pl_X500Name_ToString;
        entry.comparator = NULL;
        entry.duplicateFunction = pkix_duplicateImmutable;

        systemClasses[PKIX_X500NAME_TYPE] = entry;

        PKIX_RETURN(X500NAME);
}

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

/*
 * FUNCTION: PKIX_PL_X500Name_Create (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_X500Name_Create(
        PKIX_PL_String *stringRep,
        PKIX_PL_X500Name **pName,
        void *plContext)
{
        PKIX_PL_X500Name *x500Name = NULL;
        CERTName *nssDN = NULL;
        char *utf8String = NULL;
        PKIX_UInt32 utf8Length;

        PKIX_ENTER(X500NAME, "PKIX_PL_X500Name_Create()");
        PKIX_NULLCHECK_TWO(pName, stringRep);

        /*
         * convert the input PKIX_PL_String to PKIX_UTF8_NULL_TERM.
         * we need to use this format specifier because
         * CERT_AsciiToName expects a NULL-terminated UTF8 string.
         * Since UTF8 allow NUL characters in the middle of the
         * string, this is buggy. However, as a workaround, using
         * PKIX_UTF8_NULL_TERM gives us a NULL-terminated UTF8 string.
         */

        PKIX_CHECK(PKIX_PL_String_GetEncoded
                    (stringRep,
                    PKIX_UTF8_NULL_TERM,
                    (void **)&utf8String,
                    &utf8Length,
                    plContext),
                    PKIX_STRINGGETENCODEDFAILED);

        PKIX_X500NAME_DEBUG("\t\tCalling CERT_AsciiToName).\n");
        /* this should be really be called CERT_UTF8ToName */
        nssDN = CERT_AsciiToName(utf8String);
        if (nssDN == NULL) {
                PKIX_ERROR(PKIX_COULDNOTCREATENSSDN);
        }

        /* create a PKIX_PL_X500Name object */
        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_X500NAME_TYPE,
                    sizeof (PKIX_PL_X500Name),
                    (PKIX_PL_Object **)&x500Name,
                    plContext),
                    PKIX_COULDNOTCREATEX500NAMEOBJECT);

        /* populate the nssDN field */
        x500Name->nssDN = nssDN;

        *pName = x500Name;

cleanup:

        PKIX_FREE(utf8String);

        if (nssDN && PKIX_ERROR_RECEIVED){
                PKIX_X500NAME_DEBUG("\t\tCalling CERT_DestroyName).\n");
                CERT_DestroyName(nssDN);
                nssDN = NULL;
        }

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: PKIX_PL_X500Name_Match (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_X500Name_Match(
        PKIX_PL_X500Name *firstX500Name,
        PKIX_PL_X500Name *secondX500Name,
        PKIX_Boolean *pResult,
        void *plContext)
{
        CERTName *firstName = NULL;
        CERTName *secondName = NULL;
        SECComparison cmpResult;

        PKIX_ENTER(X500NAME, "PKIX_PL_X500Name_Equals");
        PKIX_NULLCHECK_THREE(firstX500Name, secondX500Name, pResult);

        if (firstX500Name == secondX500Name){
                *pResult = PKIX_TRUE;
                goto cleanup;
        }

        firstName = firstX500Name->nssDN;
        secondName = secondX500Name->nssDN;

        PKIX_NULLCHECK_TWO(firstName, secondName);

        PKIX_X500NAME_DEBUG("\t\tCalling CERT_CompareName).\n");
        cmpResult = CERT_CompareName(firstName, secondName);

        *pResult = (cmpResult == SECEqual);

cleanup:

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_GetSECName
 *
 * DESCRIPTION:
 *  Encodes as a SECItem the CERTName embodied by the X500Name object pointed
 *  to by "xname", using the arena pointed to by "arena", and stores the result
 *  at "pSECName". If the name cannot be successfully encoded, NULL is stored
 *  at "pSECName".
 *
 * PARAMETERS:
 *  "xname"
 *      Address of X500Name whose CERTName flag is to be encoded. Must be
 *      non-NULL.
 *  "arena"
 *      Address of the PRArenaPool to be used in the encoding, and in which
 *      "pSECName" will be allocated. Must be non-NULL.
 *  "pSECName"
 *      Address where result 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 Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_X500Name_GetSECName(
        PKIX_PL_X500Name *xname,
        PRArenaPool *arena,
        SECItem **pSECName,
        void *plContext)
{

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_GetCertName");

        PKIX_NULLCHECK_THREE(xname, arena, pSECName);

        /*
         * SEC_ASN1EncodeItem returns NULL if unsuccessful. We just
         * store the NULL result.
         */
        PKIX_PL_NSSCALLRV(X500NAME, *pSECName, SEC_ASN1EncodeItem,
                (arena, NULL, (void *)xname->nssDN, CERT_NameTemplate));

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_CreateFromUtf8
 *
 * DESCRIPTION:
 *  Creates an X500Name object from the RFC1485 string representation pointed
 *  to by "stringRep", and stores the result at "pName". If the string cannot
 *  be successfully converted, a non-fatal error is returned.
 *
 * PARAMETERS:
 *  "stringRep"
 *      Address of the RFC1485 string to be converted. Must be non-NULL.
 *  "pName"
 *      Address where the X500Name result 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 an X500NAME 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_X500Name_CreateFromUtf8(
        char *stringRep,
        PKIX_PL_X500Name **pName,
        void *plContext)
{
        PKIX_PL_X500Name *x500Name = NULL;
        CERTName *nssDN = NULL;

        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_CreateFromUtf8");
        PKIX_NULLCHECK_TWO(pName, stringRep);

        PKIX_PL_NSSCALLRV(X500NAME, nssDN, CERT_AsciiToName, (stringRep));

        if (nssDN == NULL) {
                PKIX_ERROR(PKIX_COULDNOTCREATENSSDN);
        }

        /* create a PKIX_PL_X500Name object */
        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_X500NAME_TYPE,
                    sizeof (PKIX_PL_X500Name),
                    (PKIX_PL_Object **)&x500Name,
                    plContext),
                    PKIX_COULDNOTCREATEX500NAMEOBJECT);

        /* populate the nssDN field */
        x500Name->nssDN = nssDN;

        *pName = x500Name;

cleanup:

        if (nssDN && PKIX_ERROR_RECEIVED){
                PKIX_X500NAME_DEBUG("\t\tCalling CERT_DestroyName).\n");
                CERT_DestroyName(nssDN);
                nssDN = NULL;
        }

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_GetCommonName
 *
 * DESCRIPTION:
 *  Extracts the CommonName component of the X500Name object pointed to by
 *  "xname", and stores the result at "pCommonName". If the CommonName cannot
 *  be successfully extracted, NULL is stored at "pCommonName".
 *
 *  The returned string must be freed with PORT_Free.
 *
 * PARAMETERS:
 *  "xname"
 *      Address of X500Name whose CommonName is to be extracted. Must be
 *      non-NULL.
 *  "pCommonName"
 *      Address where result 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 Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_X500Name_GetCommonName(
        PKIX_PL_X500Name *xname,
        unsigned char **pCommonName,
        void *plContext)
{
        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_GetCommonName");
        PKIX_NULLCHECK_TWO(xname, pCommonName);

        PKIX_PL_NSSCALLRV
                (X500NAME,
                *pCommonName,
                (unsigned char *)CERT_GetCommonName,
                (xname->nssDN));

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_GetCountryName
 *
 * DESCRIPTION:
 *  Extracts the CountryName component of the X500Name object pointed to by
 *  "xname", and stores the result at "pCountryName". If the CountryName cannot
 *  be successfully extracted, NULL is stored at "pCountryName".
 *
 *  The returned string must be freed with PORT_Free.
 *
 * PARAMETERS:
 *  "xname"
 *      Address of X500Name whose CountryName is to be extracted. Must be
 *      non-NULL.
 *  "pCountryName"
 *      Address where result 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 Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_X500Name_GetCountryName(
        PKIX_PL_X500Name *xname,
        unsigned char **pCountryName,
        void *plContext)
{
        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_GetCountryName");
        PKIX_NULLCHECK_TWO(xname, pCountryName);

        PKIX_PL_NSSCALLRV
                (X500NAME,
                *pCountryName,
                (unsigned char *)CERT_GetCountryName,
                (xname->nssDN));

        PKIX_RETURN(X500NAME);
}

/*
 * FUNCTION: pkix_pl_X500Name_GetOrgName
 *
 * DESCRIPTION:
 *  Extracts the OrganizationName component of the X500Name object pointed to by
 *  "xname", and stores the result at "pOrgName". If the OrganizationName cannot
 *  be successfully extracted, NULL is stored at "pOrgName".
 *
 *  The returned string must be freed with PORT_Free.
 *
 * PARAMETERS:
 *  "xname"
 *      Address of X500Name whose OrganizationName is to be extracted. Must be
 *      non-NULL.
 *  "pOrgName"
 *      Address where result 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 Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_X500Name_GetOrgName(
        PKIX_PL_X500Name *xname,
        unsigned char **pOrgName,
        void *plContext)
{
        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_GetOrgName");
        PKIX_NULLCHECK_TWO(xname, pOrgName);

        PKIX_PL_NSSCALLRV
                (X500NAME,
                *pOrgName,
                (unsigned char *)CERT_GetOrgName,
                (xname->nssDN));

        PKIX_RETURN(X500NAME);
}
PKIX_Error *
pkix_pl_X500Name_GetCERTName(
        PKIX_PL_X500Name *xname,
        CERTName **pCERTName,
        void *plContext)
{
        PKIX_ENTER(X500NAME, "pkix_pl_X500Name_GetCERTName");
        PKIX_NULLCHECK_TWO(xname, pCERTName);

        *pCERTName = xname->nssDN;

        PKIX_RETURN(X500NAME);
}
