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

#ifdef DEBUG
static const char CVS_ID[] = "@(#) $RCSfile: sessobj.c,v $ $Revision: 1.14 $ $Date: 2009/02/09 07:55:53 $";
#endif /* DEBUG */

/*
 * sessobj.c
 *
 * This file contains an NSSCKMDObject implementation for session 
 * objects.  The framework uses this implementation to manage
 * session objects when a Module doesn't wish to be bothered.
 */

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

/*
 * nssCKMDSessionObject
 *
 *  -- create --
 *  nssCKMDSessionObject_Create
 *
 *  -- EPV calls --
 *  nss_ckmdSessionObject_Finalize
 *  nss_ckmdSessionObject_IsTokenObject
 *  nss_ckmdSessionObject_GetAttributeCount
 *  nss_ckmdSessionObject_GetAttributeTypes
 *  nss_ckmdSessionObject_GetAttributeSize
 *  nss_ckmdSessionObject_GetAttribute
 *  nss_ckmdSessionObject_SetAttribute
 *  nss_ckmdSessionObject_GetObjectSize
 */

struct nssCKMDSessionObjectStr {
  CK_ULONG n;
  NSSArena *arena;
  NSSItem *attributes;
  CK_ATTRIBUTE_TYPE_PTR types;
  nssCKFWHash *hash;
};
typedef struct nssCKMDSessionObjectStr nssCKMDSessionObject;

#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
nss_ckmdSessionObject_add_pointer
(
  const NSSCKMDObject *mdObject
)
{
  return CKR_OK;
}

static CK_RV
nss_ckmdSessionObject_remove_pointer
(
  const NSSCKMDObject *mdObject
)
{
  return CKR_OK;
}

#ifdef NSS_DEBUG
static CK_RV
nss_ckmdSessionObject_verifyPointer
(
  const NSSCKMDObject *mdObject
)
{
  return CKR_OK;
}
#endif

#endif /* DEBUG */

/*
 * We must forward-declare these routines
 */
static void
nss_ckmdSessionObject_Finalize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
);

static CK_RV
nss_ckmdSessionObject_Destroy
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
);

static CK_BBOOL
nss_ckmdSessionObject_IsTokenObject
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
);

static CK_ULONG
nss_ckmdSessionObject_GetAttributeCount
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_RV *pError
);

static CK_RV
nss_ckmdSessionObject_GetAttributeTypes
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE_PTR typeArray,
  CK_ULONG ulCount
);

static CK_ULONG
nss_ckmdSessionObject_GetAttributeSize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
);

static NSSCKFWItem
nss_ckmdSessionObject_GetAttribute
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
);

static CK_RV
nss_ckmdSessionObject_SetAttribute
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  NSSItem *value
);

static CK_ULONG
nss_ckmdSessionObject_GetObjectSize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_RV *pError
);

/*
 * nssCKMDSessionObject_Create
 *
 */
NSS_IMPLEMENT NSSCKMDObject *
nssCKMDSessionObject_Create
(
  NSSCKFWToken *fwToken,
  NSSArena *arena,
  CK_ATTRIBUTE_PTR attributes,
  CK_ULONG ulCount,
  CK_RV *pError
)
{
  NSSCKMDObject *mdObject = (NSSCKMDObject *)NULL;
  nssCKMDSessionObject *mdso = (nssCKMDSessionObject *)NULL;
  CK_ULONG i;
  nssCKFWHash *hash;

  *pError = CKR_OK;

  mdso = nss_ZNEW(arena, nssCKMDSessionObject);
  if (!mdso) {
    goto loser;
  }

  mdso->arena = arena;
  mdso->n = ulCount;
  mdso->attributes = nss_ZNEWARRAY(arena, NSSItem, ulCount);
  if (!mdso->attributes) {
    goto loser;
  }

  mdso->types = nss_ZNEWARRAY(arena, CK_ATTRIBUTE_TYPE, ulCount);
  if (!mdso->types) {
    goto loser;
  }
  for( i = 0; i < ulCount; i++ ) {
    mdso->types[i] = attributes[i].type;
    mdso->attributes[i].size = attributes[i].ulValueLen;
    mdso->attributes[i].data = nss_ZAlloc(arena, attributes[i].ulValueLen);
    if (!mdso->attributes[i].data) {
      goto loser;
    }
    (void)nsslibc_memcpy(mdso->attributes[i].data, attributes[i].pValue,
      attributes[i].ulValueLen);
  }

  mdObject = nss_ZNEW(arena, NSSCKMDObject);
  if (!mdObject) {
    goto loser;
  }

  mdObject->etc = (void *)mdso;
  mdObject->Finalize = nss_ckmdSessionObject_Finalize;
  mdObject->Destroy = nss_ckmdSessionObject_Destroy;
  mdObject->IsTokenObject = nss_ckmdSessionObject_IsTokenObject;
  mdObject->GetAttributeCount = nss_ckmdSessionObject_GetAttributeCount;
  mdObject->GetAttributeTypes = nss_ckmdSessionObject_GetAttributeTypes;
  mdObject->GetAttributeSize = nss_ckmdSessionObject_GetAttributeSize;
  mdObject->GetAttribute = nss_ckmdSessionObject_GetAttribute;
  mdObject->SetAttribute = nss_ckmdSessionObject_SetAttribute;
  mdObject->GetObjectSize = nss_ckmdSessionObject_GetObjectSize;

  hash = nssCKFWToken_GetSessionObjectHash(fwToken);
  if (!hash) {
    *pError = CKR_GENERAL_ERROR;
    goto loser;
  }

  mdso->hash = hash;

  *pError = nssCKFWHash_Add(hash, mdObject, mdObject);
  if( CKR_OK != *pError ) {
    goto loser;
  }

#ifdef DEBUG
  if(( *pError = nss_ckmdSessionObject_add_pointer(mdObject)) != CKR_OK ) {
    goto loser;
  }
#endif /* DEBUG */

  return mdObject;

 loser:
  if (mdso) {
    if (mdso->attributes) {
      for( i = 0; i < ulCount; i++ ) {
        nss_ZFreeIf(mdso->attributes[i].data);
      }
      nss_ZFreeIf(mdso->attributes);
    }
    nss_ZFreeIf(mdso->types);
    nss_ZFreeIf(mdso);
  }

  nss_ZFreeIf(mdObject);
  if (*pError == CKR_OK) {
      *pError = CKR_HOST_MEMORY;
  }
  return (NSSCKMDObject *)NULL;
}

/*
 * nss_ckmdSessionObject_Finalize
 *
 */
static void
nss_ckmdSessionObject_Finalize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
)
{
  /* This shouldn't ever be called */
  return;
}

/*
 * nss_ckmdSessionObject_Destroy
 *
 */

static CK_RV
nss_ckmdSessionObject_Destroy
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
)
{
#ifdef NSSDEBUG
  CK_RV error = CKR_OK;
#endif /* NSSDEBUG */
  nssCKMDSessionObject *mdso;
  CK_ULONG i;

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

  mdso = (nssCKMDSessionObject *)mdObject->etc;

  nssCKFWHash_Remove(mdso->hash, mdObject);

  for( i = 0; i < mdso->n; i++ ) {
    nss_ZFreeIf(mdso->attributes[i].data);
  }
  nss_ZFreeIf(mdso->attributes);
  nss_ZFreeIf(mdso->types);
  nss_ZFreeIf(mdso);
  nss_ZFreeIf(mdObject);

#ifdef DEBUG
  (void)nss_ckmdSessionObject_remove_pointer(mdObject);
#endif /* DEBUG */

  return CKR_OK;
}

/*
 * nss_ckmdSessionObject_IsTokenObject
 *
 */

static CK_BBOOL
nss_ckmdSessionObject_IsTokenObject
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
)
{
#ifdef NSSDEBUG
  if( CKR_OK != nss_ckmdSessionObject_verifyPointer(mdObject) ) {
    return CK_FALSE;
  }
#endif /* NSSDEBUG */

  /*
   * This implementation is only ever used for session objects.
   */
  return CK_FALSE;
}

/*
 * nss_ckmdSessionObject_GetAttributeCount
 *
 */
static CK_ULONG
nss_ckmdSessionObject_GetAttributeCount
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_RV *pError
)
{
  nssCKMDSessionObject *obj;

#ifdef NSSDEBUG
  if (!pError) {
    return 0;
  }

  *pError = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != *pError ) {
    return 0;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  return obj->n;
}

/*
 * nss_ckmdSessionObject_GetAttributeTypes
 *
 */
static CK_RV
nss_ckmdSessionObject_GetAttributeTypes
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE_PTR typeArray,
  CK_ULONG ulCount
)
{
#ifdef NSSDEBUG
  CK_RV error = CKR_OK;
#endif /* NSSDEBUG */
  nssCKMDSessionObject *obj;

#ifdef NSSDEBUG
  error = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != error ) {
    return error;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  if( ulCount < obj->n ) {
    return CKR_BUFFER_TOO_SMALL;
  }

  (void)nsslibc_memcpy(typeArray, obj->types, 
    sizeof(CK_ATTRIBUTE_TYPE) * obj->n);

  return CKR_OK;
}

/*
 * nss_ckmdSessionObject_GetAttributeSize
 *
 */
static CK_ULONG
nss_ckmdSessionObject_GetAttributeSize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
)
{
  nssCKMDSessionObject *obj;
  CK_ULONG i;

#ifdef NSSDEBUG
  if (!pError) {
    return 0;
  }

  *pError = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != *pError ) {
    return 0;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  for( i = 0; i < obj->n; i++ ) {
    if( attribute == obj->types[i] ) {
      return (CK_ULONG)(obj->attributes[i].size);
    }
  }

  *pError = CKR_ATTRIBUTE_TYPE_INVALID;
  return 0;
}

/*
 * nss_ckmdSessionObject_GetAttribute
 *
 */
static NSSCKFWItem
nss_ckmdSessionObject_GetAttribute
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  CK_RV *pError
)
{
  NSSCKFWItem item;
  nssCKMDSessionObject *obj;
  CK_ULONG i;

  item.needsFreeing = PR_FALSE;
  item.item = NULL;
#ifdef NSSDEBUG
  if (!pError) {
    return item;
  }

  *pError = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != *pError ) {
    return item;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  for( i = 0; i < obj->n; i++ ) {
    if( attribute == obj->types[i] ) {
      item.item = &obj->attributes[i];
      return item;
    }
  }

  *pError = CKR_ATTRIBUTE_TYPE_INVALID;
  return item;
}

/*
 * nss_ckmdSessionObject_SetAttribute
 *
 */

/*
 * Okay, so this implementation sucks.  It doesn't support removing
 * an attribute (if value == NULL), and could be more graceful about
 * memory.  It should allow "blank" slots in the arrays, with some
 * invalid attribute type, and then it could support removal much
 * more easily.  Do this later.
 */
static CK_RV
nss_ckmdSessionObject_SetAttribute
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_ATTRIBUTE_TYPE attribute,
  NSSItem *value
)
{
  nssCKMDSessionObject *obj;
  CK_ULONG i;
  NSSItem n;
  NSSItem *ra;
  CK_ATTRIBUTE_TYPE_PTR rt;
#ifdef NSSDEBUG
  CK_RV error;
#endif /* NSSDEBUG */

#ifdef NSSDEBUG
  error = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != error ) {
    return 0;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  n.size = value->size;
  n.data = nss_ZAlloc(obj->arena, n.size);
  if (!n.data) {
    return CKR_HOST_MEMORY;
  }
  (void)nsslibc_memcpy(n.data, value->data, n.size);

  for( i = 0; i < obj->n; i++ ) {
    if( attribute == obj->types[i] ) {
      nss_ZFreeIf(obj->attributes[i].data);
      obj->attributes[i] = n;
      return CKR_OK;
    }
  }

  /*
   * It's new.
   */

  ra = (NSSItem *)nss_ZRealloc(obj->attributes, sizeof(NSSItem) * (obj->n + 1));
  if (!ra) {
    nss_ZFreeIf(n.data);
    return CKR_HOST_MEMORY;
  }

  rt = (CK_ATTRIBUTE_TYPE_PTR)nss_ZRealloc(obj->types, (obj->n + 1));
  if( (CK_ATTRIBUTE_TYPE_PTR)NULL == rt ) {
    nss_ZFreeIf(n.data);
    obj->attributes = (NSSItem *)nss_ZRealloc(ra, sizeof(NSSItem) * obj->n);
    if (!obj->attributes) {
      return CKR_GENERAL_ERROR;
    }
    return CKR_HOST_MEMORY;
  }

  obj->attributes = ra;
  obj->types = rt;
  obj->attributes[obj->n] = n;
  obj->types[obj->n] = attribute;
  obj->n++;

  return CKR_OK;
}

/*
 * nss_ckmdSessionObject_GetObjectSize
 *
 */
static CK_ULONG
nss_ckmdSessionObject_GetObjectSize
(
  NSSCKMDObject *mdObject,
  NSSCKFWObject *fwObject,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  CK_RV *pError
)
{
  nssCKMDSessionObject *obj;
  CK_ULONG i;
  CK_ULONG rv = (CK_ULONG)0;

#ifdef NSSDEBUG
  if (!pError) {
    return 0;
  }

  *pError = nss_ckmdSessionObject_verifyPointer(mdObject);
  if( CKR_OK != *pError ) {
    return 0;
  }

  /* We could even check all the other arguments, for sanity. */
#endif /* NSSDEBUG */

  obj = (nssCKMDSessionObject *)mdObject->etc;

  for( i = 0; i < obj->n; i++ ) {
    rv += obj->attributes[i].size;
  }

  rv += sizeof(NSSItem) * obj->n;
  rv += sizeof(CK_ATTRIBUTE_TYPE) * obj->n;
  rv += sizeof(nssCKMDSessionObject);

  return rv;
}

/*
 * nssCKMDFindSessionObjects
 *
 *  -- create --
 *  nssCKMDFindSessionObjects_Create
 *
 *  -- EPV calls --
 *  nss_ckmdFindSessionObjects_Final
 *  nss_ckmdFindSessionObjects_Next
 */

struct nodeStr {
  struct nodeStr *next;
  NSSCKMDObject *mdObject;
};

struct nssCKMDFindSessionObjectsStr {
  NSSArena *arena;
  CK_RV error;
  CK_ATTRIBUTE_PTR pTemplate;
  CK_ULONG ulCount;
  struct nodeStr *list;
  nssCKFWHash *hash;

};
typedef struct nssCKMDFindSessionObjectsStr nssCKMDFindSessionObjects;

#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
nss_ckmdFindSessionObjects_add_pointer
(
  const NSSCKMDFindObjects *mdFindObjects
)
{
  return CKR_OK;
}

static CK_RV
nss_ckmdFindSessionObjects_remove_pointer
(
  const NSSCKMDFindObjects *mdFindObjects
)
{
  return CKR_OK;
}

#ifdef NSS_DEBUG
static CK_RV
nss_ckmdFindSessionObjects_verifyPointer
(
  const NSSCKMDFindObjects *mdFindObjects
)
{
  return CKR_OK;
}
#endif

#endif /* DEBUG */

/*
 * We must forward-declare these routines.
 */
static void
nss_ckmdFindSessionObjects_Final
(
  NSSCKMDFindObjects *mdFindObjects,
  NSSCKFWFindObjects *fwFindObjects,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
);

static NSSCKMDObject *
nss_ckmdFindSessionObjects_Next
(
  NSSCKMDFindObjects *mdFindObjects,
  NSSCKFWFindObjects *fwFindObjects,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  NSSArena *arena,
  CK_RV *pError
);

static CK_BBOOL
items_match
(
  NSSItem *a,
  CK_VOID_PTR pValue,
  CK_ULONG ulValueLen
)
{
  if( a->size != ulValueLen ) {
    return CK_FALSE;
  }

  if( PR_TRUE == nsslibc_memequal(a->data, pValue, ulValueLen, (PRStatus *)NULL) ) {
    return CK_TRUE;
  } else {
    return CK_FALSE;
  }
}

/*
 * Our hashtable iterator
 */
static void
findfcn
(
  const void *key,
  void *value,
  void *closure
)
{
  NSSCKMDObject *mdObject = (NSSCKMDObject *)value;
  nssCKMDSessionObject *mdso = (nssCKMDSessionObject *)mdObject->etc;
  nssCKMDFindSessionObjects *mdfso = (nssCKMDFindSessionObjects *)closure;
  CK_ULONG i, j;
  struct nodeStr *node;

  if( CKR_OK != mdfso->error ) {
    return;
  }

  for( i = 0; i < mdfso->ulCount; i++ ) {
    CK_ATTRIBUTE_PTR p = &mdfso->pTemplate[i];

    for( j = 0; j < mdso->n; j++ ) {
      if( mdso->types[j] == p->type ) {
        if( !items_match(&mdso->attributes[j], p->pValue, p->ulValueLen) ) {
          return;
        } else {
          break;
        }
      }
    }

    if( j == mdso->n ) {
      /* Attribute not found */
      return;
    }
  }

  /* Matches */
  node = nss_ZNEW(mdfso->arena, struct nodeStr);
  if( (struct nodeStr *)NULL == node ) {
    mdfso->error = CKR_HOST_MEMORY;
    return;
  }

  node->mdObject = mdObject;
  node->next = mdfso->list;
  mdfso->list = node;

  return;
}

/*
 * nssCKMDFindSessionObjects_Create
 *
 */
NSS_IMPLEMENT NSSCKMDFindObjects *
nssCKMDFindSessionObjects_Create
(
  NSSCKFWToken *fwToken,
  CK_ATTRIBUTE_PTR pTemplate,
  CK_ULONG ulCount,
  CK_RV *pError
)
{
  NSSArena *arena;
  nssCKMDFindSessionObjects *mdfso;
  nssCKFWHash *hash;
  NSSCKMDFindObjects *rv;

#ifdef NSSDEBUG
  if (!pError) {
    return (NSSCKMDFindObjects *)NULL;
  }

  *pError = nssCKFWToken_verifyPointer(fwToken);
  if( CKR_OK != *pError ) {
    return (NSSCKMDFindObjects *)NULL;
  }

  if( (CK_ATTRIBUTE_PTR)NULL == pTemplate ) {
    *pError = CKR_ARGUMENTS_BAD;
    return (NSSCKMDFindObjects *)NULL;
  }
#endif /* NSSDEBUG */

  *pError = CKR_OK;

  hash = nssCKFWToken_GetSessionObjectHash(fwToken);
  if (!hash) {
    *pError= CKR_GENERAL_ERROR;
    return (NSSCKMDFindObjects *)NULL;
  }

  arena = NSSArena_Create();
  if (!arena) {
    *pError = CKR_HOST_MEMORY;
    return (NSSCKMDFindObjects *)NULL;
  }

  mdfso = nss_ZNEW(arena, nssCKMDFindSessionObjects);
  if (!mdfso) {
    goto loser;
  }

  rv = nss_ZNEW(arena, NSSCKMDFindObjects);
  if(rv == NULL) {
    goto loser;
  }

  mdfso->error = CKR_OK;
  mdfso->pTemplate = pTemplate;
  mdfso->ulCount = ulCount;
  mdfso->hash = hash;

  nssCKFWHash_Iterate(hash, findfcn, mdfso);

  if( CKR_OK != mdfso->error ) {
    goto loser;
  }

  rv->etc = (void *)mdfso;
  rv->Final = nss_ckmdFindSessionObjects_Final;
  rv->Next = nss_ckmdFindSessionObjects_Next;

#ifdef DEBUG
  if( (*pError = nss_ckmdFindSessionObjects_add_pointer(rv)) != CKR_OK ) {
    goto loser;
  }
#endif /* DEBUG */    
  mdfso->arena = arena;

  return rv;

loser:
  if (arena) {
    NSSArena_Destroy(arena);
  }
  if (*pError == CKR_OK) {
      *pError = CKR_HOST_MEMORY;
  }
  return NULL;
}

static void
nss_ckmdFindSessionObjects_Final
(
  NSSCKMDFindObjects *mdFindObjects,
  NSSCKFWFindObjects *fwFindObjects,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance
)
{
  nssCKMDFindSessionObjects *mdfso;

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

  mdfso = (nssCKMDFindSessionObjects *)mdFindObjects->etc;
  if (mdfso->arena) NSSArena_Destroy(mdfso->arena);

#ifdef DEBUG
  (void)nss_ckmdFindSessionObjects_remove_pointer(mdFindObjects);
#endif /* DEBUG */

  return;
}

static NSSCKMDObject *
nss_ckmdFindSessionObjects_Next
(
  NSSCKMDFindObjects *mdFindObjects,
  NSSCKFWFindObjects *fwFindObjects,
  NSSCKMDSession *mdSession,
  NSSCKFWSession *fwSession,
  NSSCKMDToken *mdToken,
  NSSCKFWToken *fwToken,
  NSSCKMDInstance *mdInstance,
  NSSCKFWInstance *fwInstance,
  NSSArena *arena,
  CK_RV *pError
)
{
  nssCKMDFindSessionObjects *mdfso;
  NSSCKMDObject *rv = (NSSCKMDObject *)NULL;

#ifdef NSSDEBUG
  if( CKR_OK != nss_ckmdFindSessionObjects_verifyPointer(mdFindObjects) ) {
    return (NSSCKMDObject *)NULL;
  }
#endif /* NSSDEBUG */

  mdfso = (nssCKMDFindSessionObjects *)mdFindObjects->etc;

  while (!rv) {
    if( (struct nodeStr *)NULL == mdfso->list ) {
      *pError = CKR_OK;
      return (NSSCKMDObject *)NULL;
    }

    if( nssCKFWHash_Exists(mdfso->hash, mdfso->list->mdObject) ) {
      rv = mdfso->list->mdObject;
    }

    mdfso->list = mdfso->list->next;
  }

  return rv;
}
