//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       SIPObjCB.cpp    (CAB)
//
//  Contents:   Microsoft SIP Provider
//
//  History:    15-Feb-1997 pberkman   created
//
//--------------------------------------------------------------------------

#include    "global.hxx"

#include    "sipobjcb.hxx"

#include    "sha.h"
#include    "md5.h"

////////////////////////////////////////////////////////////////////////////
//
// construct/destruct:
//

SIPObjectCAB_::SIPObjectCAB_(DWORD id) : SIPObject_(id)
{
    memset(&Para, 0x00, sizeof(CAB_PARA));

    fUseV1Sig           = FALSE;
}

SIPObjectCAB_::~SIPObjectCAB_(void)
{
    FreeHeader();
}

////////////////////////////////////////////////////////////////////////////
//
// public:
//

BOOL SIPObjectCAB_::RemoveSignedDataMsg(SIP_SUBJECTINFO *pSI,DWORD dwIdx)
{
    if (this->FileHandleFromSubject(pSI, GENERIC_READ | GENERIC_WRITE))
    {
        return(this->RemoveCertificate(dwIdx));
    }

    return(FALSE);
}

BOOL SIPObjectCAB_::CreateIndirectData(SIP_SUBJECTINFO *pSI,DWORD *pdwDLen,
                                   SIP_INDIRECT_DATA *psData)
{
    BOOL                    fRet;
    BYTE                    *pbDigest;
    BYTE                    *pbAttrData;

    SPC_LINK                SpcLink;
    DWORD                   cbDigest;
    HCRYPTPROV              hProvT;


    pbDigest    = NULL;
    pbAttrData  = NULL;
    fRet        = TRUE;

    hProvT = pSI->hProv;

    if (!(hProvT))
    {
        if (!(this->LoadDefaultProvider()))
        {
            goto GetProviderFailed;
        }

        hProvT = this->hProv;
    }

    memset(&SpcLink,0x00,sizeof(SPC_LINK));

    SpcLink.dwLinkChoice    = SPC_FILE_LINK_CHOICE;
    SpcLink.pwszFile        = OBSOLETE_TEXT_W;

    if (!(psData))
    {
        HCRYPTHASH  hHash;
        DWORD       dwRetLen;
        DWORD       dwEncLen;
        DWORD       dwAlgId;

        dwRetLen = sizeof(SIP_INDIRECT_DATA);

        // crypt_algorithm_identifier...
            // obj id
        dwRetLen += strlen(pSI->DigestAlgorithm.pszObjId);
        dwRetLen += 1;  // null term.
            // parameters (none)...

        // crypt_attribute_type_value size...
        dwRetLen += strlen(this->GetDataObjectID());
        dwRetLen += 1; // null term.

        // size of the value
        dwEncLen = 0;
        CryptEncodeObject(  PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                            this->GetDataOIDHint(),
                            &SpcLink,
                            NULL,
                            &dwEncLen);

        if (dwEncLen < 1)
        {
            goto EncodeError;
        }

        dwRetLen += dwEncLen;

        if ((dwAlgId = CertOIDToAlgId(pSI->DigestAlgorithm.pszObjId)) == 0)
        {
            goto BadAlgId;
        }

        switch (dwAlgId)
        {
            case CALG_MD5:
                cbDigest = MD5DIGESTLEN;
                break;

            case CALG_SHA1:
                cbDigest = A_SHA_DIGEST_LEN;
                break;

            default:
                if (!(CryptCreateHash(hProvT, dwAlgId, NULL, 0, &hHash)))
                {
                    goto CreateHashFailed;
                }

                // just to get hash length
                if (!(CryptHashData(hHash,(const BYTE *)" ",1,0)))
                {
                    CryptDestroyHash(hHash);

                    goto HashDataFailed;
                }

                cbDigest = 0;

                CryptGetHashParam(hHash, HP_HASHVAL, NULL, &cbDigest,0);

                CryptDestroyHash(hHash);
        }


        dwRetLen += cbDigest;
        *pdwDLen = dwRetLen;

        goto CommonReturn;
    }

    if (!(this->FileHandleFromSubject(pSI, (pSI->dwFlags & MSSIP_FLAGS_PROHIBIT_RESIZE_ON_CREATE) ?
                                                    GENERIC_READ : (GENERIC_READ | GENERIC_WRITE))))
    {
        goto SubjectFileFailure;
    }

    //
    //  version 1 had the signature in the header.  We want
    //  the signature at the end and our structure in the
    //  header where the signature used to be.  -- check it.
    //
    if (!(pSI->dwFlags & MSSIP_FLAGS_PROHIBIT_RESIZE_ON_CREATE))
    {
        if (!(this->ReadHeader()))
        {
            goto ReadHeaderFailed;
        }

        if (!(this->ReserveSignedData(sizeof(CABSignatureStruct_))))
        {
            goto ReserveDataFailed;
        }

        if (!(this->MapFile()))
        {
            goto MapFileFailed;
        }
    }

    if (!(pbDigest = this->DigestFile(hProvT, 0, pSI->DigestAlgorithm.pszObjId, &cbDigest)))
    {
        goto DigestFileFailed;
    }

    DWORD_PTR dwOffset;
    DWORD   dwRetLen;

    dwRetLen = 0;

    CryptEncodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, this->GetDataOIDHint(), &SpcLink,
                        NULL, &dwRetLen);

    if (dwRetLen < 1)
    {
        goto EncodeError;
    }

    if (!(pbAttrData = (BYTE *)this->SIPNew(dwRetLen)))
    {
        goto MemoryError;
    }

    if (!(CryptEncodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, this->GetDataOIDHint(), &SpcLink,
                            pbAttrData, &dwRetLen)))
    {
        goto EncodeError;
    }

    dwOffset =    (DWORD_PTR)psData + sizeof(SIP_INDIRECT_DATA);

    strcpy((char *)dwOffset, this->GetDataObjectID());
    psData->Data.pszObjId   = (LPSTR)dwOffset;
    dwOffset += (strlen(SPC_LINK_OBJID) + 1);

    memcpy((void *)dwOffset, pbAttrData,dwRetLen);
    psData->Data.Value.pbData   = (BYTE *)dwOffset;
    psData->Data.Value.cbData   = dwRetLen;
    dwOffset += dwRetLen;

    strcpy((char *)dwOffset, (char *)pSI->DigestAlgorithm.pszObjId);
    psData->DigestAlgorithm.pszObjId            = (char *)dwOffset;
    psData->DigestAlgorithm.Parameters.cbData   = 0;
    psData->DigestAlgorithm.Parameters.pbData   = NULL;
    dwOffset += (strlen(pSI->DigestAlgorithm.pszObjId) + 1);

    memcpy((void *)dwOffset,pbDigest,cbDigest);
    psData->Digest.pbData   = (BYTE *)dwOffset;
    psData->Digest.cbData   = cbDigest;

CommonReturn:

    if (pbDigest)
    {
        delete pbDigest;
    }

    if (pbAttrData)
    {
        delete pbAttrData;
    }

    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

    TRACE_ERROR_EX(DBG_SS, EncodeError);
    TRACE_ERROR_EX(DBG_SS, SubjectFileFailure);
    TRACE_ERROR_EX(DBG_SS, HashDataFailed);
    TRACE_ERROR_EX(DBG_SS, CreateHashFailed);
    TRACE_ERROR_EX(DBG_SS, ReadHeaderFailed);
    TRACE_ERROR_EX(DBG_SS, ReserveDataFailed);
    TRACE_ERROR_EX(DBG_SS, MapFileFailed);
    TRACE_ERROR_EX(DBG_SS, DigestFileFailed);
    TRACE_ERROR_EX(DBG_SS, GetProviderFailed);

    SET_ERROR_VAR_EX(DBG_SS, MemoryError,   ERROR_NOT_ENOUGH_MEMORY);
    SET_ERROR_VAR_EX(DBG_SS, BadAlgId,      NTE_BAD_ALGID);
}

////////////////////////////////////////////////////////////////////////////
//
// protected:
//

BOOL SIPObjectCAB_::GetMessageFromFile(SIP_SUBJECTINFO *pSI,
                                      WIN_CERTIFICATE *pWinCert,
                                      DWORD dwIndex,DWORD *pcbCert)
{
    DWORD       cbCert;

    if (dwIndex != 0)
    {
        goto InvalidParam;
    }

    if (!(this->ReadHeader()))
    {
        goto ReadHeaderFailed;
    }

    if (Para.Hdr.cbSig == 0)
    {
        goto NoSignature;
    }

    if (!(fUseV1Sig))
    {
        //
        //  Version 2 header
        //

        cbCert          = OFFSETOF(WIN_CERTIFICATE, bCertificate) +
                          Para.Hdr.pCabSigStruct->cbSig;

        if (*pcbCert < cbCert)
        {
            *pcbCert = cbCert;

            goto BufferTooSmall;
        }

        if (pWinCert)
        {
            if (!(this->ReadSignedData(pWinCert->bCertificate)))
            {
                goto ReadSignedFailed;
            }

        }
    }
    else
    {
        //
        //  Version 1 header
        //
        cbCert          = OFFSETOF(WIN_CERTIFICATE, bCertificate) + Para.Hdr.cbSig;

        if (*pcbCert < cbCert)
        {
            *pcbCert = cbCert;

            goto BufferTooSmall;
        }

        if (pWinCert)
        {
            BYTE    *pbSignedData;

            pbSignedData = Para.Hdr.pbReserve + RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk;

            pWinCert->wRevision = WIN_CERT_REVISION_1_0;

            memcpy(pWinCert->bCertificate, pbSignedData, Para.Hdr.cbSig);
        }
    }

    pWinCert->dwLength          = cbCert;
    pWinCert->wCertificateType  = WIN_CERT_TYPE_PKCS_SIGNED_DATA;

    return(TRUE);

ErrorReturn:
    return(FALSE);

    TRACE_ERROR_EX(DBG_SS, ReadHeaderFailed);
    TRACE_ERROR_EX(DBG_SS, ReadSignedFailed);

    SET_ERROR_VAR_EX(DBG_SS, InvalidParam,  ERROR_INVALID_PARAMETER);
    SET_ERROR_VAR_EX(DBG_SS, BufferTooSmall,ERROR_INSUFFICIENT_BUFFER);
    SET_ERROR_VAR_EX(DBG_SS, NoSignature,   TRUST_E_NOSIGNATURE);
}

BOOL SIPObjectCAB_::PutMessageInFile(SIP_SUBJECTINFO *pSI,
                                    WIN_CERTIFICATE *pWinCert,DWORD *pdwIndex)
{
    if ((pWinCert->dwLength <= OFFSETOF(WIN_CERTIFICATE,bCertificate))  ||
        (pWinCert->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA))
    {
        SetLastError((DWORD)ERROR_INVALID_PARAMETER);
        return(FALSE);
    }

    if (this->ReadHeader())
    {
        if (!(fUseV1Sig))
        {
            //
            //  version 2
            //
            if (this->WriteSignedData((BYTE *)&(pWinCert->bCertificate),
                                      pWinCert->dwLength -
                                      OFFSETOF(WIN_CERTIFICATE, bCertificate)))
            {
                return(TRUE);
            }
        }
        else
        {
            //
            //  version 1
            //
            DWORD   dwCheck;
            DWORD   cbSignedData;

            cbSignedData    = pWinCert->dwLength - OFFSETOF(WIN_CERTIFICATE, bCertificate);

            dwCheck = RESERVE_LEN_ALIGN(RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk + cbSignedData) -
                        Para.Hdr.cfres.cbCFHeader;

            if (dwCheck > 0)
            {
                SetLastError(CRYPT_E_FILERESIZED);
                return(FALSE);
            }


            if (WriteSignedDataV1((PBYTE)&(pWinCert->bCertificate), cbSignedData))
            {
                return(TRUE);
            }
        }
    }

    return(FALSE);
}

BOOL SIPObjectCAB_::GetDigestStream(DIGEST_DATA *pDigestData,
                                   DIGEST_FUNCTION pfnCallBack, DWORD dwFlags)
{
    if (dwFlags != 0)
    {
        goto InvalidParam;
    }

    if (!(this->ReadHeader()))
    {
        goto ReadHeaderFailed;
    }

    if (!(this->DigestHeader(pfnCallBack, pDigestData)))
    {
        goto DigestFailed;
    }

    DWORD   cbRemain;

    cbRemain = this->cbFileMap - Para.Hdr.cbTotalHdr;

    if (!(fUseV1Sig) && (Para.Hdr.pCabSigStruct))
    {
        cbRemain -= Para.Hdr.pCabSigStruct->cbSig;
    }

    if ((Para.Hdr.cfheader.cbCabinet - Para.Hdr.cbTotalHdr) != cbRemain)
    {
        goto BadFileFormat;
    }

    if (this->cbFileMap < (Para.Hdr.cbTotalHdr + cbRemain))
    {
        goto BadFileFormat;
    }

    __try {

    if (!(pfnCallBack(pDigestData, &this->pbFileMap[Para.Hdr.cbTotalHdr], cbRemain)))
    {
        goto HashFailed;
    }

    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(GetExceptionCode());
        goto HashFailed;
    }


    return(TRUE);

ErrorReturn:
    return(FALSE);

    TRACE_ERROR_EX(DBG_SS, DigestFailed);
    TRACE_ERROR_EX(DBG_SS, ReadHeaderFailed);
    TRACE_ERROR_EX(DBG_SS, HashFailed);

    SET_ERROR_VAR_EX(DBG_SS, InvalidParam,  ERROR_INVALID_PARAMETER);
    SET_ERROR_VAR_EX(DBG_SS, BadFileFormat, ERROR_BAD_FORMAT);
}


////////////////////////////////////////////////////////////////////////////
//
// private:
//

BOOL SIPObjectCAB_::RemoveCertificate(DWORD Index)
{
    return(FALSE);   // not yet!!! Currently, we only support 1.

#   ifdef _DONT_USE_YET

        BYTE            *pbFolders;
        DWORD           cbFolders;
        BYTE            *pbReserve;
        USHORT          cbReserve;

        if (Index != 0)
        {
            SetLastError((DWORD)ERROR_INVALID_PARAMETER);
            return(FALSE);
        }

        pbFolders   = NULL;
        cbFolders   = 0;

        Para.dwFlags = VERIFY_CAB_FLAG;

        if (this->ReadHeader())
        {
            if (Para.Hdr.cbSig <= (RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk))
            {
                SetLastError((DWORD)CRYPT_E_NO_MATCH);
                return(FALSE);
            }

            long    lShift;

            if (Para.Hdr.cbJunk)
            {
                lShift                                                  = Para.Hdr.cbSig;
                if (Para.Hdr.pbReserve)
                {
                    *((USHORT *)Para.Hdr.pbReserve)                     = Para.Hdr.cbJunk;
                    *((USHORT *)(Para.Hdr.pbReserve + sizeof(USHORT)))  = 0;    // no more sig
                }
            }
            else
            {
                lShift                  = Para.Hdr.cbSig + (sizeof(USHORT) * 2);
                Para.Hdr.cfheader.flags &= ~(cfhdrRESERVE_PRESENT);
                if (Para.Hdr.pbReserve)
                {
                    delete Para.Hdr.pbReserve;
                    Para.Hdr.pbReserve = NULL;
                }
            }

            Para.Hdr.cbSig              = 0;
            Para.Hdr.cfres.cbCFHeader   -= (USHORT)lShift;  // subtract the amount we want to shrink.

            // adjust the header offsets
            if (this->ShiftFileBytes(lShift))
            {
                Para.Hdr.cbTotalHdr         -= lShift;
                Para.Hdr.cfheader.cbCabinet -= lShift;
                Para.Hdr.cfheader.coffFiles -= lShift;
            }

            // redo checksums....
            this->ChecksumHeader();

            if (this->WriteHeader())
            {
                // We need to read in the folders to adjust their CFDATA file offset
                if (Para.Hdr.cfheader.cFolders)
                {
                    if (SetFilePointer(this->hFile,
                                        Para.Hdr.cbTotalHdr + lShift,
                                        NULL, FILE_BEGIN) == 0xFFFFFFFF)
                    {
                        return(FALSE);
                    }

                    USHORT  cFolders;
                    LONG    cbFolder;

                    cFolders    = Para.Hdr.cfheader.cFolders;
                    cbFolder    = sizeof(CFFOLDER) + Para.Hdr.cfres.cbCFFolder;
                    cbFolders   = cbFolder * cFolders;

                    if (!(pbFolders = (BYTE *)this->SIPNew(cbFolders)))
                    {
                        return(FALSE);
                    }
                    DWORD   cbFile;

                    if (!(ReadFile(this->hFile, pbFolders, cbFolders, &cbFile, NULL)) ||
                         (cbFile != cbFolders))
                    {
                        delete pbFolders;
                        SetLastError(ERROR_BAD_FORMAT);
                        return(FALSE);
                    }


                    BYTE    *pb;

                    pb = pbFolders;

                    while (cFolders > 0)
                    {
                        ((CFFOLDER *)pb)->coffCabStart -= lShift;
                        pb += cbFolder;
                        cFolders--;
                    }

                    // back up and write!
                    if (SetFilePointer(this->hFile, -((LONG)cbFolders),
                                        NULL, FILE_CURRENT) == 0xFFFFFFFF)
                    {
                        delete pbFolders;
                        return(FALSE);
                    }

                    if (!(WriteFile(this->hFile, pbFolders, cbFolders, &cbFile, NULL)) ||
                            (cbFile != cbFolders))
                    {
                        delete pbFolders;
                        return(FALSE);
                    }

                    delete pbFolders;
                }

                return(TRUE);
            }
        }

        return(FALSE);

#   endif // _DONT_USE_YET
}

BOOL SIPObjectCAB_::ReadSignedData(BYTE *pbRet)
{
    //
    //  this function is NOT called for version 1 Sigs!
    //

    if (Para.Hdr.pCabSigStruct->cbFileOffset != (DWORD)Para.Hdr.cfheader.cbCabinet)
    {
        SetLastError((DWORD)TRUST_E_NOSIGNATURE);
        return(FALSE);
    }

    if (this->cbFileMap < (Para.Hdr.pCabSigStruct->cbFileOffset +
                           Para.Hdr.pCabSigStruct->cbSig))
    {
        SetLastError(ERROR_BAD_FORMAT);
        return(FALSE);
    }

    __try {
    memcpy(pbRet, &this->pbFileMap[Para.Hdr.pCabSigStruct->cbFileOffset], Para.Hdr.pCabSigStruct->cbSig);
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(GetExceptionCode());
        return(FALSE);
    }

    return(TRUE);
}

BOOL SIPObjectCAB_::WriteSignedData(BYTE *pbSig, DWORD cbSig)
{
    //
    //  this function is NOT called for version 1 Sigs!
    //

    if (!(pbSig) || (cbSig == 0))
    {
        return(FALSE);
    }

    CABSignatureStruct_     sSig;

    memset(&sSig, 0x00, sizeof(CABSignatureStruct_));

    sSig.cbFileOffset   = Para.Hdr.cfheader.cbCabinet;
    sSig.cbSig          = cbSig;

    memcpy(Para.Hdr.pbReserve + RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk,
            &sSig, sizeof(CABSignatureStruct_));

    if (!(this->WriteHeader()))
    {
        return(FALSE);
    }

    if (SetFilePointer(this->hFile, Para.Hdr.cfheader.cbCabinet, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
        return(FALSE);
    }

    DWORD   cbWritten;

    if (!(WriteFile(this->hFile, pbSig, cbSig, &cbWritten, NULL)) ||
        (cbWritten != cbSig))
    {
        return(FALSE);
    }

    this->UnmapFile();

    SetEndOfFile(this->hFile);    // signature is the LAST thing!!!

    return(this->MapFile());
}

BOOL SIPObjectCAB_::WriteSignedDataV1(BYTE *pbSignedData, DWORD cbSignedData)
{
    if (!(pbSignedData) || (cbSignedData == 0))
    {
        return(FALSE);
    }

    memcpy(Para.Hdr.pbReserve + RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk,
                        pbSignedData, cbSignedData);
    Para.Hdr.cbSig = (USHORT)cbSignedData;

    ChecksumHeader();

    return(this->WriteHeader());
}

BOOL SIPObjectCAB_::ReadHeader(void)
{
    DWORD   cbOffset;
    BOOL    fRet;

    this->FreeHeader();

    if (this->cbFileMap < sizeof(Para.Hdr.cfheader))
    {
        goto BadCABFormat;
    }

    __try {

    memcpy(&Para.Hdr.cfheader, &this->pbFileMap[0], sizeof(Para.Hdr.cfheader));

    cbOffset = sizeof(Para.Hdr.cfheader);

    if (Para.Hdr.cfheader.sig != sigCFHEADER)
    {
        goto BadCABFormat;
    }

    if (Para.Hdr.cfheader.flags & cfhdrRESERVE_PRESENT)
    {
        if (this->cbFileMap < (cbOffset + sizeof(Para.Hdr.cfres)))
        {
            goto BadCABFormat;
        }

        memcpy(&Para.Hdr.cfres, &this->pbFileMap[cbOffset], sizeof(Para.Hdr.cfres));

        cbOffset += sizeof(Para.Hdr.cfres);

        Para.Hdr.cbcfres = sizeof(Para.Hdr.cfres);

        if (Para.Hdr.cfres.cbCFHeader > 0)
        {
            if (Para.Hdr.pbReserve = (BYTE *)this->SIPNew(Para.Hdr.cfres.cbCFHeader))
            {
                if (this->cbFileMap < (cbOffset + Para.Hdr.cfres.cbCFHeader))
                {
                    goto BadCABFormat;
                }

                memcpy(Para.Hdr.pbReserve, &this->pbFileMap[cbOffset], Para.Hdr.cfres.cbCFHeader);

                cbOffset += Para.Hdr.cfres.cbCFHeader;

                if (Para.Hdr.cfres.cbCFHeader >= RESERVE_CNT_HDR_LEN)
                {
                    Para.Hdr.cbJunk = *((USHORT *)Para.Hdr.pbReserve);
                    Para.Hdr.cbSig  = *((USHORT *)(Para.Hdr.pbReserve + sizeof(USHORT)));

                    if (RESERVE_CNT_HDR_LEN + Para.Hdr.cbJunk + Para.Hdr.cbSig > Para.Hdr.cfres.cbCFHeader)
                    {
                        goto BadCABFormat;
                    }

                    if (Para.Hdr.cbSig == sizeof(CABSignatureStruct_))
                    {
                        fUseV1Sig = FALSE;

                        Para.Hdr.pCabSigStruct = (CABSignatureStruct_ *)(Para.Hdr.pbReserve +
                                                                         RESERVE_CNT_HDR_LEN +
                                                                         Para.Hdr.cbJunk);
                    }
                    else
                    {
                        fUseV1Sig = TRUE;
                    }
                }
            }
        }
    }

    DWORD   cStrings;
    DWORD   cb;

    cStrings = 0;

    if (Para.Hdr.cfheader.flags & cfhdrPREV_CABINET)
    {
        cStrings += 2;
    }

    if (Para.Hdr.cfheader.flags & cfhdrNEXT_CABINET)
    {
        cStrings += 2;
    }

    if (cStrings > 0)
    {
        // First read to get total length of all the strings
        cb = 0;
        for (; cStrings > 0; cStrings--)
        {
            while (this->pbFileMap[cbOffset + cb])
            {
                cb++;

                if (this->cbFileMap < (cbOffset + cb))
                {
                    goto BadCABFormat;
                }
            }

            //Increment the counter for the NULL terminator
            cb++;
        }

        if (!(Para.Hdr.pbStrings = new BYTE[cb]))
        {
            goto MemoryError;
        }

        Para.Hdr.cbStrings  = cb;

        memcpy(Para.Hdr.pbStrings, &this->pbFileMap[cbOffset], cb);

        cbOffset += cb;
    }

    Para.Hdr.cbTotalHdr = sizeof(Para.Hdr.cfheader) + Para.Hdr.cbcfres +
                            Para.Hdr.cfres.cbCFHeader + Para.Hdr.cbStrings;

    if ((long)Para.Hdr.cbTotalHdr > Para.Hdr.cfheader.cbCabinet)
    {
        goto BadCABFormat;
    }

    fRet = TRUE;

    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(GetExceptionCode());
        goto ErrorReturn;
    }

CommonReturn:
    return(fRet);

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;

    SET_ERROR_VAR_EX(DBG_SS, BadCABFormat, ERROR_BAD_FORMAT);
    SET_ERROR_VAR_EX(DBG_SS, MemoryError,  ERROR_NOT_ENOUGH_MEMORY);
}

void SIPObjectCAB_::FreeHeader(void)
{
    DELETE_OBJECT(Para.Hdr.pbReserve);
    DELETE_OBJECT(Para.Hdr.pbStrings);

    memset(&Para, 0x00, sizeof(CAB_PARA));
}

BOOL SIPObjectCAB_::WriteHeader(void)
{
    DWORD cbWritten;

    // Position at beginning of file
    if (SetFilePointer(this->hFile, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
        return(FALSE);
    }

    if (!(WriteFile(this->hFile, &Para.Hdr.cfheader, sizeof(Para.Hdr.cfheader),
                        &cbWritten, NULL)) ||
        (cbWritten != sizeof(Para.Hdr.cfheader)))
    {
        return(FALSE);
    }

    if (Para.Hdr.cbcfres)
    {
        if (!(WriteFile(this->hFile, &Para.Hdr.cfres, sizeof(Para.Hdr.cfres),
                        &cbWritten, NULL)) ||
            (cbWritten != sizeof(Para.Hdr.cfres)))
        {
            return(FALSE);
        }

        if (Para.Hdr.pbReserve)
        {
            *((USHORT *)(Para.Hdr.pbReserve + sizeof(USHORT)))  = Para.Hdr.cbSig;

            if (!(WriteFile(this->hFile, Para.Hdr.pbReserve, Para.Hdr.cfres.cbCFHeader,
                                &cbWritten, NULL)) ||
                (cbWritten != Para.Hdr.cfres.cbCFHeader))
            {
                return(FALSE);
            }
        }
    }

    if (Para.Hdr.pbStrings)
    {
        if (!(WriteFile(this->hFile, Para.Hdr.pbStrings, Para.Hdr.cbStrings,
                            &cbWritten, NULL)) ||
            (cbWritten != Para.Hdr.cbStrings))
        {
            return(FALSE);
        }
    }

    return(TRUE);
}

BOOL SIPObjectCAB_::ShiftFileBytes(LONG lbShift)
{
    LONG    lStartOffset;
    LONG    lEndOffset;
    LONG    lNewEndOffset;
    LONG    cbTotalMove;
    LONG    cbMove;

    lStartOffset    = SetFilePointer(this->hFile, 0, NULL, FILE_CURRENT);
    lEndOffset      = (LONG)this->cbFileMap;

    lNewEndOffset   = lEndOffset + lbShift;
    cbTotalMove     = lEndOffset - lStartOffset;

    BYTE    szMove[512];

    while (cbTotalMove)
    {
        cbMove = min(cbTotalMove, sizeof(szMove));

        if (lbShift > 0)
        {
            if (!(SeekAndReadFile(lEndOffset - cbMove, &szMove[0], cbMove)))
            {
                return(FALSE);
            }
            if (!(SeekAndWriteFile((lEndOffset - cbMove) + lbShift, &szMove[0], cbMove)))
            {
                return(FALSE);
            }

            lEndOffset -= cbMove;
        }
        else if (lbShift < 0)
        {
            if (!(SeekAndReadFile(lStartOffset, &szMove[0], cbMove)))
            {
                return(FALSE);
            }
            if (!(SeekAndWriteFile(lStartOffset + lbShift, &szMove[0], cbMove)))
            {
                return(FALSE);
            }

            lStartOffset += cbMove;
        }

        cbTotalMove -= cbMove;
    }

    //
    // Set end of file
    //
    if (SetFilePointer(this->hFile, lNewEndOffset, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
        return(FALSE);
    }

    this->UnmapFile();

    SetEndOfFile(this->hFile);

    return(this->MapFile());
}


BOOL SIPObjectCAB_::ReserveSignedData(DWORD cbSignedData)
{
    LONG    lbShift;
    USHORT  cbReserve;


    if (cbSignedData != sizeof(CABSignatureStruct_))
    {
        return(FALSE);
    }

    if (SetFilePointer(this->hFile, Para.Hdr.cbTotalHdr, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
        return(FALSE);
    }

    fUseV1Sig           = FALSE;

    //
    // Calculate length needed for CFRESERVE's abReserve[] and allocate
    //
    cbReserve = (USHORT)(RESERVE_LEN_ALIGN(RESERVE_CNT_HDR_LEN +
                Para.Hdr.cbJunk + cbSignedData));

    //
    // Calculate number of bytes to grow or shrink the cab file
    //
    lbShift = cbReserve - Para.Hdr.cfres.cbCFHeader;

    //
    //  we're alread a v1 cab!
    //
    if (lbShift == 0)
    {
        return(TRUE);
    }

    BYTE    *pbReserve;
    BYTE    *pbFolders;
    DWORD   cbFolders;

    pbFolders   = NULL;
    cbFolders   = 0;


    if (!(pbReserve = (BYTE *)this->SIPNew(cbReserve)))
    {
        return(FALSE);
    }

    memset(pbReserve, 0x00, cbReserve);

    //
    // Update allocated abReserve[] with counts and old junk
    //
    if (Para.Hdr.cbJunk)
    {
        *((USHORT *)pbReserve) = Para.Hdr.cbJunk;
        memcpy(pbReserve + RESERVE_CNT_HDR_LEN,
                Para.Hdr.pbReserve + RESERVE_CNT_HDR_LEN, Para.Hdr.cbJunk);
    }
    *((USHORT *)(pbReserve + sizeof(USHORT))) = (USHORT)cbSignedData;

    //
    // Update Hdr's CFRESERVE abReserve[] to reflect above changes
    //
    if (Para.Hdr.pbReserve)
    {
        delete Para.Hdr.pbReserve;
        Para.Hdr.pbReserve = NULL;
    }
    Para.Hdr.pbReserve          = pbReserve;
    Para.Hdr.cfres.cbCFHeader   = cbReserve;
    Para.Hdr.cbSig              = (USHORT)cbSignedData;

    if (Para.Hdr.cbcfres == 0)
    {
        // Need to add CFRESERVE record
        Para.Hdr.cfheader.flags |= cfhdrRESERVE_PRESENT;
        Para.Hdr.cbcfres        = sizeof(CFRESERVE);
        lbShift                 += sizeof(CFRESERVE);
    }

    //
    // We need to read in the folders to adjust their CFDATA file offset
    //
    if (Para.Hdr.cfheader.cFolders)
    {
        USHORT  cFolders;
        LONG    cbFolder;
        BYTE    *pb;
        DWORD   cbRead;

        cFolders    = Para.Hdr.cfheader.cFolders;
        cbFolder    = sizeof(CFFOLDER) + Para.Hdr.cfres.cbCFFolder;
        cbFolders   = cbFolder * cFolders;

        if (!(pbFolders = (BYTE *)this->SIPNew(cbFolders)))
        {
            return(FALSE);
        }

        if (!(ReadFile(this->hFile, pbFolders, cbFolders, &cbRead, NULL)) ||
            (cbRead != cbFolders))
        {
            delete pbFolders;
            SetLastError(ERROR_BAD_FORMAT);
            return(FALSE);
        }

        pb = pbFolders;

        for (; cFolders > 0; cFolders--, pb += cbFolder)
        {
            ((CFFOLDER *) pb)->coffCabStart += lbShift;
        }
    }

    //
    // We need to shift the remaining contents of the cab file (CFFILE (s)
    // and CFDATA (s)) by lbShift
    //
    if (!(ShiftFileBytes(lbShift)))
    {
        if (pbFolders)
        {
            delete pbFolders;
        }
        return(FALSE);
    }

    //
    // Update lengths and offsets in the header by the delta shift needed
    // to store the signed data.
    //
    Para.Hdr.cbTotalHdr         += lbShift;
    Para.Hdr.cfheader.cbCabinet += lbShift;
    Para.Hdr.cfheader.coffFiles += lbShift;

    //
    //  pberkman - if someone starts using these, we don't want to mess them up!!!
    //
    // Para.Hdr.cfheader.csumHeader    = 0;
    // Para.Hdr.cfheader.csumFolders   = 0;
    // Para.Hdr.cfheader.csumFiles     = 0;

    //
    // Write the header and folders back to the cab file
    //
    if (!(this->WriteHeader()))
    {
        if (pbFolders)
        {
            delete pbFolders;
        }
        return(FALSE);
    }

    if (pbFolders)
    {
        DWORD cbWritten;

        cbWritten = 0;
        if (!(WriteFile(this->hFile, pbFolders, cbFolders, &cbWritten, NULL)) ||
            (cbWritten != cbFolders))
        {
            delete pbFolders;
            return(FALSE);
        }
        delete pbFolders;
    }

    return(TRUE);
}

BOOL SIPObjectCAB_::DigestHeader(DIGEST_FUNCTION pfnDigestData, DIGEST_HANDLE hDigestData)
{
    //
    // Digest CFHEADER, skipping the csumHeader field
    //
    if (!(pfnDigestData(hDigestData, (BYTE *)&Para.Hdr.cfheader.sig,
                        sizeof(Para.Hdr.cfheader.sig))))
    {
        return(FALSE);
    }

    if (!(pfnDigestData(hDigestData, (BYTE *)&Para.Hdr.cfheader.cbCabinet,
                        sizeof(CFHEADER) - sizeof(Para.Hdr.cfheader.sig) - sizeof(CHECKSUM))))
    {
        return(FALSE);
    }

    if (Para.Hdr.cbcfres)
    {
        // skip the cfres itself!

        if (Para.Hdr.cfres.cbCFHeader >= RESERVE_CNT_HDR_LEN)
        {
            // Digest any "junk" in abReserve[] before the signature
            if (!(pfnDigestData(hDigestData, (BYTE *)&Para.Hdr.cbJunk,
                                    sizeof(Para.Hdr.cbJunk))))
            {
                return(FALSE);
            }
            if (Para.Hdr.cbJunk)
            {
                if (!(pfnDigestData(hDigestData,
                                    Para.Hdr.pbReserve + RESERVE_CNT_HDR_LEN,
                                    Para.Hdr.cbJunk)))
                {
                    return(FALSE);
                }
            }
        }
    }

    if (Para.Hdr.pbStrings)
    {
        // Digest the strings
        if (!(pfnDigestData(hDigestData, Para.Hdr.pbStrings, Para.Hdr.cbStrings)))
        {
            return(FALSE);
        }
    }

    return(TRUE);
}

void SIPObjectCAB_::ChecksumHeader(void)
{
    return;

    // version 1 set checksum to zero.  this seems to be the correct thing to do????

#   ifdef _DONT_USE_YET

        CHECKSUM csum = 0;

        if (Para.Hdr.cfheader.csumHeader == 0)
        {
            return;
        }

        // Checksum CFHEADER, skipping the csumHeader field
        csum = CSUMCompute(&Para.Hdr.cfheader.sig, sizeof(Para.Hdr.cfheader.sig), csum);
        csum = CSUMCompute(&Para.Hdr.cfheader.cbCabinet,
                            sizeof(CFHEADER) -
                            sizeof(Para.Hdr.cfheader.sig) -
                            sizeof(CHECKSUM),
                            csum);

        if (Para.Hdr.cbcfres)
        {
            csum = CSUMCompute(&Para.Hdr.cfres, sizeof(Para.Hdr.cfres), csum);
            if (Para.Hdr.pbReserve) 
            {
                csum = CSUMCompute(Para.Hdr.pbReserve, Para.Hdr.cfres.cbCFHeader, csum);
            }
        }

        if (Para.Hdr.pbStrings)
        {
            csum = CSUMCompute(Para.Hdr.pbStrings, Para.Hdr.cbStrings, csum);
        }

        Para.Hdr.cfheader.csumHeader = csum;

#   endif

}

#ifdef _DONT_USE_YET

    CHECKSUM SIPObjectCAB_::CSUMCompute(void *pv, UINT cb, CHECKSUM seed)
    {
        int         cUlong;                 // Number of ULONGs in block
        CHECKSUM    csum;                   // Checksum accumulator
        BYTE       *pb;
        ULONG       ul;

        cUlong = cb / 4;                    // Number of ULONGs
        csum = seed;                        // Init checksum
        pb = (BYTE*)pv;                            // Start at front of data block

        //** Checksum integral multiple of ULONGs
        while (cUlong-- > 0) {
            //** NOTE: Build ULONG in big/little-endian independent manner
            ul = *pb++;                     // Get low-order byte
            ul |= (((ULONG)(*pb++)) <<  8); // Add 2nd byte
            ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
            ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte

            csum ^= ul;                     // Update checksum
        }

        //** Checksum remainder bytes
        ul = 0;
        switch (cb % 4) {
            case 3:
                ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
            case 2:
                ul |= (((ULONG)(*pb++)) <<  8); // Add 2nd byte
            case 1:
                ul |= *pb++;                    // Get low-order byte
            default:
                break;
        }
        csum ^= ul;                         // Update checksum

        //** Return computed checksum
        return csum;
    }

#endif // _DONT_USE_YET

