/* ***** 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.
 *
 *
 * 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 ***** */
#include "secitem.h"
#include "pkcs11.h"
#include "lgdb.h"
#include "lowkeyi.h"
#include "pcert.h"
#include "blapi.h"

#include "keydbi.h" 

/*
 * This code maps PKCS #11 Finds to legacy database searches. This code
 * was orginally in pkcs11.c in previous versions of NSS.
 */

struct SDBFindStr {
    CK_OBJECT_HANDLE    *handles;
    int                 size;
    int                 index;
    int                 array_size;
};


/*
 * free a search structure
 */
void
lg_FreeSearch(SDBFind *search)
{
    if (search->handles) {
	PORT_Free(search->handles);
    }
    PORT_Free(search);
}

void
lg_addHandle(SDBFind *search, CK_OBJECT_HANDLE handle)
{
    if (search->handles == NULL) {
	return;
    }
    if (search->size >= search->array_size) {
	search->array_size += LG_SEARCH_BLOCK_SIZE;
	search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles,
				 sizeof(CK_OBJECT_HANDLE)* search->array_size);
	if (search->handles == NULL) {
	   return;
	}
    }
    search->handles[search->size] = handle;
    search->size++;
}

/*
 * find any certs that may match the template and load them.
 */
#define LG_CERT 	0x00000001
#define LG_TRUST	0x00000002
#define LG_CRL		0x00000004
#define LG_SMIME	0x00000008
#define LG_PRIVATE	0x00000010
#define LG_PUBLIC	0x00000020
#define LG_KEY		0x00000040

/*
 * structure to collect key handles.
 */
typedef struct lgEntryDataStr {
    SDB *sdb;
    SDBFind *searchHandles;
    const CK_ATTRIBUTE *template;
    CK_ULONG templ_count;
} lgEntryData;


static SECStatus
lg_crl_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
{
    lgEntryData *crlData;
    CK_OBJECT_HANDLE class_handle;
    SDB *sdb;
    
    crlData = (lgEntryData *)arg;
    sdb = crlData->sdb;

    class_handle = (type == certDBEntryTypeRevocation) ? LG_TOKEN_TYPE_CRL :
							LG_TOKEN_KRL_HANDLE;
    if (lg_tokenMatch(sdb, key, class_handle,
			crlData->template, crlData->templ_count)) {
	lg_addHandle(crlData->searchHandles,
				 lg_mkHandle(sdb,key,class_handle));
    }
    return(SECSuccess);
}

static void
lg_searchCrls(SDB *sdb, SECItem *derSubject, PRBool isKrl, 
		unsigned long classFlags, SDBFind *search,
		const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
{
    NSSLOWCERTCertDBHandle *certHandle = NULL;

    certHandle = lg_getCertDB(sdb);
    if (certHandle == NULL) {
	return;
    }
    if (derSubject->data != NULL)  {
	certDBEntryRevocation *crl = 
	    nsslowcert_FindCrlByKey(certHandle, derSubject, isKrl);

	if (crl != NULL) {
	    lg_addHandle(search, lg_mkHandle(sdb, derSubject,
		isKrl ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL));
	    nsslowcert_DestroyDBEntry((certDBEntry *)crl);
	}
    } else {
	lgEntryData crlData;

	/* traverse */
	crlData.sdb = sdb;
	crlData.searchHandles = search;
	crlData.template = pTemplate;
	crlData.templ_count = ulCount;
	nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeRevocation,
		lg_crl_collect, (void *)&crlData);
	nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeKeyRevocation,
		lg_crl_collect, (void *)&crlData);
    } 
}

/*
 * structure to collect key handles.
 */
typedef struct lgKeyDataStr {
    SDB *sdb;
    NSSLOWKEYDBHandle *keyHandle;
    SDBFind *searchHandles;
    SECItem *id;
    const CK_ATTRIBUTE *template;
    CK_ULONG templ_count;
    unsigned long classFlags;
    PRBool strict;
} lgKeyData;

static PRBool
isSecretKey(NSSLOWKEYPrivateKey *privKey)
{
  if (privKey->keyType == NSSLOWKEYRSAKey &&
                privKey->u.rsa.publicExponent.len == 1 &&
                                privKey->u.rsa.publicExponent.data[0] == 0)
    return PR_TRUE;

  return PR_FALSE;
}



static SECStatus
lg_key_collect(DBT *key, DBT *data, void *arg)
{
    lgKeyData *keyData;
    NSSLOWKEYPrivateKey *privKey = NULL;
    SECItem tmpDBKey;
    SDB *sdb;
    unsigned long classFlags;
    
    keyData = (lgKeyData *)arg;
    sdb = keyData->sdb;
    classFlags = keyData->classFlags;

    tmpDBKey.data = key->data;
    tmpDBKey.len = key->size;
    tmpDBKey.type = siBuffer;

    PORT_Assert(keyData->keyHandle);
    if (!keyData->strict && keyData->id && keyData->id->data) {
	SECItem result;
	PRBool haveMatch= PR_FALSE;
	unsigned char hashKey[SHA1_LENGTH];
	result.data = hashKey;
	result.len = sizeof(hashKey);

	if (keyData->id->len == 0) {
	    /* Make sure this isn't a LG_KEY */
	    privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle, 
					&tmpDBKey, keyData->sdb/*->password*/);
	    if (privKey) {
		/* turn off the unneeded class flags */
		classFlags &= isSecretKey(privKey) ?  ~(LG_PRIVATE|LG_PUBLIC) :
							~LG_KEY;
		haveMatch = (PRBool)
			((classFlags & (LG_KEY|LG_PRIVATE|LG_PUBLIC)) != 0);
		nsslowkey_DestroyPrivateKey(privKey);
	    }
	} else {
	    SHA1_HashBuf( hashKey, key->data, key->size ); /* match id */
	    haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
	    if (!haveMatch && ((unsigned char *)key->data)[0] == 0) {
		/* This is a fix for backwards compatibility.  The key
		 * database indexes private keys by the public key, and
		 * versions of NSS prior to 3.4 stored the public key as
		 * a signed integer.  The public key is now treated as an
		 * unsigned integer, with no leading zero.  In order to
		 * correctly compute the hash of an old key, it is necessary
		 * to fallback and detect the leading zero.
		 */
		SHA1_HashBuf(hashKey, 
		             (unsigned char *)key->data + 1, key->size - 1);
		haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
	    }
	}
	if (haveMatch) {
	    if (classFlags & LG_PRIVATE)  {
		lg_addHandle(keyData->searchHandles,
			lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
	    }
	    if (classFlags & LG_PUBLIC) {
		lg_addHandle(keyData->searchHandles,
			lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PUB));
	    }
	    if (classFlags & LG_KEY) {
		lg_addHandle(keyData->searchHandles,
			lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_KEY));
	    }
	}
	return SECSuccess;
    }

    privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle, &tmpDBKey, 
						 keyData->sdb/*->password*/);
    if ( privKey == NULL ) {
	goto loser;
    }

    if (isSecretKey(privKey)) {
	if ((classFlags & LG_KEY) && 
		lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY,
			keyData->template, keyData->templ_count)) {
	    lg_addHandle(keyData->searchHandles,
		lg_mkHandle(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY));
	}
    } else {
	if ((classFlags & LG_PRIVATE) && 
		lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PRIV,
			keyData->template, keyData->templ_count)) {
	    lg_addHandle(keyData->searchHandles,
		lg_mkHandle(keyData->sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
	}
	if ((classFlags & LG_PUBLIC) && 
		lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PUB,
			keyData->template, keyData->templ_count)) {
	    lg_addHandle(keyData->searchHandles,
		lg_mkHandle(keyData->sdb, &tmpDBKey,LG_TOKEN_TYPE_PUB));
	}
    }

loser:
    if ( privKey ) {
	nsslowkey_DestroyPrivateKey(privKey);
    }
    return(SECSuccess);
}

static void
lg_searchKeys(SDB *sdb, SECItem *key_id,
	unsigned long classFlags, SDBFind *search, PRBool mustStrict,
	const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
{
    NSSLOWKEYDBHandle *keyHandle = NULL;
    NSSLOWKEYPrivateKey *privKey;
    lgKeyData keyData;
    PRBool found = PR_FALSE;

    keyHandle = lg_getKeyDB(sdb);
    if (keyHandle == NULL) {
	return;
    }

    if (key_id->data) {
	privKey = nsslowkey_FindKeyByPublicKey(keyHandle, key_id, sdb);
	if (privKey) {
	    if ((classFlags & LG_KEY) && isSecretKey(privKey)) {
    	        lg_addHandle(search,
			lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_KEY));
		found = PR_TRUE;
	    }
	    if ((classFlags & LG_PRIVATE) && !isSecretKey(privKey)) {
    	        lg_addHandle(search,
			lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PRIV));
		found = PR_TRUE;
	    }
	    if ((classFlags & LG_PUBLIC) && !isSecretKey(privKey)) {
    	        lg_addHandle(search,
			lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PUB));
		found = PR_TRUE;
	    }
    	    nsslowkey_DestroyPrivateKey(privKey);
	}
	/* don't do the traversal if we have an up to date db */
	if (keyHandle->version != 3) {
	    goto loser;
	}
	/* don't do the traversal if it can't possibly be the correct id */
	/* all soft token id's are SHA1_HASH_LEN's */
	if (key_id->len != SHA1_LENGTH) {
	    goto loser;
	}
	if (found) {
	   /* if we already found some keys, don't do the traversal */
	   goto loser;
	}
    }
    keyData.sdb = sdb;
    keyData.keyHandle = keyHandle;
    keyData.searchHandles = search;
    keyData.id = key_id;
    keyData.template = pTemplate;
    keyData.templ_count = ulCount;
    keyData.classFlags = classFlags;
    keyData.strict = mustStrict ? mustStrict : LG_STRICT;

    nsslowkey_TraverseKeys(keyHandle, lg_key_collect, &keyData);

loser:
    return;
}

/*
 * structure to collect certs into
 */
typedef struct lgCertDataStr {
    SDB *sdb;
    int cert_count;
    int max_cert_count;
    NSSLOWCERTCertificate **certs;
    const CK_ATTRIBUTE *template;
    CK_ULONG	templ_count;
    unsigned long classFlags;
    PRBool	strict;
} lgCertData;

/*
 * collect all the certs from the traverse call.
 */	
static SECStatus
lg_cert_collect(NSSLOWCERTCertificate *cert,void *arg)
{
    lgCertData *cd = (lgCertData *)arg;

    if (cert == NULL) {
	return SECSuccess;
    }

    if (cd->certs == NULL) {
	return SECFailure;
    }

    if (cd->strict) {
	if ((cd->classFlags & LG_CERT) && !lg_tokenMatch(cd->sdb,
	  &cert->certKey, LG_TOKEN_TYPE_CERT, cd->template,cd->templ_count)) {
	    return SECSuccess;
	}
	if ((cd->classFlags & LG_TRUST) && !lg_tokenMatch(cd->sdb,
	  &cert->certKey, LG_TOKEN_TYPE_TRUST, 
					     cd->template, cd->templ_count)) {
	    return SECSuccess;
	}
    }

    /* allocate more space if we need it. This should only happen in
     * the general traversal case */
    if (cd->cert_count >= cd->max_cert_count) {
	int size;
	cd->max_cert_count += LG_SEARCH_BLOCK_SIZE;
	size = cd->max_cert_count * sizeof (NSSLOWCERTCertificate *);
	cd->certs = (NSSLOWCERTCertificate **)PORT_Realloc(cd->certs,size);
	if (cd->certs == NULL) {
	    return SECFailure;
	}
    }

    cd->certs[cd->cert_count++] = nsslowcert_DupCertificate(cert);
    return SECSuccess;
}

/* provide impedence matching ... */
static SECStatus
lg_cert_collect2(NSSLOWCERTCertificate *cert, SECItem *dymmy, void *arg)
{
    return lg_cert_collect(cert, arg);
}

static void
lg_searchSingleCert(lgCertData *certData,NSSLOWCERTCertificate *cert)
{
    if (cert == NULL) {
	    return;
    }
    if (certData->strict && 
	!lg_tokenMatch(certData->sdb, &cert->certKey, LG_TOKEN_TYPE_CERT, 
				certData->template,certData->templ_count)) {
	nsslowcert_DestroyCertificate(cert);
	return;
    }
    certData->certs = (NSSLOWCERTCertificate **) 
				PORT_Alloc(sizeof (NSSLOWCERTCertificate *));
    if (certData->certs == NULL) {
	nsslowcert_DestroyCertificate(cert);
	return;
    }
    certData->certs[0] = cert;
    certData->cert_count = 1;
}

static void
lg_CertSetupData(lgCertData *certData,int count)
{
    certData->max_cert_count = count;

    if (certData->max_cert_count <= 0) {
	return;
    }
    certData->certs = (NSSLOWCERTCertificate **)
			 PORT_Alloc( count * sizeof(NSSLOWCERTCertificate *));
    return;
}

static void
lg_searchCertsAndTrust(SDB *sdb, SECItem *derCert, SECItem *name, 
			SECItem *derSubject, NSSLOWCERTIssuerAndSN *issuerSN, 
			SECItem *email,
			unsigned long classFlags, SDBFind *handles, 
			const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
{
    NSSLOWCERTCertDBHandle *certHandle = NULL;
    lgCertData certData;
    int i;

    certHandle = lg_getCertDB(sdb);
    if (certHandle == NULL) return;

    certData.sdb = sdb;
    certData.max_cert_count = 0;
    certData.certs = NULL;
    certData.cert_count = 0;
    certData.template = pTemplate;
    certData.templ_count = ulCount;
    certData.classFlags = classFlags; 
    certData.strict = LG_STRICT; 


    /*
     * Find the Cert.
     */
    if (derCert->data != NULL) {
	NSSLOWCERTCertificate *cert = 
			nsslowcert_FindCertByDERCert(certHandle,derCert);
	lg_searchSingleCert(&certData,cert);
    } else if (name->data != NULL) {
	char *tmp_name = (char*)PORT_Alloc(name->len+1);
	int count;

	if (tmp_name == NULL) {
	    return;
	}
	PORT_Memcpy(tmp_name,name->data,name->len);
	tmp_name[name->len] = 0;

	count= nsslowcert_NumPermCertsForNickname(certHandle,tmp_name);
	lg_CertSetupData(&certData,count);
	nsslowcert_TraversePermCertsForNickname(certHandle,tmp_name,
				lg_cert_collect, &certData);
	PORT_Free(tmp_name);
    } else if (derSubject->data != NULL) {
	int count;

	count = nsslowcert_NumPermCertsForSubject(certHandle,derSubject);
	lg_CertSetupData(&certData,count);
	nsslowcert_TraversePermCertsForSubject(certHandle,derSubject,
				lg_cert_collect, &certData);
    } else if ((issuerSN->derIssuer.data != NULL) && 
			(issuerSN->serialNumber.data != NULL)) {
        if (classFlags & LG_CERT) {
	    NSSLOWCERTCertificate *cert = 
		nsslowcert_FindCertByIssuerAndSN(certHandle,issuerSN);

	    lg_searchSingleCert(&certData,cert);
	}
	if (classFlags & LG_TRUST) {
	    NSSLOWCERTTrust *trust = 
		nsslowcert_FindTrustByIssuerAndSN(certHandle, issuerSN);

	    if (trust) {
		lg_addHandle(handles,
		    lg_mkHandle(sdb,&trust->dbKey,LG_TOKEN_TYPE_TRUST));
		nsslowcert_DestroyTrust(trust);
	    }
	}
    } else if (email->data != NULL) {
	char *tmp_name = (char*)PORT_Alloc(email->len+1);
	certDBEntrySMime *entry = NULL;

	if (tmp_name == NULL) {
	    return;
	}
	PORT_Memcpy(tmp_name,email->data,email->len);
	tmp_name[email->len] = 0;

	entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
	if (entry) {
	    int count;
	    SECItem *subjectName = &entry->subjectName;

	    count = nsslowcert_NumPermCertsForSubject(certHandle, subjectName);
	    lg_CertSetupData(&certData,count);
	    nsslowcert_TraversePermCertsForSubject(certHandle, subjectName, 
						lg_cert_collect, &certData);

	    nsslowcert_DestroyDBEntry((certDBEntry *)entry);
	}
	PORT_Free(tmp_name);
    } else {
	/* we aren't filtering the certs, we are working on all, so turn
	 * on the strict filters. */
	certData.strict = PR_TRUE;
	lg_CertSetupData(&certData,LG_SEARCH_BLOCK_SIZE);
	nsslowcert_TraversePermCerts(certHandle, lg_cert_collect2, &certData);
    }

    /*
     * build the handles
     */	
    for (i=0 ; i < certData.cert_count ; i++) {
	NSSLOWCERTCertificate *cert = certData.certs[i];

	/* if we filtered it would have been on the stuff above */
	if (classFlags & LG_CERT) {
	    lg_addHandle(handles,
		lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT));
	}
	if ((classFlags & LG_TRUST) && nsslowcert_hasTrust(cert->trust)) {
	    lg_addHandle(handles,
		lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST));
	}
	nsslowcert_DestroyCertificate(cert);
    }

    if (certData.certs) PORT_Free(certData.certs);
    return;
}

static SECStatus
lg_smime_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
{
    lgEntryData *smimeData;
    SDB *sdb;
    
    smimeData = (lgEntryData *)arg;
    sdb = smimeData->sdb;

    if (lg_tokenMatch(sdb, key, LG_TOKEN_TYPE_SMIME,
			smimeData->template, smimeData->templ_count)) {
	lg_addHandle(smimeData->searchHandles,
				 lg_mkHandle(sdb,key,LG_TOKEN_TYPE_SMIME));
    }
    return(SECSuccess);
}

static void
lg_searchSMime(SDB *sdb, SECItem *email, SDBFind *handles, 
			const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
{
    NSSLOWCERTCertDBHandle *certHandle = NULL;
    certDBEntrySMime *entry;

    certHandle = lg_getCertDB(sdb);
    if (certHandle == NULL) return;

    if (email->data != NULL) {
	char *tmp_name = (char*)PORT_Alloc(email->len+1);

	if (tmp_name == NULL) {
	    return;
	}
	PORT_Memcpy(tmp_name,email->data,email->len);
	tmp_name[email->len] = 0;

	entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
	if (entry) {
	    SECItem emailKey;

	    emailKey.data = (unsigned char *)tmp_name;
	    emailKey.len = PORT_Strlen(tmp_name)+1;
	    emailKey.type = 0;
	    lg_addHandle(handles,
		lg_mkHandle(sdb,&emailKey,LG_TOKEN_TYPE_SMIME));
	    nsslowcert_DestroyDBEntry((certDBEntry *)entry);
	}
	PORT_Free(tmp_name);
    } else {
	/* traverse */
	lgEntryData smimeData;

	/* traverse */
	smimeData.sdb = sdb;
	smimeData.searchHandles = handles;
	smimeData.template = pTemplate;
	smimeData.templ_count = ulCount;
	nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeSMimeProfile,
		lg_smime_collect, (void *)&smimeData);
    }
    return;
}

static CK_RV
lg_searchTokenList(SDB *sdb, SDBFind *search,
	 		const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
{
    int i;
    PRBool isKrl = PR_FALSE;
    SECItem derCert = { siBuffer, NULL, 0 };
    SECItem derSubject = { siBuffer, NULL, 0 };
    SECItem name = { siBuffer, NULL, 0 };
    SECItem email = { siBuffer, NULL, 0 };
    SECItem key_id = { siBuffer, NULL, 0 };
    SECItem cert_sha1_hash = { siBuffer, NULL, 0 };
    SECItem cert_md5_hash  = { siBuffer, NULL, 0 };
    NSSLOWCERTIssuerAndSN issuerSN = {
	{ siBuffer, NULL, 0 },
	{ siBuffer, NULL, 0 }
    };
    SECItem *copy = NULL;
    CK_CERTIFICATE_TYPE certType;
    CK_OBJECT_CLASS objectClass;
    CK_RV crv;
    unsigned long classFlags;

    if (lg_getCertDB(sdb) == NULL) {
	classFlags = LG_PRIVATE|LG_KEY;
    } else {
	classFlags = LG_CERT|LG_TRUST|LG_PUBLIC|LG_SMIME|LG_CRL;
    }

    /*
     * look for things to search on token objects for. If the right options
     * are specified, we can use them as direct indeces into the database
     * (rather than using linear searches. We can also use the attributes to
     * limit the kinds of objects we are searching for. Later we can use this
     * array to filter the remaining objects more finely.
     */
    for (i=0 ;classFlags && i < (int)ulCount; i++) {

	switch (pTemplate[i].type) {
	case CKA_SUBJECT:
	    copy = &derSubject;
	    classFlags &= (LG_CERT|LG_PRIVATE|LG_PUBLIC|LG_SMIME|LG_CRL);
	    break;
	case CKA_ISSUER: 
	    copy = &issuerSN.derIssuer;
	    classFlags &= (LG_CERT|LG_TRUST);
	    break;
	case CKA_SERIAL_NUMBER: 
	    copy = &issuerSN.serialNumber;
	    classFlags &= (LG_CERT|LG_TRUST);
	    break;
	case CKA_VALUE:
	    copy = &derCert;
	    classFlags &= (LG_CERT|LG_CRL|LG_SMIME);
	    break;
	case CKA_LABEL:
	    copy = &name;
	    break;
	case CKA_NETSCAPE_EMAIL:
	    copy = &email;
	    classFlags &= LG_SMIME|LG_CERT;
	    break;
	case CKA_NETSCAPE_SMIME_TIMESTAMP:
	    classFlags &= LG_SMIME;
	    break;
	case CKA_CLASS:
	    crv = lg_GetULongAttribute(CKA_CLASS,&pTemplate[i],1, &objectClass);
	    if (crv != CKR_OK) {
		classFlags = 0;
		break;
	    }
	    switch (objectClass) {
	    case CKO_CERTIFICATE:
		classFlags &= LG_CERT;
		break;
	    case CKO_NETSCAPE_TRUST:
		classFlags &= LG_TRUST;
		break;
	    case CKO_NETSCAPE_CRL:
		classFlags &= LG_CRL;
		break;
	    case CKO_NETSCAPE_SMIME:
		classFlags &= LG_SMIME;
		break;
	    case CKO_PRIVATE_KEY:
		classFlags &= LG_PRIVATE;
		break;
	    case CKO_PUBLIC_KEY:
		classFlags &= LG_PUBLIC;
		break;
	    case CKO_SECRET_KEY:
		classFlags &= LG_KEY;
		break;
	    default:
		classFlags = 0;
		break;
	    }
	    break;
	case CKA_PRIVATE:
	    if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
		classFlags = 0;
		break;
	    }
	    if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
		classFlags &= (LG_PRIVATE|LG_KEY);
	    } else {
		classFlags &= ~(LG_PRIVATE|LG_KEY);
	    }
	    break;
	case CKA_SENSITIVE:
	    if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
		classFlags = 0;
		break;
	    }
	    if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
		classFlags &= (LG_PRIVATE|LG_KEY);
	    } else {
		classFlags = 0;
	    }
	    break;
	case CKA_TOKEN:
	    if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
		classFlags = 0;
		break;
	    }
	    if (*((CK_BBOOL *)pTemplate[i].pValue) != CK_TRUE) {
		classFlags = 0;
	    }
	    break;
	case CKA_CERT_SHA1_HASH:
	    classFlags &= LG_TRUST;
	    copy = &cert_sha1_hash; break;
	case CKA_CERT_MD5_HASH:
	    classFlags &= LG_TRUST;
	    copy = &cert_md5_hash; break;
	case CKA_CERTIFICATE_TYPE:
	    crv = lg_GetULongAttribute(CKA_CERTIFICATE_TYPE,&pTemplate[i],
								1,&certType);
	    if (crv != CKR_OK) {
		classFlags = 0;
		break;
	    }
	    classFlags &= LG_CERT;
	    if (certType != CKC_X_509) {
		classFlags = 0;
	    }
	    break;
	case CKA_ID:
	    copy = &key_id;
	    classFlags &= (LG_CERT|LG_PRIVATE|LG_KEY|LG_PUBLIC);
	    break;
	case CKA_NETSCAPE_KRL:
	    if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
		classFlags = 0;
		break;
	    }
	    classFlags &= LG_CRL;
	    isKrl = (PRBool)(*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE);
	    break;
	case CKA_MODIFIABLE:
	     break;
	case CKA_KEY_TYPE:
	case CKA_DERIVE:
	    classFlags &= LG_PUBLIC|LG_PRIVATE|LG_KEY;
	    break;
	case CKA_VERIFY_RECOVER:
	    classFlags &= LG_PUBLIC;
	    break;
	case CKA_SIGN_RECOVER:
	    classFlags &= LG_PRIVATE;
	    break;
	case CKA_ENCRYPT:
	case CKA_VERIFY:
	case CKA_WRAP:
	    classFlags &= LG_PUBLIC|LG_KEY;
	    break;
	case CKA_DECRYPT:
	case CKA_SIGN:
	case CKA_UNWRAP:
	case CKA_ALWAYS_SENSITIVE:
	case CKA_EXTRACTABLE:
	case CKA_NEVER_EXTRACTABLE:
	    classFlags &= LG_PRIVATE|LG_KEY;
	    break;
	/* can't be a certificate if it doesn't match one of the above 
	 * attributes */
	default: 
	     classFlags  = 0;
	     break;
	}
 	if (copy) {
	    copy->data = (unsigned char*)pTemplate[i].pValue;
	    copy->len = pTemplate[i].ulValueLen;
	}
	copy = NULL;
    }

    /* certs */
    if (classFlags & (LG_CERT|LG_TRUST)) {
	lg_searchCertsAndTrust(sdb,&derCert,&name,&derSubject,
				 &issuerSN, &email,classFlags,search, 
				pTemplate, ulCount);
    }

    /* keys */
    if (classFlags & (LG_PRIVATE|LG_PUBLIC|LG_KEY)) {
	PRBool mustStrict = (name.len != 0);
	lg_searchKeys(sdb, &key_id, classFlags, search,
			 mustStrict, pTemplate, ulCount);
    }

    /* crl's */
    if (classFlags & LG_CRL) {
	lg_searchCrls(sdb, &derSubject, isKrl, classFlags, search,
			pTemplate, ulCount);
    }
    /* Add S/MIME entry stuff */
    if (classFlags & LG_SMIME) {
	lg_searchSMime(sdb, &email, search, pTemplate, ulCount);
    }
    return CKR_OK;
}
	

/* lg_FindObjectsInit initializes a search for token and session objects 
 * that match a template. */
CK_RV lg_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *pTemplate,
			 CK_ULONG ulCount, SDBFind **retSearch)
{
    SDBFind *search;
    CK_RV crv = CKR_OK;
  
    *retSearch = NULL; 

    search = (SDBFind *)PORT_Alloc(sizeof(SDBFind));
    if (search == NULL) {
	crv = CKR_HOST_MEMORY;
	goto loser;
    }
    search->handles = (CK_OBJECT_HANDLE *)
		PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * LG_SEARCH_BLOCK_SIZE);
    if (search->handles == NULL) {
	crv = CKR_HOST_MEMORY;
	goto loser;
    }
    search->index = 0;
    search->size = 0;
    search->array_size = LG_SEARCH_BLOCK_SIZE;
    /* FIXME - do we still need to get Login state? */

    crv = lg_searchTokenList(sdb, search, pTemplate, ulCount);
    if (crv != CKR_OK) {
	goto loser;
    }

    *retSearch = search;
    return CKR_OK;

loser:
    if (search) {
	lg_FreeSearch(search);
    }
    return crv;
}


/* lg_FindObjects continues a search for token and session objects 
 * that match a template, obtaining additional object handles. */
CK_RV lg_FindObjects(SDB *sdb, SDBFind *search, 
    CK_OBJECT_HANDLE *phObject,CK_ULONG ulMaxObjectCount,
    					CK_ULONG *pulObjectCount)
{
    int	transfer;
    int left;

    *pulObjectCount = 0;
    left = search->size - search->index;
    transfer = ((int)ulMaxObjectCount > left) ? left : ulMaxObjectCount;
    if (transfer > 0) {
	PORT_Memcpy(phObject,&search->handles[search->index],
                                        transfer*sizeof(CK_OBJECT_HANDLE));
    } else {
       *phObject = CK_INVALID_HANDLE;
    }

    search->index += transfer;
    *pulObjectCount = transfer;
    return CKR_OK;
}

/* lg_FindObjectsFinal finishes a search for token and session objects. */
CK_RV lg_FindObjectsFinal(SDB* lgdb, SDBFind *search)
{

    if (search != NULL) {
	lg_FreeSearch(search);
    }
    return CKR_OK;
}
