/*
 * FUNCTION: pkix_EkuChecker_Create
 * DESCRIPTION:
 *
 *  Creates a new Extend Key Usage CheckerState using "params" to retrieve
 *  application specified EKU for verification and stores it at "pState".
 *
 * PARAMETERS:
 *  "params"
 *      a PKIX_ProcessingParams links to PKIX_ComCertSelParams where a list of
 *      Extended Key Usage OIDs specified by application can be retrieved for
 *      verification.
 *  "pState"
 *      Address where state 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 UserDefinedModules 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_EkuChecker_Create(
        PKIX_ProcessingParams *params,
        pkix_EkuChecker **pState,
        void *plContext)
{
        pkix_EkuChecker *state = NULL;
        PKIX_CertSelector *certSelector = NULL;
        PKIX_ComCertSelParams *comCertSelParams = NULL;
        PKIX_List *requiredOids = NULL;

        PKIX_ENTER(EKUCHECKER, "pkix_EkuChecker_Create");
        PKIX_NULLCHECK_TWO(params, pState);

        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_EKUCHECKER_TYPE,
                    sizeof (pkix_EkuChecker),
                    (PKIX_PL_Object **)&state,
                    plContext),
                    PKIX_COULDNOTCREATEEKUCHECKERSTATEOBJECT);


        PKIX_CHECK(PKIX_ProcessingParams_GetTargetCertConstraints
                    (params, &certSelector, plContext),
                    PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED);

        if (certSelector != NULL) {

            /* Get initial EKU OIDs from ComCertSelParams, if set */
            PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams
                       (certSelector, &comCertSelParams, plContext),
                       PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED);

            if (comCertSelParams != NULL) {
                PKIX_CHECK(PKIX_ComCertSelParams_GetExtendedKeyUsage
                           (comCertSelParams, &requiredOids, plContext),
                           PKIX_COMCERTSELPARAMSGETEXTENDEDKEYUSAGEFAILED);

            }
        }

        PKIX_CHECK(PKIX_PL_OID_Create
                    (PKIX_EXTENDEDKEYUSAGE_OID,
                    &state->ekuOID,
                    plContext),
                    PKIX_OIDCREATEFAILED);

        state->requiredExtKeyUsageOids = requiredOids;
        requiredOids = NULL;
        *pState = state;
        state = NULL;

cleanup:

        PKIX_DECREF(certSelector);
        PKIX_DECREF(comCertSelParams);
        PKIX_DECREF(requiredOids);
        PKIX_DECREF(state);

        PKIX_RETURN(EKUCHECKER);
}
/*
 * FUNCTION: pkix_pl_LdapCertStore_GetCert
 *  (see description of PKIX_CertStore_CertCallback in pkix_certstore.h)
 */
PKIX_Error *
pkix_pl_LdapCertStore_GetCert(
        PKIX_CertStore *store,
        PKIX_CertSelector *selector,
        PKIX_VerifyNode *verifyNode,
        void **pNBIOContext,
        PKIX_List **pCertList,
        void *plContext)
{
        PRArenaPool *requestArena = NULL;
        LDAPRequestParams requestParams;
        void *pollDesc = NULL;
        PKIX_Int32 minPathLen = 0;
        PKIX_Boolean cacheFlag = PKIX_FALSE;
        PKIX_ComCertSelParams *params = NULL;
        PKIX_PL_LdapCertStoreContext *lcs = NULL;
        PKIX_List *responses = NULL;
        PKIX_List *unfilteredCerts = NULL;
        PKIX_List *filteredCerts = NULL;
        PKIX_PL_X500Name *subjectName = 0;

        PKIX_ENTER(CERTSTORE, "pkix_pl_LdapCertStore_GetCert");
        PKIX_NULLCHECK_THREE(store, selector, pCertList);

        requestParams.baseObject = "c=US";
        requestParams.scope = WHOLE_SUBTREE;
        requestParams.derefAliases = NEVER_DEREF;
        requestParams.sizeLimit = 0;
        requestParams.timeLimit = 0;

        /* Prepare elements for request filter */

        /*
         * Get a short-lived arena. We'll be done with this space once
         * the request is encoded.
         */
        requestArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
        if (!requestArena) {
                PKIX_ERROR_FATAL(PKIX_OUTOFMEMORY);
        }

        PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams
                (selector, &params, plContext),
                PKIX_CERTSELECTORGETCOMCERTSELPARAMSFAILED);

        /*
         * If we have the subject name for the desired subject,
         * ask the server for Certs with that subject.
         */
        PKIX_CHECK(PKIX_ComCertSelParams_GetSubject
                (params, &subjectName, plContext),
                PKIX_COMCERTSELPARAMSGETSUBJECTFAILED);

        PKIX_CHECK(PKIX_ComCertSelParams_GetBasicConstraints
                (params, &minPathLen, plContext),
                PKIX_COMCERTSELPARAMSGETBASICCONSTRAINTSFAILED);

        if (subjectName) {
                PKIX_CHECK(pkix_pl_LdapCertStore_MakeNameAVAList
                        (requestArena,
                        subjectName,
                        &(requestParams.nc),
                        plContext),
                        PKIX_LDAPCERTSTOREMAKENAMEAVALISTFAILED);

                if (*requestParams.nc == NULL) {
                        /*
                         * The subjectName may not include any components
                         * that we know how to encode. We do not return
                         * an error, because the caller did not necessarily
                         * do anything wrong, but we return an empty List.
                         */
                        PKIX_PL_NSSCALL(CERTSTORE, PORT_FreeArena,
                                (requestArena, PR_FALSE));

                        PKIX_CHECK(PKIX_List_Create(&filteredCerts, plContext),
                                PKIX_LISTCREATEFAILED);

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

                        *pNBIOContext = NULL;
                        *pCertList = filteredCerts;
                        filteredCerts = NULL;
                        goto cleanup;
                }
        } else {
                PKIX_ERROR(PKIX_INSUFFICIENTCRITERIAFORCERTQUERY);
        }

        /* Prepare attribute field of request */

        requestParams.attributes = 0;

        if (minPathLen < 0) {
                requestParams.attributes |= LDAPATTR_USERCERT;
        }

        if (minPathLen > -2) {
                requestParams.attributes |=
                        LDAPATTR_CACERT | LDAPATTR_CROSSPAIRCERT;
        }

        /* All request fields are done */

        PKIX_CHECK(PKIX_CertStore_GetCertStoreContext
                (store, (PKIX_PL_Object **)&lcs, plContext),
                PKIX_CERTSTOREGETCERTSTORECONTEXTFAILED);

        PKIX_CHECK(PKIX_PL_LdapClient_InitiateRequest
                ((PKIX_PL_LdapClient *)lcs,
                &requestParams,
                &pollDesc,
                &responses,
                plContext),
                PKIX_LDAPCLIENTINITIATEREQUESTFAILED);

        PKIX_CHECK(pkix_pl_LdapCertStore_DestroyAVAList
                (requestParams.nc, plContext),
                PKIX_LDAPCERTSTOREDESTROYAVALISTFAILED);

        if (requestArena) {
                PKIX_PL_NSSCALL(CERTSTORE, PORT_FreeArena,
                        (requestArena, PR_FALSE));
                requestArena = NULL;
        }

        if (pollDesc != NULL) {
                /* client is waiting for non-blocking I/O to complete */
                *pNBIOContext = (void *)pollDesc;
                *pCertList = NULL;
                goto cleanup;
        }
        /* LdapClient has given us a response! */

        if (responses) {
                PKIX_CHECK(PKIX_CertStore_GetCertStoreCacheFlag
                        (store, &cacheFlag, plContext),
                        PKIX_CERTSTOREGETCERTSTORECACHEFLAGFAILED);

                PKIX_CHECK(pkix_pl_LdapCertStore_BuildCertList
                        (responses, &unfilteredCerts, plContext),
                        PKIX_LDAPCERTSTOREBUILDCERTLISTFAILED);

                PKIX_CHECK(pkix_CertSelector_Select
                        (selector, unfilteredCerts, &filteredCerts, plContext),
                        PKIX_CERTSELECTORSELECTFAILED);
        }

        *pNBIOContext = NULL;
        *pCertList = filteredCerts;
        filteredCerts = NULL;

cleanup:

        PKIX_DECREF(params);
        PKIX_DECREF(subjectName);
        PKIX_DECREF(responses);
        PKIX_DECREF(unfilteredCerts);
        PKIX_DECREF(filteredCerts);
        PKIX_DECREF(lcs);

        PKIX_RETURN(CERTSTORE);
}
/*
 * FUNCTION: pkix_TargetCertCheckerState_Create
 * DESCRIPTION:
 *
 *  Creates a new TargetCertCheckerState using the CertSelector pointed to
 *  by "certSelector" and the number of certs represented by "certsRemaining"
 *  and stores it at "pState".
 *
 * PARAMETERS:
 *  "certSelector"
 *      Address of CertSelector representing the criteria against which the
 *      final certificate in a chain is to be matched. Must be non-NULL.
 *  "certsRemaining"
 *      Number of certificates remaining in the chain.
 *  "pState"
 *      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 TargetCertCheckerState 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_TargetCertCheckerState_Create(
    PKIX_CertSelector *certSelector,
    PKIX_UInt32 certsRemaining,
    pkix_TargetCertCheckerState **pState,
    void *plContext)
{
        pkix_TargetCertCheckerState *state = NULL;
        PKIX_ComCertSelParams *certSelectorParams = NULL;
        PKIX_List *pathToNameList = NULL;
        PKIX_List *extKeyUsageList = NULL;
        PKIX_List *subjAltNameList = NULL;
        PKIX_PL_OID *extKeyUsageOID = NULL;
        PKIX_PL_OID *subjAltNameOID = NULL;
        PKIX_Boolean subjAltNameMatchAll = PKIX_TRUE;

        PKIX_ENTER(TARGETCERTCHECKERSTATE,
                    "pkix_TargetCertCheckerState_Create");
        PKIX_NULLCHECK_ONE(pState);

        PKIX_CHECK(PKIX_PL_OID_Create
                    (PKIX_EXTENDEDKEYUSAGE_OID,
                    &extKeyUsageOID,
                    plContext),
                    PKIX_OIDCREATEFAILED);

        PKIX_CHECK(PKIX_PL_OID_Create
                    (PKIX_CERTSUBJALTNAME_OID,
                    &subjAltNameOID,
                    plContext),
                    PKIX_OIDCREATEFAILED);

        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_TARGETCERTCHECKERSTATE_TYPE,
                    sizeof (pkix_TargetCertCheckerState),
                    (PKIX_PL_Object **)&state,
                    plContext),
                    PKIX_COULDNOTCREATETARGETCERTCHECKERSTATEOBJECT);

        /* initialize fields */

        if (certSelector != NULL) {

                PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams
                        (certSelector, &certSelectorParams, plContext),
                        PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMFAILED);

                if (certSelectorParams != NULL) {

                        PKIX_CHECK(PKIX_ComCertSelParams_GetPathToNames
                            (certSelectorParams,
                            &pathToNameList,
                            plContext),
                            PKIX_COMCERTSELPARAMSGETPATHTONAMESFAILED);

                        PKIX_CHECK(PKIX_ComCertSelParams_GetExtendedKeyUsage
                            (certSelectorParams,
                            &extKeyUsageList,
                            plContext),
                            PKIX_COMCERTSELPARAMSGETEXTENDEDKEYUSAGEFAILED);

                        PKIX_CHECK(PKIX_ComCertSelParams_GetSubjAltNames
                            (certSelectorParams,
                            &subjAltNameList,
                            plContext),
                            PKIX_COMCERTSELPARAMSGETSUBJALTNAMESFAILED);

                        PKIX_CHECK(PKIX_ComCertSelParams_GetMatchAllSubjAltNames
                            (certSelectorParams,
                            &subjAltNameMatchAll,
                            plContext),
                            PKIX_COMCERTSELPARAMSGETSUBJALTNAMESFAILED);
                }
        }

        state->certsRemaining = certsRemaining;
        state->subjAltNameMatchAll = subjAltNameMatchAll;

        PKIX_INCREF(certSelector);
        state->certSelector = certSelector;

        state->pathToNameList = pathToNameList;
        pathToNameList = NULL;

        state->extKeyUsageList = extKeyUsageList;
        extKeyUsageList = NULL;

        state->subjAltNameList = subjAltNameList;
        subjAltNameList = NULL;

        state->extKeyUsageOID = extKeyUsageOID;
        extKeyUsageOID = NULL;

        state->subjAltNameOID = subjAltNameOID;
        subjAltNameOID = NULL;

        *pState = state;
        state = NULL;

cleanup:
        
        PKIX_DECREF(extKeyUsageOID);
        PKIX_DECREF(subjAltNameOID);
        PKIX_DECREF(pathToNameList);
        PKIX_DECREF(extKeyUsageList);
        PKIX_DECREF(subjAltNameList);
        PKIX_DECREF(state);

        PKIX_DECREF(certSelectorParams);

        PKIX_RETURN(TARGETCERTCHECKERSTATE);

}