/* 
 * 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 Netscape are 
 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
 * Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

#ifdef DEBUG
static const char CVS_ID[] = "@(#) $RCSfile: object.c,v $ $Revision: 1.9 $ $Date: 2002/03/29 18:53:15 $ $Name: MOZILLA_1_7_BRANCH $";
#endif /* DEBUG */

/*
 * object.c
 *
 * This file implements the NSSCKFWObject type and methods.
 */

#ifndef CK_T
#include "ck.h"
#endif /* CK_T */

/*
 * NSSCKFWObject
 *
 * -- create/destroy --
 *  nssCKFWObject_Create
 *  nssCKFWObject_Finalize
 *  nssCKFWObject_Destroy
 *
 * -- public accessors --
 *  NSSCKFWObject_GetMDObject
 *  NSSCKFWObject_GetArena
 *  NSSCKFWObject_IsTokenObject
 *  NSSCKFWObject_GetAttributeCount
 *  NSSCKFWObject_GetAttributeTypes
 *  NSSCKFWObject_GetAttributeSize
 *  NSSCKFWObject_GetAttribute
 *  NSSCKFWObject_SetAttribute
 *  NSSCKFWObject_GetObjectSize
 *
 * -- implement public accessors --
 *  nssCKFWObject_GetMDObject
 *  nssCKFWObject_GetArena
 *
 * -- private accessors --
 *  nssCKFWObject_SetHandle
 *  nssCKFWObject_GetHandle
 *
 * -- module fronts --
 *  nssCKFWObject_IsTokenObject
 *  nssCKFWObject_GetAttributeCount
 *  nssCKFWObject_GetAttributeTypes
 *  nssCKFWObject_GetAttributeSize
 *  nssCKFWObject_GetAttribute
 *  nssCKFWObject_SetAttribute
 *  nssCKFWObject_GetObjectSize
 */

struct NSSCKFWObjectStr {
  NSSCKFWMutex *mutex; /* merely to serialise the MDObject calls */
  NSSArena *arena;
  NSSCKMDObject *mdObject;
  NSSCKMDSession *mdSession;
  NSSCKFWSession *fwSession;
  NSSCKMDToken *mdToken;
  NSSCKFWToken *fwToken;
  NSSCKMDInstance *mdInstance;
  NSSCKFWInstance *fwInstance;
  CK_OBJECT_HANDLE hObject;
};

#ifdef DEBUG
/*
 * But first, the pointer-tracking stuff.
 *
 * NOTE: the pointer-tracking support in NSS/base currently relies
 * upon NSPR's CallOnce support.  That, however, relies upon NSPR's
 * locking, which is tied into the runtime.  We need a pointer-tracker
 * implementation that uses the locks supplied through C_Initialize.
 * That support, however, can be filled in later.  So for now, I'll
 * just do this routines as no-ops.
 */

static CK_RV
object_add_pointer
(
  const NSSCKFWObject *fwObject
)
{
  return CKR_OK;
}

static CK_RV
object_remove_pointer
(
  const NSSCKFWObject *fwObject
)
{
  return CKR_OK;
}

NSS_IMPLEMENT CK_RV
nssCKFWObject_verifyPointer
(
  const NSSCKFWObject *fwObject
)
{
  return CKR_OK;
}

#endif /* DEBUG */


/*
 * nssCKFWObject_Create
 *
 */
NSS_IMPLEMENT NSSCKFWObject *
nssCKFWObject_Create
(
  NSSArena *arena,
  NSSCKMDObject *mdObject,
  NSSCKFWSession *fwSession,
  NSSCKFWToken *fwToken,
  NSSCKFWInstance *fwInstance,
  CK_RV *pError
)
{
  NSSCKFWObject *fwObject;
  nssCKFWHash *mdObjectHash;

#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (NSSCKFWObject *)NULL;
  }

  if( PR_SUCCESS != nssArena_verifyPointer(arena) ) {
    *pError = CKR_ARGUMENTS_BAD;
    return (NSSCKFWObject *)NULL;
  }
#endif /* NSSDEBUG */

  mdObjectHash = nssCKFWToken_GetMDObjectHash(fwToken);
  if( (nssCKFWHash *)NULL == mdObjectHash ) {
    *pError = CKR_GENERAL_ERROR;
    return (NSSCKFWObject *)NULL;
  }

  if( nssCKFWHash_Exists(mdObjectHash, mdObject) ) {
    fwObject = nssCKFWHash_Lookup(mdObjectHash, mdObject);
    return fwObject;
  }

  fwObject = nss_ZNEW(arena, NSSCKFWObject);
  if( (NSSCKFWObject *)NULL == fwObject ) {
    *pError = CKR_HOST_MEMORY;
    return (NSSCKFWObject *)NULL;
  }

  fwObject->arena = arena;
  fwObject->mdObject = mdObject;
  fwObject->fwSession = fwSession;

  if( (NSSCKFWSession *)NULL != fwSession ) {
    fwObject->mdSession = nssCKFWSession_GetMDSession(fwSession);
  }

  fwObject->fwToken = fwToken;

  if( (NSSCKFWToken *)NULL != fwToken ) {
    fwObject->mdToken = nssCKFWToken_GetMDToken(fwToken);
  }

  fwObject->fwInstance = fwInstance;
  fwObject->mdInstance = nssCKFWInstance_GetMDInstance(fwInstance);
  fwObject->mutex = nssCKFWInstance_CreateMutex(fwInstance, arena, pError);
  if( (NSSCKFWMutex *)NULL == fwObject->mutex ) {
    if( CKR_OK == *pError ) {
      *pError = CKR_GENERAL_ERROR;
    }
    return (NSSCKFWObject *)NULL;
  }

  *pError = nssCKFWHash_Add(mdObjectHash, mdObject, fwObject);
  if( CKR_OK != *pError ) {
    nss_ZFreeIf(fwObject);
    return (NSSCKFWObject *)NULL;
  }

#ifdef DEBUG
  *pError = object_add_pointer(fwObject);
  if( CKR_OK != *pError ) {
    nssCKFWHash_Remove(mdObjectHash, mdObject);
    nss_ZFreeIf(fwObject);
    return (NSSCKFWObject *)NULL;
  }
#endif /* DEBUG */

  *pError = CKR_OK;
  return fwObject;
}

/*
 * nssCKFWObject_Finalize
 *
 */
NSS_IMPLEMENT void
nssCKFWObject_Finalize
(
  NSSCKFWObject *fwObject
)
{
  nssCKFWHash *mdObjectHash;

#ifdef NSSDEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return;
  }
#endif /* NSSDEBUG */

  (void)nssCKFWMutex_Destroy(fwObject->mutex);

  if( (void *)NULL != (void *)fwObject->mdObject->Finalize ) {
    fwObject->mdObject->Finalize(fwObject->mdObject, fwObject,
      fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
      fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
  }

  mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
  if( (nssCKFWHash *)NULL != mdObjectHash ) {
    nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
  }

  nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
  nss_ZFreeIf(fwObject);

#ifdef DEBUG
  (void)object_remove_pointer(fwObject);
#endif /* DEBUG */

  return;
}

/*
 * nssCKFWObject_Destroy
 *
 */
NSS_IMPLEMENT void
nssCKFWObject_Destroy
(
  NSSCKFWObject *fwObject
)
{
  nssCKFWHash *mdObjectHash;

#ifdef NSSDEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return;
  }
#endif /* NSSDEBUG */

  (void)nssCKFWMutex_Destroy(fwObject->mutex);

  if( (void *)NULL != (void *)fwObject->mdObject->Destroy ) {
    fwObject->mdObject->Destroy(fwObject->mdObject, fwObject,
      fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
      fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
  }

  mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
  if( (nssCKFWHash *)NULL != mdObjectHash ) {
    nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
  }

  nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
  nss_ZFreeIf(fwObject);

#ifdef DEBUG
  (void)object_remove_pointer(fwObject);
#endif /* DEBUG */

  return;
}

/*
 * nssCKFWObject_GetMDObject
 *
 */
NSS_IMPLEMENT NSSCKMDObject *
nssCKFWObject_GetMDObject
(
  NSSCKFWObject *fwObject
)
{
#ifdef NSSDEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return (NSSCKMDObject *)NULL;
  }
#endif /* NSSDEBUG */

  return fwObject->mdObject;
}

/*
 * nssCKFWObject_GetArena
 *
 */
NSS_IMPLEMENT NSSArena *
nssCKFWObject_GetArena
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (NSSArena *)NULL;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (NSSArena *)NULL;
  }
#endif /* NSSDEBUG */

  return fwObject->arena;
}

/*
 * nssCKFWObject_SetHandle
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_SetHandle
(
  NSSCKFWObject *fwObject,
  CK_OBJECT_HANDLE hObject
)
{
#ifdef NSSDEBUG
  CK_RV error = CKR_OK;
#endif /* NSSDEBUG */

#ifdef NSSDEBUG
  error = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != error ) {
    return error;
  }
#endif /* NSSDEBUG */

  if( (CK_OBJECT_HANDLE)0 != fwObject->hObject ) {
    return CKR_GENERAL_ERROR;
  }

  fwObject->hObject = hObject;

  return CKR_OK;
}

/*
 * nssCKFWObject_GetHandle
 *
 */
NSS_IMPLEMENT CK_OBJECT_HANDLE
nssCKFWObject_GetHandle
(
  NSSCKFWObject *fwObject
)
{
#ifdef NSSDEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return (CK_OBJECT_HANDLE)0;
  }
#endif /* NSSDEBUG */

  return fwObject->hObject;
}

/*
 * nssCKFWObject_IsTokenObject
 *
 */
NSS_IMPLEMENT CK_BBOOL
nssCKFWObject_IsTokenObject
(
  NSSCKFWObject *fwObject
)
{
  CK_BBOOL b = CK_FALSE;

#ifdef NSSDEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return CK_FALSE;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->IsTokenObject ) {
    NSSItem item;
    NSSItem *pItem;
    CK_RV rv = CKR_OK;

    item.data = (void *)&b;
    item.size = sizeof(b);

    pItem = nssCKFWObject_GetAttribute(fwObject, CKA_TOKEN, &item, 
      (NSSArena *)NULL, &rv);
    if( (NSSItem *)NULL == pItem ) {
      /* Error of some type */
      b = CK_FALSE;
      goto done;
    }

    goto done;
  }

  b = fwObject->mdObject->IsTokenObject(fwObject->mdObject, fwObject, 
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);

 done:
  return b;
}

/*
 * nssCKFWObject_GetAttributeCount
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetAttributeCount
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
  CK_ULONG rv;

#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeCount ) {
    *pError = CKR_GENERAL_ERROR;
    return (CK_ULONG)0;
  }

  *pError = nssCKFWMutex_Lock(fwObject->mutex);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }

  rv = fwObject->mdObject->GetAttributeCount(fwObject->mdObject, fwObject,
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
    pError);

  (void)nssCKFWMutex_Unlock(fwObject->mutex);
  return rv;
}

/*
 * nssCKFWObject_GetAttributeTypes
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_GetAttributeTypes
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE_PTR typeArray,
  CK_ULONG ulCount
)
{
  CK_RV error = CKR_OK;

#ifdef NSSDEBUG
  error = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != error ) {
    return error;
  }

  if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) {
    return CKR_ARGUMENTS_BAD;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeTypes ) {
    return CKR_GENERAL_ERROR;
  }

  error = nssCKFWMutex_Lock(fwObject->mutex);
  if( CKR_OK != error ) {
    return error;
  }

  error = fwObject->mdObject->GetAttributeTypes(fwObject->mdObject, fwObject,
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
    typeArray, ulCount);

  (void)nssCKFWMutex_Unlock(fwObject->mutex);
  return error;
}

/*
 * nssCKFWObject_GetAttributeSize
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetAttributeSize
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
)
{
  CK_ULONG rv;

#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->GetAttributeSize ) {
    *pError = CKR_GENERAL_ERROR;
    return (CK_ULONG )0;
  }

  *pError = nssCKFWMutex_Lock(fwObject->mutex);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }

  rv = fwObject->mdObject->GetAttributeSize(fwObject->mdObject, fwObject,
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
    attribute, pError);

  (void)nssCKFWMutex_Unlock(fwObject->mutex);
  return rv;
}

/*
 * nssCKFWObject_GetAttribute
 *
 * Usual NSS allocation rules:
 * If itemOpt is not NULL, it will be returned; otherwise an NSSItem
 * will be allocated.  If itemOpt is not NULL but itemOpt->data is,
 * the buffer will be allocated; otherwise, the buffer will be used.
 * Any allocations will come from the optional arena, if one is
 * specified.
 */
NSS_IMPLEMENT NSSItem *
nssCKFWObject_GetAttribute
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE attribute,
  NSSItem *itemOpt,
  NSSArena *arenaOpt,
  CK_RV *pError
)
{
  NSSItem *rv = (NSSItem *)NULL;
  NSSCKFWItem mdItem;

#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (NSSItem *)NULL;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (NSSItem *)NULL;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->GetAttribute ) {
    *pError = CKR_GENERAL_ERROR;
    return (NSSItem *)NULL;
  }

  *pError = nssCKFWMutex_Lock(fwObject->mutex);
  if( CKR_OK != *pError ) {
    return (NSSItem *)NULL;
  }

  mdItem = fwObject->mdObject->GetAttribute(fwObject->mdObject, fwObject,
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
    attribute, pError);

  if( (NSSItem *)NULL == mdItem.item ) {
    if( CKR_OK == *pError ) {
      *pError = CKR_GENERAL_ERROR;
    }

    goto done;
  }

  if( (NSSItem *)NULL == itemOpt ) {
    rv = nss_ZNEW(arenaOpt, NSSItem);
    if( (NSSItem *)NULL == rv ) {
      *pError = CKR_HOST_MEMORY;
      goto done;
    }
  } else {
    rv = itemOpt;
  }

  if( (void *)NULL == rv->data ) {
    rv->size = mdItem.item->size;
    rv->data = nss_ZAlloc(arenaOpt, rv->size);
    if( (void *)NULL == rv->data ) {
      *pError = CKR_HOST_MEMORY;
      if( (NSSItem *)NULL == itemOpt ) {
        nss_ZFreeIf(rv);
      }
      rv = (NSSItem *)NULL;
      goto done;
    }
  } else {
    if( rv->size >= mdItem.item->size ) {
      rv->size = mdItem.item->size;
    } else {
      *pError = CKR_BUFFER_TOO_SMALL;
      /* Should we set rv->size to mdItem->size? */
      /* rv can't have been allocated */
      rv = (NSSItem *)NULL;
      goto done;
    }
  }

  (void)nsslibc_memcpy(rv->data, mdItem.item->data, rv->size);

  if (PR_TRUE == mdItem.needsFreeing) {
    PR_ASSERT(fwObject->mdObject->FreeAttribute);
    if (fwObject->mdObject->FreeAttribute) {
      *pError = fwObject->mdObject->FreeAttribute(&mdItem);
    }
  }

 done:
  (void)nssCKFWMutex_Unlock(fwObject->mutex);
  return rv;
}

/*
 * nssCKFWObject_SetAttribute
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_SetAttribute
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE attribute,
  NSSItem *value
)
{
  CK_RV error = CKR_OK;

#ifdef NSSDEBUG
  error = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != error ) {
    return error;
  }
#endif /* NSSDEBUG */

  if( CKA_TOKEN == attribute ) {
    /*
     * We're changing from a session object to a token object or 
     * vice-versa.
     */

    CK_ATTRIBUTE a;
    NSSCKFWObject *newFwObject;
    NSSCKFWObject swab;

    a.type = CKA_TOKEN;
    a.pValue = value->data;
    a.ulValueLen = value->size;

    newFwObject = nssCKFWSession_CopyObject(fwObject->fwSession, fwObject,
                    &a, 1, &error);
    if( (NSSCKFWObject *)NULL == newFwObject ) {
      if( CKR_OK == error ) {
        error = CKR_GENERAL_ERROR;
      }
      return error;
    }

    /*
     * Actually, I bet the locking is worse than this.. this part of
     * the code could probably use some scrutiny and reworking.
     */
    error = nssCKFWMutex_Lock(fwObject->mutex);
    if( CKR_OK != error ) {
      nssCKFWObject_Destroy(newFwObject);
      return error;
    }

    error = nssCKFWMutex_Lock(newFwObject->mutex);
    if( CKR_OK != error ) {
      nssCKFWMutex_Unlock(fwObject->mutex);
      nssCKFWObject_Destroy(newFwObject);
      return error;
    }

    /* 
     * Now, we have our new object, but it has a new fwObject pointer,
     * while we have to keep the existing one.  So quick swap the contents.
     */
    swab = *fwObject;
    *fwObject = *newFwObject;
    *newFwObject = swab;

    /* But keep the mutexes the same */
    swab.mutex = fwObject->mutex;
    fwObject->mutex = newFwObject->mutex;
    newFwObject->mutex = swab.mutex;

    (void)nssCKFWMutex_Unlock(newFwObject->mutex);
    (void)nssCKFWMutex_Unlock(fwObject->mutex);

    /*
     * Either remove or add this to the list of session objects
     */

    if( CK_FALSE == *(CK_BBOOL *)value->data ) {
      /* 
       * New one is a session object, except since we "stole" the fwObject, it's
       * not in the list.  Add it.
       */
      nssCKFWSession_RegisterSessionObject(fwObject->fwSession, fwObject);
    } else {
      /*
       * New one is a token object, except since we "stole" the fwObject, it's
       * in the list.  Remove it.
       */
      nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
    }

    /*
     * Now delete the old object.  Remember the names have changed.
     */
    nssCKFWObject_Destroy(newFwObject);

    return CKR_OK;
  } else {
    /*
     * An "ordinary" change.
     */
    if( (void *)NULL == (void *)fwObject->mdObject->SetAttribute ) {
      /* We could fake it with copying, like above.. later */
      return CKR_ATTRIBUTE_READ_ONLY;
    }

    error = nssCKFWMutex_Lock(fwObject->mutex);
    if( CKR_OK != error ) {
      return error;
    }

    error = fwObject->mdObject->SetAttribute(fwObject->mdObject, fwObject,
      fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
      fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
      attribute, value);

    (void)nssCKFWMutex_Unlock(fwObject->mutex);

    return error;
  }
}

/*
 * nssCKFWObject_GetObjectSize
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetObjectSize
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
  CK_ULONG rv;

#ifdef NSSDEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* NSSDEBUG */

  if( (void *)NULL == (void *)fwObject->mdObject->GetObjectSize ) {
    *pError = CKR_INFORMATION_SENSITIVE;
    return (CK_ULONG)0;
  }

  *pError = nssCKFWMutex_Lock(fwObject->mutex);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }

  rv = fwObject->mdObject->GetObjectSize(fwObject->mdObject, fwObject,
    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken, 
    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
    pError);

  (void)nssCKFWMutex_Unlock(fwObject->mutex);
  return rv;
}

/*
 * NSSCKFWObject_GetMDObject
 *
 */
NSS_IMPLEMENT NSSCKMDObject *
NSSCKFWObject_GetMDObject
(
  NSSCKFWObject *fwObject
)
{
#ifdef DEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return (NSSCKMDObject *)NULL;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetMDObject(fwObject);
}

/*
 * NSSCKFWObject_GetArena
 *
 */
NSS_IMPLEMENT NSSArena *
NSSCKFWObject_GetArena
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
#ifdef DEBUG
  if( (CK_RV *)NULL == pError ) {
    return (NSSArena *)NULL;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (NSSArena *)NULL;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetArena(fwObject, pError);
}

/*
 * NSSCKFWObject_IsTokenObject
 *
 */
NSS_IMPLEMENT CK_BBOOL
NSSCKFWObject_IsTokenObject
(
  NSSCKFWObject *fwObject
)
{
#ifdef DEBUG
  if( CKR_OK != nssCKFWObject_verifyPointer(fwObject) ) {
    return CK_FALSE;
  }
#endif /* DEBUG */

  return nssCKFWObject_IsTokenObject(fwObject);
}

/*
 * NSSCKFWObject_GetAttributeCount
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetAttributeCount
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
#ifdef DEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetAttributeCount(fwObject, pError);
}

/*
 * NSSCKFWObject_GetAttributeTypes
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWObject_GetAttributeTypes
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE_PTR typeArray,
  CK_ULONG ulCount
)
{
#ifdef DEBUG
  CK_RV error = CKR_OK;

  error = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != error ) {
    return error;
  }

  if( (CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray ) {
    return CKR_ARGUMENTS_BAD;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetAttributeTypes(fwObject, typeArray, ulCount);
}

/*
 * NSSCKFWObject_GetAttributeSize
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetAttributeSize
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
)
{
#ifdef DEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetAttributeSize(fwObject, attribute, pError);
}

/*
 * NSSCKFWObject_GetAttribute
 *
 */
NSS_IMPLEMENT NSSItem *
NSSCKFWObject_GetAttribute
(
  NSSCKFWObject *fwObject,
  CK_ATTRIBUTE_TYPE attribute,
  NSSItem *itemOpt,
  NSSArena *arenaOpt,
  CK_RV *pError
)
{
#ifdef DEBUG
  if( (CK_RV *)NULL == pError ) {
    return (NSSItem *)NULL;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (NSSItem *)NULL;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetAttribute(fwObject, attribute, itemOpt, arenaOpt, pError);
}

/*
 * NSSCKFWObject_GetObjectSize
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetObjectSize
(
  NSSCKFWObject *fwObject,
  CK_RV *pError
)
{
#ifdef DEBUG
  if( (CK_RV *)NULL == pError ) {
    return (CK_ULONG)0;
  }

  *pError = nssCKFWObject_verifyPointer(fwObject);
  if( CKR_OK != *pError ) {
    return (CK_ULONG)0;
  }
#endif /* DEBUG */

  return nssCKFWObject_GetObjectSize(fwObject, pError);
}
