text: /*
   * Copyright (c) 2013 SURFnet bv
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /*****************************************************************************
   DBToken.cpp
   
   The token class; a token is stored in a directory containing a single
   database file.
   Each object is stored in multiple tables with every attribute base type
   stored in a different table.
   *****************************************************************************/
  
  #include "config.h"
  #include "log.h"
  #include "OSAttributes.h"
  #include "OSAttribute.h"
  #include "OSPathSep.h"
  
  #include "cryptoki.h"
  #include "DBToken.h"
  #include "DBObject.h"
  #include "DB.h"
  
  #include "Directory.h"
  
  #include <vector>
  #include <string>
  #include <set>
  #include <map>
  #include <list>
  #include <cstdio>
  #include <sys/stat.h>
  #include <errno.h>
  
  const char * const DBTOKEN_FILE = "sqlite3.db";
  const long long DBTOKEN_OBJECT_TOKENINFO = 1;
  
  // Constructor for creating a new token.
  DBToken::DBToken(const std::string &baseDir, const std::string &tokenName, const ByteString &label, const ByteString &serial)
  	: _connection(NULL), _tokenMutex(NULL)
  {
  	std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
  	std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
  
  	// Refuse to open an already existing database.
  	FILE *f = fopen(tokenPath.c_str(),"r");
  	if (f)
  	{
  		fclose(f);
  		ERROR_MSG("Refusing to overwrite and existing database at \"%s\"", tokenPath.c_str());
  		return;
  	}
  
  	// First create the directory for the token, we expect basePath to already exist
  	if (mkdir(tokenDir.c_str(), S_IFDIR | S_IRWXU))
  	{
  		// Allow the directory to exists already.
  		if (errno != EEXIST)
  		{
  			ERROR_MSG("Unable to create directory \"%s\"", tokenDir.c_str());
  			return;
  		}
  	}
  
  	// Create
  	_connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
  	if (_connection == NULL)
  	{
  		ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
  		return;
  	}
  
  	if (!_connection->connect())
  	{
  		delete _connection;
  		_connection = NULL;
  
  		ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
  
  		// Now remove the token directory
  		if (remove(tokenDir.c_str()))
  		{
  			ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
  		}
  
  		return;
  	}
  
  	// Create a DBObject for the established connection to the database.
  	DBObject tokenObject(_connection);
  
  	// First create the tables that support storage of object attributes and then insert the object containing
  	// the token info into the database.
  	if (!tokenObject.createTables() || !tokenObject.insert() || tokenObject.objectId()!=DBTOKEN_OBJECT_TOKENINFO)
  	{
  		tokenObject.dropConnection();
  
  		_connection->close();
  		delete _connection;
  		_connection = NULL;
  
  		ERROR_MSG("Failed to create tables for storing objects in database at \"%s\"", tokenPath.c_str());
  		return;
  	}
  
  	// Set the initial attributes
  	CK_ULONG flags =
  		CKF_RNG |
  		CKF_LOGIN_REQUIRED | // FIXME: check
  		CKF_RESTORE_KEY_NOT_NEEDED |
  		CKF_TOKEN_INITIALIZED |
  		CKF_SO_PIN_LOCKED |
  		CKF_SO_PIN_TO_BE_CHANGED;
  
  	OSAttribute tokenLabel(label);
  	OSAttribute tokenSerial(serial);
  	OSAttribute tokenFlags(flags);
  
  	if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel) ||
  		!tokenObject.setAttribute(CKA_OS_TOKENSERIAL, tokenSerial) ||
  		!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
  	{
  		_connection->close();
  		delete _connection;
  		_connection = NULL;
  
  		// Now remove the token file
  		if (remove(tokenPath.c_str()))
  		{
  			ERROR_MSG("Failed to remove the token file at \"%s\"", tokenPath.c_str());
  		}
  
  		// Now remove the token directory
  		if (remove(tokenDir.c_str()))
  		{
  			ERROR_MSG("Failed to remove the token directory at \"%s\"", tokenDir.c_str());
  		}
  		return;
  	}
  
  	_tokenMutex = MutexFactory::i()->getMutex();
  	// Success!
  }
  
  // Constructor for accessing an existing token.
  DBToken::DBToken(const std::string &baseDir, const std::string &tokenName)
  	: _connection(NULL), _tokenMutex(NULL)
  {
  	std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
  	std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
  
  	// Refuse to open an already existing database.
  	FILE *f = fopen(tokenPath.c_str(),"r");
  	if (f == NULL)
  	{
  		ERROR_MSG("Refusing to open a non-existant database at \"%s\"", tokenPath.c_str());
  		return;
  	}
  	fclose(f);
  
  	// Create a database connection.
  	_connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
  	if (_connection == NULL)
  	{
  		ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
  		return;
  	}
  
  	if (!_connection->connect())
  	{
  		delete _connection;
  		_connection = NULL;
  
  		ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
  
  		return;
  	}
  
  	// Find the DBObject for the established connection to the database.
  	DBObject tokenObject(_connection);
  
  	// First find the token obect that indicates the token is properly initialized.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		tokenObject.dropConnection();
  
  		_connection->close();
  		delete _connection;
  		_connection = NULL;
  
  		ERROR_MSG("Failed to open token object in the token database at \"%s\"", tokenPath.c_str());
  		return;
  	}
  
  	_tokenMutex = MutexFactory::i()->getMutex();
  
  	// Success!
  }
  
  DBToken *DBToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString &label, const ByteString &serial)
  {
  	Directory baseDir(basePath);
  
  	if (!baseDir.isValid())
  	{
  		return NULL;
  	}
  
  	// Create the token directory
  	if (!baseDir.mkdir(tokenDir))
  	{
  		return NULL;
  	}
  
  	DBToken *token = new DBToken(basePath, tokenDir, label, serial);
  	if (!token->isValid())
  	{
  		baseDir.rmdir(tokenDir);
  
  		delete token;
  		return NULL;
  	}
  
  	DEBUG_MSG("Created new token %s", tokenDir.c_str());
  
  	return token;
  }
  
  DBToken *DBToken::accessToken(const std::string &basePath, const std::string &tokenDir)
  {
  	return new DBToken(basePath, tokenDir);
  }
  
  // Destructor
  DBToken::~DBToken()
  {
  	if (_tokenMutex)
  	{
  		MutexFactory::i()->recycleMutex(_tokenMutex);
  		_tokenMutex = NULL;
  	}
  
  	std::map<long long, OSObject*> cleanUp = _allObjects;
  	_allObjects.clear();
  	for (std::map<long long, OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); ++i)
  	{
  		delete i->second;
  	}
  
  	if (_connection)
  	{
  		delete _connection;
  		_connection = NULL;
  	}
  }
  
  // Set the SO PIN
  bool DBToken::setSOPIN(const ByteString& soPINBlob)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadWrite))
  	{
  		ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	OSAttribute soPIN(soPINBlob);
  	if (!tokenObject.setAttribute(CKA_OS_SOPIN, soPIN))
  	{
  		ERROR_MSG("Error while setting SOPIN in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
  	{
  		ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	// Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
  	CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
  					& ~(CKF_SO_PIN_COUNT_LOW | CKF_SO_PIN_FINAL_TRY | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED);
  
  	OSAttribute changedTokenFlags(flags);
  	if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
  	{
  		ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.commitTransaction())
  	{
  		ERROR_MSG("Error while committing SOPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	return true;
  }
  
  // Get the SO PIN
  bool DBToken::getSOPIN(ByteString& soPINBlob)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadOnly))
  	{
  		ERROR_MSG("Unable to start a transaction for getting the SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_SOPIN))
  	{
  		ERROR_MSG("Error while getting SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	tokenObject.commitTransaction();
  	soPINBlob = tokenObject.getAttribute(CKA_OS_SOPIN).getByteStringValue();
  	return true;
  }
  
  // Set the user PIN
  bool DBToken::setUserPIN(ByteString userPINBlob)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadWrite))
  	{
  		ERROR_MSG("Unable to start a transaction for updating the USERPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	OSAttribute userPIN(userPINBlob);
  	if (!tokenObject.setAttribute(CKA_OS_USERPIN, userPIN))
  	{
  		ERROR_MSG("Error while setting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
  	{
  		ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	// Retrieve flags from the database and reset flags related to tries and expiration of the user PIN.
  	CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue();
  	flags |= CKF_USER_PIN_INITIALIZED;
  	flags &= ~(CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED);
  
  	OSAttribute changedTokenFlags(flags);
  	if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
  	{
  		ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.commitTransaction())
  	{
  		ERROR_MSG("Error while committing USERPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	return true;
  }
  
  // Get the user PIN
  bool DBToken::getUserPIN(ByteString& userPINBlob)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadOnly))
  	{
  		ERROR_MSG("Unable to start a transaction for getting the USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_USERPIN))
  	{
  		ERROR_MSG("Error while getting USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	tokenObject.commitTransaction();
  	userPINBlob = tokenObject.getAttribute(CKA_OS_USERPIN).getByteStringValue();
  	return true;
  }
  
  // Retrieve the token label
  bool DBToken::getTokenLabel(ByteString& label)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadOnly))
  	{
  		ERROR_MSG("Unable to start a transaction for getting the TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENLABEL))
  	{
  		ERROR_MSG("Error while getting TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	tokenObject.commitTransaction();
  	label = tokenObject.getAttribute(CKA_OS_TOKENLABEL).getByteStringValue();
  	return true;
  }
  
  // Retrieve the token serial
  bool DBToken::getTokenSerial(ByteString& serial)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadOnly))
  	{
  		ERROR_MSG("Unable to start a transaction for getting the TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENSERIAL))
  	{
  		ERROR_MSG("Error while getting TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	tokenObject.commitTransaction();
  	serial = tokenObject.getAttribute(CKA_OS_TOKENSERIAL).getByteStringValue();
  	return true;
  }
  
  // Get the token flags
  bool DBToken::getTokenFlags(CK_ULONG& flags)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadOnly))
  	{
  		ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
  	{
  		ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	tokenObject.commitTransaction();
  	flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue();
  	return true;
  }
  
  // Set the token flags
  bool DBToken::setTokenFlags(const CK_ULONG flags)
  {
  	if (_connection == NULL) return false;
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadWrite))
  	{
  		ERROR_MSG("Unable to start a transaction for setting the TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	OSAttribute tokenFlags(flags);
  	if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
  	{
  		ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.commitTransaction())
  	{
  		ERROR_MSG("Error while committing TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	return true;
  }
  
  // Retrieve objects
  std::set<OSObject *> DBToken::getObjects()
  {
  	std::set<OSObject*> objects;
  	getObjects(objects);
  	return objects;
  }
  
  void DBToken::getObjects(std::set<OSObject*> &objects)
  {
  	if (_connection == NULL) return;
  
  	if (!_connection->beginTransactionRO()) return;
  
  	DB::Statement statement = _connection->prepare("select id from object limit -1 offset 1");
  
  	DB::Result result = _connection->perform(statement);
  
  	if (result.isValid())
  	{
  		do {
  			long long objectId = result.getLongLong(1);
  			{
  				MutexLocker lock(_tokenMutex);
  				std::map<long long, OSObject*>::iterator it = _allObjects.find(objectId);
  				if (it == _allObjects.end())
  				{
  					DBObject *object = new DBObject(_connection, this, objectId);
  					_allObjects[objectId] = object;
  					objects.insert(object);
  				}
  				else
  				{
  					objects.insert(it->second);
  				}
  			}
  		} while (result.nextRow());
  	}
  
  	_connection->endTransactionRO();
  }
  
  // Create a new object
  OSObject *DBToken::createObject()
  {
  	if (_connection == NULL) return NULL;
  
  	DBObject *newObject = new DBObject(_connection, this);
  	if (newObject == NULL)
  	{
  		ERROR_MSG("Failed to create an object: out of memory");
  		return NULL;
  	}
  
  	if (!newObject->startTransaction(DBObject::ReadWrite))
  	{
  		delete newObject;
  		ERROR_MSG("Unable to start a transaction in token database at \"%s\"", _connection->dbpath().c_str());
  		return NULL;
  	}
  
  	if (!newObject->insert())
  	{
  		newObject->abortTransaction();
  		delete newObject;
  		ERROR_MSG("Unable to insert an object into token database at \"%s\"", _connection->dbpath().c_str());
  		return NULL;
  	}
  
  	if (!newObject->isValid())
  	{
  		newObject->abortTransaction();
  		delete newObject;
  		ERROR_MSG("Object that was inserted in not valid");
  		return NULL;
  	}
  
  	if (!newObject->commitTransaction())
  	{
  		newObject->abortTransaction();
  		delete newObject;
  		ERROR_MSG("Unable to commit a created object to token database at \"%s\"", _connection->dbpath().c_str());
  		return NULL;
  	}
  
  	// Now add the new object to the list of existing objects.
  	{
  		MutexLocker lock(_tokenMutex);
  		_allObjects[newObject->objectId()] = newObject;
  	}
  
  	return newObject;
  }
  
  bool DBToken::deleteObject(OSObject *object)
  {
  	if (_connection == NULL) return false;
  
  	if (object == NULL)
  	{
  		ERROR_MSG("Object passed in as a parameter is NULL");
  		return false;
  	}
  
  	if (!object->startTransaction(DBObject::ReadWrite))
  	{
  		ERROR_MSG("Unable to start a transaction for deleting an object in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	if (!static_cast<DBObject *>(object)->remove())
  	{
  		ERROR_MSG("Error while deleting an existing object from the token database at \"%s\"", _connection->dbpath().c_str());
  		object->abortTransaction();
  		return false;
  	}
  
  	if (!object->commitTransaction())
  	{
  		ERROR_MSG("Error while committing the deletion of an existing object in token database at \"%s\"", _connection->dbpath().c_str());
  		object->abortTransaction();
  		return false;
  	}
  
  	return true;
  }
  
  // Checks if the token is consistent
  bool DBToken::isValid()
  {
  	return _connection != NULL && _connection->tableExists("object");
  }
  
  // Invalidate the token (for instance if it is deleted)
  void DBToken::invalidate()
  {
  }
  
  // Delete the token.
  bool DBToken::clearToken()
  {
  	if (_connection == NULL) return false;
  
  	std::string tokenDir = _connection->dbdir();
  	std::string tokenPath = _connection->dbpath();
  
  	if (!DBObject(_connection).dropTables())
  	{
  		ERROR_MSG("Failed to drop all tables in the token database at \"%s\"", tokenPath.c_str());
  		return false;
  	}
  
  	_connection->close();
  	delete _connection;
  	_connection = NULL;
  
  	// Remove all files from the token directory, even ones not placed there by us.
  	Directory dir(tokenDir);
  	std::vector<std::string> tokenFiles = dir.getFiles();
  
  	for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++)
  	{
  		if (!dir.remove(*i))
  		{
  			ERROR_MSG("Failed to remove \"%s\" from token directory \"%s\"", i->c_str(), tokenDir.c_str());
  
  			return false;
  		}
  	}
  
  	// Now remove the token directory
  	if (!dir.rmdir(""))
  	{
  		ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
  
  		return false;
  	}
  
  	DEBUG_MSG("Token instance %s was succesfully cleared", tokenDir.c_str());
  
  	return true;
  }
  
  // Reset the token
  bool DBToken::resetToken(const ByteString& label)
  {
  	if (_connection == NULL) return false;
  
  	std::string tokenDir = _connection->dbdir();
  
  	// Clean up
  	std::set<OSObject*> cleanUp = getObjects();
  
  	for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
  	{
  		if (!deleteObject(*i))
  		{
  			ERROR_MSG("Unable to delete all objects in token database at \"%s\"", _connection->dbpath().c_str());
  			return false;
  		}
  	}
  
  	// Create a DBObject for the established connection to the token object in the database
  	DBObject tokenObject(_connection);
  
  	if (!tokenObject.startTransaction(DBObject::ReadWrite))
  	{
  		ERROR_MSG("Unable to start a transaction for setting the TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
  		return false;
  	}
  
  	// First find the token object in the database.
  	if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
  	{
  		ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (tokenObject.attributeExists(CKA_OS_USERPIN))
  	{
  		if (!tokenObject.deleteAttribute(CKA_OS_USERPIN))
  		{
  			ERROR_MSG("Error while deleting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
  			tokenObject.abortTransaction();
  			return false;
  		}
  	}
  
  	if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
  	{
  		ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	// Retrieve flags from the database and reset flags related to tries and expiration of the user PIN.
  	CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
  					& ~(CKF_USER_PIN_INITIALIZED | CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED);
  
  	OSAttribute changedTokenFlags(flags);
  	if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
  	{
  		ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	OSAttribute tokenLabel(label);
  	if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel))
  	{
  		ERROR_MSG("Error while setting TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	if (!tokenObject.commitTransaction())
  	{
  		ERROR_MSG("Error while committing TOKENLABEL changes to token database at \"%s\"", _connection->dbpath().c_str());
  		tokenObject.abortTransaction();
  		return false;
  	}
  
  	DEBUG_MSG("Token instance %s was succesfully reset", tokenDir.c_str());
  
  	return true;
  }
  