/* -*- Mode: C; tab-width: 8 -*-*/
/* 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/. */

/*
 * This file will contain all routines dealing with creating a
 * CMMFCertRepContent structure through Create/Set functions.
 */

#include "cmmf.h"
#include "cmmfi.h"
#include "crmf.h"
#include "crmfi.h"
#include "secitem.h"
#include "secder.h"

CMMFCertRepContent *
CMMF_CreateCertRepContent(void)
{
    CMMFCertRepContent *retCertRep;
    PLArenaPool *poolp;

    poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE);
    if (poolp == NULL) {
        goto loser;
    }
    retCertRep = PORT_ArenaZNew(poolp, CMMFCertRepContent);
    if (retCertRep == NULL) {
        goto loser;
    }
    retCertRep->poolp = poolp;
    return retCertRep;
loser:
    if (poolp != NULL) {
        PORT_FreeArena(poolp, PR_FALSE);
    }
    return NULL;
}

SECStatus
cmmf_CertOrEncCertSetCertificate(CMMFCertOrEncCert *certOrEncCert,
                                 PLArenaPool *poolp,
                                 CERTCertificate *inCert)
{
    SECItem *derDest = NULL;
    SECStatus rv = SECFailure;

    if (inCert->derCert.data == NULL) {
        derDest = SEC_ASN1EncodeItem(NULL, NULL, inCert,
                                     CMMFCertOrEncCertCertificateTemplate);
        if (derDest == NULL) {
            goto loser;
        }
    } else {
        derDest = SECITEM_DupItem(&inCert->derCert);
        if (derDest == NULL) {
            goto loser;
        }
    }
    PORT_Assert(certOrEncCert->cert.certificate == NULL);
    certOrEncCert->cert.certificate = CERT_DupCertificate(inCert);
    certOrEncCert->choice = cmmfCertificate;
    if (poolp != NULL) {
        rv = SECITEM_CopyItem(poolp, &certOrEncCert->derValue, derDest);
        if (rv != SECSuccess) {
            goto loser;
        }
    } else {
        certOrEncCert->derValue = *derDest;
    }
    PORT_Free(derDest);
    return SECSuccess;
loser:
    if (derDest != NULL) {
        SECITEM_FreeItem(derDest, PR_TRUE);
    }
    return rv;
}

SECStatus
cmmf_ExtractCertsFromList(CERTCertList *inCertList,
                          PLArenaPool *poolp,
                          CERTCertificate ***certArray)
{
    CERTCertificate **arrayLocalCopy;
    CERTCertListNode *node;
    int numNodes = 0, i;

    for (node = CERT_LIST_HEAD(inCertList); !CERT_LIST_END(node, inCertList);
         node = CERT_LIST_NEXT(node)) {
        numNodes++;
    }

    arrayLocalCopy = *certArray = (poolp == NULL) ? PORT_NewArray(CERTCertificate *, (numNodes + 1)) : PORT_ArenaNewArray(poolp, CERTCertificate *, (numNodes + 1));
    if (arrayLocalCopy == NULL) {
        return SECFailure;
    }
    for (node = CERT_LIST_HEAD(inCertList), i = 0;
         !CERT_LIST_END(node, inCertList);
         node = CERT_LIST_NEXT(node), i++) {
        arrayLocalCopy[i] = CERT_DupCertificate(node->cert);
        if (arrayLocalCopy[i] == NULL) {
            int j;

            for (j = 0; j < i; j++) {
                CERT_DestroyCertificate(arrayLocalCopy[j]);
            }
            if (poolp == NULL) {
                PORT_Free(arrayLocalCopy);
            }
            *certArray = NULL;
            return SECFailure;
        }
    }
    arrayLocalCopy[numNodes] = NULL;
    return SECSuccess;
}

SECStatus
CMMF_CertRepContentSetCertResponses(CMMFCertRepContent *inCertRepContent,
                                    CMMFCertResponse **inCertResponses,
                                    int inNumResponses)
{
    PLArenaPool *poolp;
    CMMFCertResponse **respArr, *newResp;
    void *mark;
    SECStatus rv;
    int i;

    PORT_Assert(inCertRepContent != NULL &&
                inCertResponses != NULL &&
                inNumResponses > 0);
    if (inCertRepContent == NULL ||
        inCertResponses == NULL ||
        inCertRepContent->response != NULL) {
        return SECFailure;
    }
    poolp = inCertRepContent->poolp;
    mark = PORT_ArenaMark(poolp);
    respArr = inCertRepContent->response =
        PORT_ArenaZNewArray(poolp, CMMFCertResponse *, (inNumResponses + 1));
    if (respArr == NULL) {
        goto loser;
    }
    for (i = 0; i < inNumResponses; i++) {
        newResp = PORT_ArenaZNew(poolp, CMMFCertResponse);
        if (newResp == NULL) {
            goto loser;
        }
        rv = cmmf_CopyCertResponse(poolp, newResp, inCertResponses[i]);
        if (rv != SECSuccess) {
            goto loser;
        }
        respArr[i] = newResp;
    }
    respArr[inNumResponses] = NULL;
    PORT_ArenaUnmark(poolp, mark);
    return SECSuccess;

loser:
    PORT_ArenaRelease(poolp, mark);
    return SECFailure;
}

CMMFCertResponse *
CMMF_CreateCertResponse(long inCertReqId)
{
    SECItem *dummy;
    CMMFCertResponse *newResp;

    newResp = PORT_ZNew(CMMFCertResponse);
    if (newResp == NULL) {
        goto loser;
    }
    dummy = SEC_ASN1EncodeInteger(NULL, &newResp->certReqId, inCertReqId);
    if (dummy != &newResp->certReqId) {
        goto loser;
    }
    return newResp;

loser:
    if (newResp != NULL) {
        CMMF_DestroyCertResponse(newResp);
    }
    return NULL;
}

SECStatus
CMMF_CertResponseSetPKIStatusInfoStatus(CMMFCertResponse *inCertResp,
                                        CMMFPKIStatus inPKIStatus)
{
    PORT_Assert(inCertResp != NULL && inPKIStatus >= cmmfGranted &&
                inPKIStatus < cmmfNumPKIStatus);

    if (inCertResp == NULL) {
        return SECFailure;
    }
    return cmmf_PKIStatusInfoSetStatus(&inCertResp->status, NULL,
                                       inPKIStatus);
}

SECStatus
CMMF_CertResponseSetCertificate(CMMFCertResponse *inCertResp,
                                CERTCertificate *inCertificate)
{
    CMMFCertifiedKeyPair *keyPair = NULL;
    SECStatus rv = SECFailure;

    PORT_Assert(inCertResp != NULL && inCertificate != NULL);
    if (inCertResp == NULL || inCertificate == NULL) {
        return SECFailure;
    }
    if (inCertResp->certifiedKeyPair == NULL) {
        keyPair = inCertResp->certifiedKeyPair =
            PORT_ZNew(CMMFCertifiedKeyPair);
    } else {
        keyPair = inCertResp->certifiedKeyPair;
    }
    if (keyPair == NULL) {
        goto loser;
    }
    rv = cmmf_CertOrEncCertSetCertificate(&keyPair->certOrEncCert, NULL,
                                          inCertificate);
    if (rv != SECSuccess) {
        goto loser;
    }
    return SECSuccess;
loser:
    if (keyPair) {
        if (keyPair->certOrEncCert.derValue.data) {
            PORT_Free(keyPair->certOrEncCert.derValue.data);
        }
        PORT_Free(keyPair);
    }
    return rv;
}

SECStatus
CMMF_CertRepContentSetCAPubs(CMMFCertRepContent *inCertRepContent,
                             CERTCertList *inCAPubs)
{
    PLArenaPool *poolp;
    void *mark;
    SECStatus rv;

    PORT_Assert(inCertRepContent != NULL &&
                inCAPubs != NULL &&
                inCertRepContent->caPubs == NULL);

    if (inCertRepContent == NULL ||
        inCAPubs == NULL || inCertRepContent == NULL) {
        return SECFailure;
    }

    poolp = inCertRepContent->poolp;
    mark = PORT_ArenaMark(poolp);

    rv = cmmf_ExtractCertsFromList(inCAPubs, poolp,
                                   &inCertRepContent->caPubs);

    if (rv != SECSuccess) {
        PORT_ArenaRelease(poolp, mark);
    } else {
        PORT_ArenaUnmark(poolp, mark);
    }
    return rv;
}

CERTCertificate *
CMMF_CertifiedKeyPairGetCertificate(CMMFCertifiedKeyPair *inCertKeyPair,
                                    CERTCertDBHandle *inCertdb)
{
    PORT_Assert(inCertKeyPair != NULL);
    if (inCertKeyPair == NULL) {
        return NULL;
    }
    return cmmf_CertOrEncCertGetCertificate(&inCertKeyPair->certOrEncCert,
                                            inCertdb);
}