// // // 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 "utl/UtlTokenizer.h" #include "fastdb/fastdb.h" #include "sipdb/ResultSet.h" #include "sipdb/SIPDBManager.h" #include "sipdb/DialByNameRow.h" #include "sipdb/DialByNameDB.h" #include "sipdb/PermissionDB.h" #include "sipdb/CredentialDB.h" REGISTER( DialByNameRow ); // Static Initializers DialByNameDB* DialByNameDB::spInstance = NULL; OsMutex DialByNameDB::sLockMutex (OsMutex::Q_FIFO); UtlString DialByNameDB::gNp_identityKey("np_identity"); UtlString DialByNameDB::gNp_contactKey("np_contact"); UtlString DialByNameDB::gNp_digitsKey("np_digits"); // GLOBAL VARS const char digitmap [] = { '2','2','2', // a,b,c '3','3','3', // d,e,f '4','4','4', // g,h,i '5','5','5', // j,k,l '6','6','6', // m,n,o '7','7','7','7', // p,q,r,s '8','8','8', // t,u,v '9','9','9','9' // w,x,y,z }; /* ============================ CREATORS ================================== */ DialByNameDB::DialByNameDB( const UtlString& name ) : mDatabaseName( name ) { // Access the shared table databse 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 users = pSIPDBManager->getNumDatabaseProcesses(name); if ( users == 1 ) { // Load the file implicitly this->load(); } } DialByNameDB::~DialByNameDB() { OsSysLog::add(FAC_DB, PRI_DEBUG, "<><>## DialByNameDB:: DESTRUCTOR"); } /* ============================ MANIPULATORS ============================== */ void DialByNameDB::releaseInstance() { OsSysLog::add(FAC_DB, PRI_DEBUG, "<><>## DialByNameDB:: 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 DialByNameDB::load() const { // 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 (); // Query all Identities with 'AutoAttendant' permission set PermissionDB * pPermissionDB = PermissionDB::getInstance(); ResultSet permissionsResultSet; pPermissionDB->getIdentities ( "AutoAttendant", permissionsResultSet ); CredentialDB * pCredentialDB = CredentialDB::getInstance(); ResultSet credentialsResultSet; UtlString identity, permission; int numAutoAttendees = permissionsResultSet.getSize(); for (int index = 0; index < numAutoAttendees; index++) { // get the next identity UtlString identityKey("identity"); UtlHashMap record; permissionsResultSet.getIndex( index, record ); UtlString identity = *((UtlString*)record.findValue(&identityKey)); Url identityUrl (identity); pCredentialDB-> getAllCredentials ( identityUrl, credentialsResultSet ); // we should only have one credential! we're // only interested in the uri column's display name if ( credentialsResultSet.getSize() == 1) { UtlString uriKey("uri"); UtlHashMap record; credentialsResultSet.getIndex( 0, record ); UtlString uri = *((UtlString*)record.findValue(&uriKey)); // must have a display name present before inserting a row // @TODO convert to url and get display name UtlHashMap nvPairs; if (!uri.isNull()) { // Null Element value create a special // char string we have key and value so insert UtlString* contactValue = new UtlString( uri ); // Memory Leak fixes, make shallow copies of static keys UtlString* contactKey = new UtlString( gNp_contactKey ); nvPairs.insertKeyAndValue ( contactKey, contactValue ); } // Insert the item row into the IMDB insertRow ( nvPairs ); } } // Reset the changed flags after a successful load SIPDBManager::getInstance()-> setDatabaseChangedFlag("credential", FALSE); SIPDBManager::getInstance()-> setDatabaseChangedFlag("permission", FALSE); } else { result = OS_FAILED; } return result; } OsStatus DialByNameDB::store() { // this is a no-op for DialByNameDB as its // backing store is actually the join of the credentials // and permission databases if ( m_pFastDB != NULL ) { return OS_SUCCESS; } else { return OS_FAILED; } } UtlBoolean DialByNameDB::insertRow ( const UtlHashMap& nvPairs ) const { // Note we do not need the identity object here // as it is inferred from the uri return insertRow ( Url( *((UtlString*)nvPairs.findValue(&gNp_contactKey)) ) ); } UtlBoolean DialByNameDB::insertRow ( const Url& contact ) const { UtlBoolean result = FALSE; if ( m_pFastDB != NULL ) { // Fetch the display name UtlString identity, displayName, contactString; contact.getIdentity( identity ); contact.getDisplayName( displayName ); contact.toString( contactString ); // Make sure that the contact URL is valid and contains // a contactIdentity and a contactDisplayName if ( !identity.isNull() && !displayName.isNull() ) { UtlSList dtmfStrings; getDigitStrings ( displayName, dtmfStrings ); if ( !dtmfStrings.isEmpty() ) { // Thread Local Storage m_pFastDB->attach(); // Search for a matching row before deciding to update or insert dbCursor< DialByNameRow > cursor(dbCursorForUpdate); DialByNameRow row; dbQuery query; // Primary Key is identity query="np_identity=",identity; // Purge all existing entries associated with this identity if ( cursor.select( query ) > 0 ) { cursor.removeAllSelected(); } // insert all dtmf combinations for this user unsigned int i; for (i=0; idata(); insert (row); } // Commit rows to memory - multiprocess workaround m_pFastDB->detach(0); } } } return result; } UtlBoolean DialByNameDB::getDigitStrings ( const UtlString& displayName, UtlSList& rDTMFStrings ) const { UtlString lowerString = displayName; lowerString.toLower(); lowerString = lowerString.strip(UtlString::both, '"'); UtlTokenizer next( lowerString ); UtlString token; UtlSList names; // Parse the Display name into a list of names // The algorithm for breaking up the string is as follows // if the string has > 2 tokens (forget about , separators) // create multiple entries in the IMDB for the all combinations // so for example // John Peter Smith Jr would result in DTMF entries for // PeterSmithJrJohn, SmithJrJohnPeter and JrJohnPeterSmith // @JC Added - separator for MIT's Avery-Smith example while (next.next(token, "\t\n,- ")) { names.insert ( new UtlString ( token ) ); } size_t numNames = names.entries(); if ( numNames > 0 ) { UtlString reorderedString; unsigned int splitPosition = 1; do { unsigned int i; UtlString firstNames; for ( i=0; i= 0) && (offset < 26) ) digitString += digitmap[ offset ]; } rDTMFStrings.insert ( new UtlString (digitString) ); splitPosition++; } while ( splitPositionattach(); dbCursor< DialByNameRow > cursor(dbCursorForUpdate); dbQuery query; query="np_identity=",identity; if ( cursor.select( query ) > 0 ) { cursor.removeAllSelected(); removed = TRUE; } // Commit rows to memory - multiprocess workaround m_pFastDB->detach(0); } return removed; } void DialByNameDB::removeAllRows () const { if (m_pFastDB != NULL ) { // Thread Local Storage m_pFastDB->attach(); dbCursor< DialByNameRow > cursor(dbCursorForUpdate); if (cursor.select() > 0) { cursor.removeAllSelected(); } // Commit rows to memory - multiprocess workaround m_pFastDB->detach(0); } } void DialByNameDB::getAllRows(ResultSet& rResultSet) const { // Clear the results rResultSet.destroyAll(); // Check the TableInfo table to see whether we need to reload // the Tables from the Credential/Permission tables if ( m_pFastDB != NULL ) { SIPDBManager* pSIPDBManager = SIPDBManager::getInstance(); if ( pSIPDBManager->getDatabaseChangedFlag( "credential" ) || pSIPDBManager->getDatabaseChangedFlag( "permission" ) ) { // Reload this IMDB and reset the changed flags // in both the credential and permission tables this->load(); } // Thread Local Storage m_pFastDB->attach(); dbCursor< DialByNameRow > cursor; if ( cursor.select() > 0 ) { do { UtlHashMap record; UtlString* np_identityValue = new UtlString ( cursor->np_identity ); UtlString* np_contactValue = new UtlString ( cursor->np_contact ); UtlString* np_digitsValue = new UtlString ( cursor->np_digits ); // Memory Leak fixes, make shallow copies of static keys UtlString* np_identityKey = new UtlString( gNp_identityKey ); UtlString* np_contactKey = new UtlString( gNp_contactKey ); UtlString* np_digitsKey = new UtlString( gNp_digitsKey ); record.insertKeyAndValue ( np_identityKey, np_identityValue ); record.insertKeyAndValue ( np_contactKey, np_contactValue ); record.insertKeyAndValue ( np_digitsKey, np_digitsValue ); rResultSet.addValue(record); } while (cursor.next()); } // Commit rows to memory - multiprocess workaround m_pFastDB->detach(0); } } void DialByNameDB::getContacts ( const UtlString& digitString, ResultSet& rResultSet ) const { // This should erase the contents of the existing resultset rResultSet.destroyAll(); if ( !digitString.isNull() && (m_pFastDB != NULL) ) { // Check the TableInfo table to see whether we need to reload // the Tables from the Credential/Permission tables SIPDBManager* pSIPDBManager = SIPDBManager::getInstance(); if ( pSIPDBManager->getDatabaseChangedFlag( "credential" ) || pSIPDBManager->getDatabaseChangedFlag( "permission" ) ) { // Reload this IMDB and rese the changed flags // in the credential and permission tables this->load(); } // Thread Local Storage m_pFastDB->attach(); // Search to see if we have a Credential Row dbCursor< DialByNameRow > cursor; dbQuery query; UtlString queryString = "np_digits like '" + digitString + "%'"; query = queryString; if ( cursor.select(query) > 0 ) { do { UtlHashMap record; UtlString* np_identityValue = new UtlString ( cursor->np_identity ); UtlString* np_contactValue = new UtlString ( cursor->np_contact ); UtlString* np_digitsValue = new UtlString ( cursor->np_digits ); // Memory Leak fixes, make shallow copies of static keys UtlString* np_identityKey = new UtlString( gNp_identityKey ); UtlString* np_contactKey = new UtlString( gNp_contactKey ); UtlString* np_digitsKey = new UtlString( gNp_digitsKey ); record.insertKeyAndValue ( np_identityKey, np_identityValue ); record.insertKeyAndValue ( np_contactKey, np_contactValue ); record.insertKeyAndValue ( np_digitsKey, np_digitsValue ); rResultSet.addValue(record); } while ( cursor.next() ); } // Commit the rows to memory - multiprocess workaround m_pFastDB->detach(0); } } DialByNameDB* DialByNameDB::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 DialByNameDB( name ); } return spInstance; }