//
//
// Copyright (C) 2004 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2004 Pingtel Corp.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES
// APPLICATION INCLUDES
#include "os/OsLock.h"
#include "os/OsDateTime.h"
#include "os/OsFS.h"
#include "os/OsSysLog.h"
#include "net/Url.h"
#include "xmlparser/tinyxml.h"
#include "fastdb/fastdb.h"
#include "sipdb/ResultSet.h"
#include "sipdb/SIPDBManager.h"
#include "sipdb/CredentialRow.h"
#include "sipdb/CredentialDB.h"
// DEFINES
REGISTER( CredentialRow );
// MACROS
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
// STATIC INITIALIZERS
CredentialDB* CredentialDB::spInstance = NULL;
OsMutex CredentialDB::sLockMutex (OsMutex::Q_FIFO);
// GLOBAL VARIABLES
UtlString CredentialDB::gUriKey("uri");
UtlString CredentialDB::gRealmKey("realm");
UtlString CredentialDB::gUseridKey("userid");
UtlString CredentialDB::gPasstokenKey("passtoken");
UtlString CredentialDB::gPintokenKey("pintoken");
UtlString CredentialDB::gAuthtypeKey("authtype");
/* ============================ CREATORS ================================== */
CredentialDB::CredentialDB ( const UtlString& name )
: mDatabaseName( name )
{
// Access the shared table database
SIPDBManager* pSIPDBManager = SIPDBManager::getInstance();
m_pFastDB = pSIPDBManager->getDatabase( name );
// If we are the first process to attach
// then we need to load the DB
int numusers = pSIPDBManager->getNumDatabaseProcesses (name);
if ( numusers == 1 )
{
// Load the file implicitly
this->load();
}
}
CredentialDB::~CredentialDB()
{
OsSysLog::add(FAC_DB, PRI_DEBUG, "<><>## CredentialDB:: DESTRUCTOR");
}
/* ============================ MANIPULATORS ============================== */
void
CredentialDB::releaseInstance()
{
OsSysLog::add(FAC_DB, PRI_DEBUG, "<><>## CredentialDB:: releaseInstance() spInstance=%p", spInstance);
// Critical Section here
OsLock lock( sLockMutex );
// if it exists, delete the object and NULL out the pointer
if (spInstance != NULL) {
// unregister this table/process from the IMDB
SIPDBManager::getInstance()->removeDatabase ( spInstance->mDatabaseName );
// NULL out the fastDB pointer also
spInstance->m_pFastDB = NULL;
delete spInstance;
spInstance = NULL;
}
}
OsStatus
CredentialDB::load()
{
// Critical Section here
OsLock lock( sLockMutex );
OsStatus result = OS_SUCCESS;
if ( m_pFastDB != NULL )
{
// Clean out the existing DB rows before loading
// a new set from persistent storage
removeAllRows ();
UtlString fileName =
SIPDBManager::getInstance()->
getConfigDirectory() +
OsPath::separator + mDatabaseName + ".xml";
OsSysLog::add(FAC_DB, PRI_DEBUG, "CredentialDB::load loading \"%s\"",
fileName.data());
TiXmlDocument doc ( fileName );
// Verify that we can load the file (i.e it must exist)
if( doc.LoadFile() )
{
// the checksum is used to determine if the db changed between reloads
int loadChecksum = 0;
TiXmlNode * rootNode = doc.FirstChild ("items");
if (rootNode != NULL)
{
// the folder node contains at least the name/displayname/
// and autodelete elements, it may contain others
for( TiXmlNode *itemNode = rootNode->FirstChild( "item" );
itemNode;
itemNode = itemNode->NextSibling( "item" ) )
{
// Create a hash dictionary for element attributes
UtlHashMap nvPairs;
for( TiXmlNode *elementNode = itemNode->FirstChild();
elementNode;
elementNode = elementNode->NextSibling() )
{
// Bypass comments and other element types only interested
// in parsing element attributes
if ( elementNode->Type() == TiXmlNode::ELEMENT )
{
UtlString elementName = elementNode->Value();
UtlString elementValue;
result = SIPDBManager::getAttributeValue (
*itemNode, elementName, elementValue);
// update the load checksum
loadChecksum += ( elementName.hash() + elementValue.hash() );
if (result == OS_SUCCESS)
{
UtlString* collectableKey =
new UtlString( elementName );
UtlString* collectableValue =
new UtlString( elementValue );
nvPairs.insertKeyAndValue (
collectableKey, collectableValue );
} else if ( elementNode->FirstChild() == NULL )
{
// Null Element value creaete a special
// char string we have key and value so insert
UtlString* collectableKey =
new UtlString( elementName );
UtlString* collectableValue =
new UtlString( SPECIAL_IMDB_NULL_VALUE );
nvPairs.insertKeyAndValue (
collectableKey, collectableValue );
}
}
}
// If this is an old credentials file, it may not contain
// the pintoken element - if not, duplicate the passtoken
// to create it.
if (!nvPairs.contains(&gPintokenKey))
{
UtlString* pintokenKey = new UtlString(gPintokenKey);
UtlString* pintokenValue
= new UtlString(*((UtlString*)nvPairs.findValue(&gPasstokenKey)));
nvPairs.insertKeyAndValue(pintokenKey, pintokenValue);
}
// Insert the item row into the IMDB
insertRow ( nvPairs );
}
}
// Update the tableInfo table and determine if the db has
// changed as a result of the reload (setting the
// changed tableInfo field
SIPDBManager::getInstance()->
updateDatabaseInfo(
mDatabaseName, loadChecksum);
} else
{
OsSysLog::add(FAC_DB, PRI_WARNING, "CredentialDB::load failed to load \"%s\"",
fileName.data());
}
} else
{
OsSysLog::add(FAC_DB, PRI_ERR, "CredentialDB::load failed - no DB");
result = OS_FAILED;
}
return result;
}
OsStatus
CredentialDB::store()
{
// Critical Section here
OsLock lock( sLockMutex );
OsStatus result = OS_SUCCESS;
if ( m_pFastDB != NULL )
{
UtlString fileName =
SIPDBManager::getInstance()->
getConfigDirectory() +
OsPath::separator + mDatabaseName + ".xml";
// Thread Local Storage
m_pFastDB->attach();
// Search our memory for rows
dbCursor< CredentialRow > cursor;
// Select everything in the IMDB and add as item elements if present
if ( cursor.select() > 0 )
{
// Create an empty document
TiXmlDocument document;
// Create a hard coded standalone declaration section
document.Parse ("<?xml version=\"1.0\" standalone=\"yes\"?>");
// Create the root node container
TiXmlElement itemsElement ( "items" );
itemsElement.SetAttribute( "type", mDatabaseName.data() );
// metadata contains column names
dbTableDescriptor* pTableMetaData = &CredentialRow::dbDescriptor;
do {
// Create an item container
TiXmlElement itemElement ("item");
byte* base = (byte*)cursor.get();
// Add the column name value pairs
for ( dbFieldDescriptor* fd = pTableMetaData->getFirstField();
fd != NULL; fd = fd->nextField )
{
// if the column name does not contain the
// np_prefix we must_presist it
if ( strstr( fd->name, "np_" ) == NULL )
{
// Create the a column element named after the IMDB column name
TiXmlElement element (fd->name );
// See if the IMDB has the predefined SPECIAL_NULL_VALUE
UtlString textValue;
SIPDBManager::getFieldValue(base, fd, textValue);
// If the value is not null append a text child element
if ( textValue != SPECIAL_IMDB_NULL_VALUE )
{
// Text type assumed here... @todo change this
TiXmlText value ( textValue.data() );
// Store the column value in the element making this
// <colname>coltextvalue</colname>
element.InsertEndChild ( value );
}
// Store this in the item tag as follows
// <item>
// .. <col1name>col1textvalue</col1name>
// .. <col2name>col2textvalue</col2name>
// .... etc
itemElement.InsertEndChild ( element );
}
}
// add the line to the element
itemsElement.InsertEndChild ( itemElement );
} while ( cursor.next() );
// Attach the root node to the document
document.InsertEndChild ( itemsElement );
document.SaveFile ( fileName );
} else
{
// database contains no rows so delete the file
UtlString fileName =
SIPDBManager::getInstance()->
getConfigDirectory() +
OsPath::separator + mDatabaseName + ".xml";
if ( OsFileSystem::exists ( fileName ) ) {
OsFileSystem::remove( fileName );
}
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
} else
{
result = OS_FAILED;
}
return result;
}
UtlBoolean
CredentialDB::insertRow (const UtlHashMap& nvPairs)
{
// Note: identity inferred from the uri
return insertRow (
Url (*((UtlString*)nvPairs.findValue(&gUriKey))),
*((UtlString*)nvPairs.findValue(&gRealmKey)),
*((UtlString*)nvPairs.findValue(&gUseridKey)),
*((UtlString*)nvPairs.findValue(&gPasstokenKey)),
*((UtlString*)nvPairs.findValue(&gPintokenKey)),
*((UtlString*)nvPairs.findValue(&gAuthtypeKey)));
}
UtlBoolean
CredentialDB::insertRow (
const Url& uri,
const UtlString& realm,
const UtlString& userid,
const UtlString& passToken,
const UtlString& pinToken,
const UtlString& authType )
{
UtlBoolean result = FALSE;
UtlString identity;
uri.getIdentity(identity);
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
// Search for a matching row before
// deciding to update or insert
dbCursor< CredentialRow > cursor(dbCursorForUpdate);
// The URI field should be unique.
dbQuery query;
query="np_identity=",identity,"and realm=",realm;
UtlString fullUri = uri.toString();
if ( cursor.select( query ) > 0 )
{
do {
// Update row (Identiy and realm already match so ignore
cursor->uri = fullUri;
cursor->userid = userid;
cursor->passtoken = passToken;
cursor->pintoken = pinToken;
cursor->authtype = authType;
cursor.update();
} while (cursor.next());
} else
{
// Insert new row
CredentialRow row;
row.np_identity = identity;
row.realm = realm;
row.uri = fullUri;
row.userid = userid;
row.passtoken = passToken;
row.pintoken = pinToken;
row.authtype = authType;
insert (row);
}
// Either did an insert or an update
result = TRUE;
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
// Table Data changed
SIPDBManager::getInstance()->
setDatabaseChangedFlag(mDatabaseName, TRUE);
}
// Most likely an arg problem
return result;
}
void CredentialDB::removeRows (
const Url& uri,
const UtlString& realm )
{
UtlString identity;
uri.getIdentity(identity);
if ( !identity.isNull() && (m_pFastDB != NULL))
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor(dbCursorForUpdate);
dbQuery query;
query="np_identity=",identity,"and realm=",realm;
if (cursor.select(query) > 0)
{
cursor.removeAllSelected();
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
// Table Data changed
SIPDBManager::getInstance()->
setDatabaseChangedFlag(mDatabaseName, TRUE);
}
}
void
CredentialDB::removeRows ( const Url& uri )
{
UtlString identity;
uri.getIdentity(identity);
if ( !identity.isNull() && (m_pFastDB != NULL))
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor(dbCursorForUpdate);
dbQuery query;
query="np_identity=",identity;
if (cursor.select(query) > 0)
{
cursor.removeAllSelected();
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
// Table Data changed
SIPDBManager::getInstance()->
setDatabaseChangedFlag(mDatabaseName, TRUE);
}
}
void
CredentialDB::removeAllRows ()
{
if ( m_pFastDB != NULL )
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor(dbCursorForUpdate);
if (cursor.select() > 0)
{
cursor.removeAllSelected();
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
// Table Data changed
SIPDBManager::getInstance()->
setDatabaseChangedFlag(mDatabaseName, TRUE);
}
}
void
CredentialDB::getAllRows( ResultSet& rResultSet ) const
{
// Clear the out any previous records
rResultSet.destroyAll();
if (m_pFastDB != NULL)
{
// must do this first to ensure process/tls integrity
m_pFastDB->attach();
dbCursor< CredentialRow > cursor;
if ( cursor.select() > 0 )
{
do {
UtlHashMap record;
UtlString* uriValue =
new UtlString ( cursor->uri );
UtlString* realmValue =
new UtlString ( cursor->realm );
UtlString* useridValue =
new UtlString ( cursor->userid );
UtlString* passtokenValue =
new UtlString ( cursor->passtoken );
UtlString* pintokenValue =
new UtlString ( cursor->pintoken );
UtlString* authtypeValue =
new UtlString ( cursor->authtype );
// Memory Leak fixes, make shallow copies of static keys
UtlString* uriKey = new UtlString( gUriKey );
UtlString* realmKey = new UtlString( gRealmKey );
UtlString* useridKey = new UtlString( gUseridKey );
UtlString* passtokenKey = new UtlString( gPasstokenKey );
UtlString* pintokenKey = new UtlString( gPintokenKey );
UtlString* authtypeKey = new UtlString( gAuthtypeKey );
record.insertKeyAndValue ( uriKey, uriValue );
record.insertKeyAndValue ( realmKey, realmValue );
record.insertKeyAndValue ( useridKey, useridValue );
record.insertKeyAndValue ( passtokenKey, passtokenValue );
record.insertKeyAndValue ( pintokenKey, pintokenValue );
record.insertKeyAndValue ( authtypeKey, authtypeValue );
rResultSet.addValue(record);
} while (cursor.next());
}
// commit rows and also ensure process/tls integrity
m_pFastDB->detach(0);
}
}
UtlBoolean
CredentialDB::getCredentialByUserid (
const Url& uri,
const UtlString& realm,
const UtlString& userid,
UtlString& passtoken,
UtlString& authType ) const
{
UtlBoolean found = FALSE;
UtlString identity;
uri.getIdentity(identity);
// Match the row and return the passtoken and authtype
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor;
dbQuery query;
query="np_identity=",identity, \
"and realm=",realm, \
"and userid=",userid, \
"order by np_identity asc, realm asc";
if ( cursor.select( query ) > 0 )
{
do {
passtoken = cursor->passtoken;
authType = cursor->authtype;
found = TRUE;
} while ( cursor.next() );
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
return found;
}
UtlBoolean
CredentialDB::getCredential (
const UtlString& userid,
const UtlString& realm,
Url& uri,
UtlString& passtoken,
UtlString& authType ) const
{
UtlBoolean found = FALSE;
if ( !userid.isNull() && !realm.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
// Search to see if we have a Credential Row
dbCursor< CredentialRow > cursor;
dbQuery query;
query="userid=",userid,"and realm=",realm;
if ( cursor.select(query) > 0 ) {
do {
uri = cursor->uri;
passtoken = cursor->passtoken;
authType = cursor->authtype;
} while ( cursor.next() );
found = TRUE;
}
// Commit the rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
return found;
}
UtlBoolean
CredentialDB::getCredential (
const Url& uri,
const UtlString& realm,
UtlString& userid,
UtlString& passtoken,
UtlString& authType ) const
{
UtlBoolean found = FALSE;
UtlString identity;
uri.getIdentity(identity);
// Match the row and return the passtoken and authtype
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor;
dbQuery query;
query="np_identity=",identity, \
"and realm=",realm, \
"order by np_identity asc, realm asc";
if ( cursor.select( query ) > 0 )
{
do {
userid = cursor->userid;
passtoken = cursor->passtoken;
authType = cursor->authtype;
found = TRUE;
} while ( cursor.next() );
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
return found;
}
/// Retrieve the User PIN check values for a given identity and realm
UtlBoolean CredentialDB::getUserPin (
const Url& uri,
const UtlString& realm,
UtlString& userid,
UtlString& pintoken,
UtlString& authType
) const
{
UtlBoolean found = FALSE;
UtlString identity;
uri.getIdentity(identity);
// Match the row and return the passtoken and authtype
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
dbCursor< CredentialRow > cursor;
dbQuery query;
query="np_identity=",identity, \
"and realm=",realm, \
"order by np_identity asc, realm asc";
if ( cursor.select( query ) > 0 )
{
do {
userid = cursor->userid;
pintoken = cursor->pintoken;
authType = cursor->authtype;
found = TRUE;
} while ( cursor.next() );
}
// Commit rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
return found;
}
/// Retrieve the User PIN check values for a given userid and realm
UtlBoolean CredentialDB::getUserPin (
const UtlString& userid,
const UtlString& realm,
Url& uri,
UtlString& pintoken,
UtlString& authType
) const
{
UtlBoolean found = FALSE;
if ( !userid.isNull() && !realm.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
// Search to see if we have a Credential Row
dbCursor< CredentialRow > cursor;
dbQuery query;
query="userid=",userid,"and realm=",realm;
if ( cursor.select(query) > 0 ) {
do {
uri = cursor->uri;
pintoken = cursor->pintoken;
authType = cursor->authtype;
} while ( cursor.next() );
found = TRUE;
}
// Commit the rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
return found;
}
void
CredentialDB::getAllCredentials (
const Url& uri,
ResultSet& rResultSet ) const
{
UtlString identity;
uri.getIdentity(identity);
// This should erase the contents of the existing resultset
rResultSet.clear();
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
// Search to see if we have a Credential Row
dbCursor< CredentialRow > cursor;
dbQuery query;
query="np_identity=",identity;
if ( cursor.select( query ) > 0 )
{
do {
UtlHashMap record;
UtlString* uriValue =
new UtlString ( cursor->uri );
UtlString* realmValue =
new UtlString ( cursor->realm );
UtlString* useridValue =
new UtlString ( cursor->userid );
UtlString* passtokenValue =
new UtlString ( cursor->passtoken );
UtlString* pintokenValue =
new UtlString ( cursor->pintoken );
UtlString* authtypeValue =
new UtlString ( cursor->authtype );
// Memory Leak fixes, make shallow copies of static keys
UtlString* uriKey = new UtlString( gUriKey );
UtlString* realmKey = new UtlString( gRealmKey );
UtlString* useridKey = new UtlString( gUseridKey );
UtlString* passtokenKey = new UtlString( gPasstokenKey );
UtlString* pintokenKey = new UtlString( gPintokenKey );
UtlString* authtypeKey = new UtlString( gAuthtypeKey );
record.insertKeyAndValue ( uriKey, uriValue );
record.insertKeyAndValue ( realmKey, realmValue );
record.insertKeyAndValue ( useridKey, useridValue );
record.insertKeyAndValue ( passtokenKey, passtokenValue );
record.insertKeyAndValue ( pintokenKey, pintokenValue );
record.insertKeyAndValue ( authtypeKey, authtypeValue );
rResultSet.addValue(record);
} while ( cursor.next() );
}
// Commit the rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
}
UtlBoolean
CredentialDB::isUriDefined (
const Url& uri,
UtlString& realm,
UtlString& authType ) const
{
UtlBoolean found = FALSE;
UtlString identity;
uri.getIdentity(identity);
OsSysLog::add(FAC_DB, PRI_DEBUG, "CredentialDB::isUriDefined identity %s, m_pFastDB=0x%08x ",
identity.data(), (int)m_pFastDB);
if ( !identity.isNull() && (m_pFastDB != NULL) )
{
// Thread Local Storage
m_pFastDB->attach();
// Search to see if we have a Credential Row
dbCursor< CredentialRow > cursor;
dbQuery query;
// This is equivalent to select * from credentials
// where identity=uri.getIdentity() and realm=realm
// and authType = authType
query="np_identity=",identity;
if ( cursor.select(query) > 0 ) {
OsSysLog::add(FAC_DB, PRI_DEBUG, "CredentialDB::isUriDefined cursor selected ");
do {
realm = cursor->realm;
authType = cursor->authtype;
} while ( cursor.next() );
found = TRUE;
}
// Commit the rows to memory - multiprocess workaround
m_pFastDB->detach(0);
}
OsSysLog::add(FAC_DB, PRI_DEBUG, "CredentialDB::isUriDefined found=%d ", (int)found);
return found;
}
CredentialDB*
CredentialDB::getInstance( const UtlString& name )
{
// Critical Section here
OsLock lock( sLockMutex );
// See if this is the first time through for this process
// Note that this being null => pgDatabase is also null
if ( spInstance == NULL )
{
// Create the singleton class for clients to use
spInstance = new CredentialDB( name );
}
return spInstance;
}
syntax highlighted by Code2HTML, v. 0.9.1