/*
 * FUNCTION: pkix_pl_LdapCertStore_GetCertContinue
 *  (see description of PKIX_CertStore_CertCallback in pkix_certstore.h)
 */
PKIX_Error *
pkix_pl_LdapCertStore_GetCertContinue(
        PKIX_CertStore *store,
        PKIX_CertSelector *selector,
        PKIX_VerifyNode *verifyNode,
        void **pNBIOContext,
        PKIX_List **pCertList,
        void *plContext)
{
        PKIX_Boolean cacheFlag = PKIX_FALSE;
        PKIX_PL_LdapCertStoreContext *lcs = NULL;
        void *pollDesc = NULL;
        PKIX_List *responses = NULL;
        PKIX_List *unfilteredCerts = NULL;
        PKIX_List *filteredCerts = NULL;

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

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

        PKIX_CHECK(PKIX_PL_LdapClient_ResumeRequest
                ((PKIX_PL_LdapClient *)lcs, &pollDesc, &responses, plContext),
                PKIX_LDAPCLIENTRESUMEREQUESTFAILED);

        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;

cleanup:

        PKIX_DECREF(responses);
        PKIX_DECREF(unfilteredCerts);
        PKIX_DECREF(lcs);

        PKIX_RETURN(CERTSTORE);
}
/*
 * 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_DefaultCRLChecker_Check_Store
 *
 * DESCRIPTION:
 *  Checks the certStore pointed to by "certStore" for a CRL that may determine
 *  whether the Cert pointed to by "cert" has been revoked.
 *
 * PARAMETERS
 *  "checker"
 *      Address of CertChainChecker which has the state data.
 *      Must be non-NULL.
 *  "cert"
 *      Address of Certificate that is to be validated. Must be non-NULL.
 *  "prevPublicKey"
 *      Address of previous public key in the backward chain. May be NULL.
 *  "state"
 *      Address of DefaultCrlCheckerState. Must be non-NULL.
 *  "unresolvedCriticalExtensions"
 *      A List OIDs. Not **yet** used in this checker function.
 *  "certStore"
 *      Address of the CertStore to be queried for a relevant CRL. Must be
 *      non-NULL.
 *  "pNBIOContext"
 *      Address at which platform-dependent information is stored if processing
 *      is suspended for non-blocking I/O. 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 DefaultCrlCheckerState Error if the function fails in a
 *  non-fatal way.
 *  Returns a Fatal Error
 */
static PKIX_Error *
pkix_DefaultCRLChecker_Check_Store(
        PKIX_CertChainChecker *checker,
        PKIX_PL_Cert *cert,
        PKIX_PL_PublicKey *prevPublicKey,
        pkix_DefaultCRLCheckerState *state,
        PKIX_List *unresolvedCriticalExtensions,
        PKIX_CertStore *certStore,
        void **pNBIOContext,
        void *plContext)
{

        PKIX_Boolean cacheFlag = PKIX_FALSE;
        PKIX_Boolean cacheHit = PKIX_FALSE;
        PKIX_UInt32 numEntries = 0;
        PKIX_UInt32 i = 0;
        PKIX_Int32 reasonCode = 0;
        PKIX_UInt32 allReasonCodes = 0;
        PKIX_List *crlList = NULL;
        PKIX_List *crlEntryList = NULL;
        PKIX_PL_CRLEntry *crlEntry = NULL;
        PKIX_Error *checkCrlFail = NULL;
        PKIX_CertStore_CRLCallback getCrls = NULL;
        void *nbioContext = NULL;

        PKIX_ENTER(CERTCHAINCHECKER, "pkix_DefaultCRLChecker_Check_Store");
        PKIX_NULLCHECK_TWO(checker, cert);
        PKIX_NULLCHECK_THREE(state, certStore, pNBIOContext);

        nbioContext = *pNBIOContext;
        *pNBIOContext = NULL;

        /* Are this CertStore's entries in cache? */
        PKIX_CHECK(PKIX_CertStore_GetCertStoreCacheFlag
                (certStore, &cacheFlag, plContext),
                PKIX_CERTSTOREGETCERTSTORECACHEFLAGFAILED);

        if (cacheFlag) {

                PKIX_CHECK(pkix_CacheCrlEntry_Lookup
                        (certStore,
                        state->certIssuer,
                        state->certSerialNumber,
                        &cacheHit,
                        &crlEntryList,
                        plContext),
                        PKIX_CACHECRLENTRYLOOKUPFAILED);

        }

        if (cacheHit) {

                /* Use cached data */

                PKIX_CHECK(PKIX_List_GetLength
                        (crlEntryList, &numEntries, plContext),
                        PKIX_LISTGETLENGTHFAILED);

                for (i = 0; i < numEntries; i++) {

                    PKIX_CHECK(PKIX_List_GetItem
                            (crlEntryList,
                            i,
                            (PKIX_PL_Object **)&crlEntry,
                            plContext),
                            PKIX_LISTGETITEMFAILED);

                    PKIX_CHECK(PKIX_PL_CRLEntry_GetCRLEntryReasonCode
                            (crlEntry, &reasonCode, plContext),
                            PKIX_CRLENTRYGETCRLENTRYREASONCODEFAILED);

		    if (reasonCode >= 0) {
			if (reasonCode >= numReasonCodes) 
			    reasonCode = 0;

			allReasonCodes |= (1 << reasonCode);

			PKIX_DEFAULTCRLCHECKERSTATE_DEBUG_ARG
                                    ("CRL revocation Reason: %s\n ",
                                    reasonCodeMsgString[reasonCode]);

                    }

                    PKIX_DECREF(crlEntry);
                }

                state->reasonCodeMask |= allReasonCodes;

                if (allReasonCodes != 0) {

                        PKIX_ERROR(PKIX_CERTIFICATEREVOKEDBYCRL);
                }
 
                PKIX_DECREF(crlEntryList);

       } else {

                if (nbioContext == NULL) {
                        PKIX_CHECK(PKIX_CertStore_GetCRLCallback
                                (certStore, &getCrls, plContext),
                                PKIX_CERTSTOREGETCRLCALLBACKFAILED);

                        PKIX_CHECK(getCrls
                                (certStore,
                                state->crlSelector,
                                &nbioContext,
                                &crlList,
                                plContext),
                                PKIX_GETCRLSFAILED);
                } else {
                        PKIX_CHECK(PKIX_CertStore_CrlContinue
                                (certStore,
                                state->crlSelector,
                                &nbioContext,
                                &crlList,
                                plContext),
                                PKIX_CERTSTORECRLCONTINUEFAILED);
                }

                /*
                 * Verify Certificate validity: if one CertStore provides
                 * reason code, we stop here. Instead of exhausting all
                 * CertStores to get all possible reason codes associated
                 *  with the Cert. May be expanded if desire otherwise.
                 */

                if (crlList == NULL) {

                        *pNBIOContext = nbioContext;
                } else {

                        *pNBIOContext = NULL;

                        checkCrlFail = pkix_DefaultCRLChecker_CheckCRLs
                                (cert,
                                state->certIssuer,
                                state->certSerialNumber,
                                prevPublicKey,
                                crlList,
                                state,
                                &crlEntryList,
                                plContext);

                        if (checkCrlFail) {
                                if (crlEntryList != NULL) {
                                        /* Add to cache */
                                        PKIX_CHECK(pkix_CacheCrlEntry_Add
                                               (certStore,
                                               state->certIssuer,
                                               state->certSerialNumber,
                                               crlEntryList,
                                               plContext),
                                               PKIX_CACHECRLENTRYADDFAILED);
                                }
                                PKIX_ERROR(PKIX_CERTIFICATEREVOKEDBYCRL);
                        }
                }

                PKIX_DECREF(crlList);

        }

cleanup:
        PKIX_DECREF(crlEntryList);
        PKIX_DECREF(crlEntry);
        PKIX_DECREF(crlList);
        PKIX_DECREF(checkCrlFail);

        PKIX_RETURN(CERTCHAINCHECKER);
}