// // // 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 //#include <...> // APPLICATION INCLUDES #include "os/OsLock.h" #include "os/OsDateTime.h" #include "os/OsFS.h" #include "os/OsSysLog.h" #include "net/Url.h" #include "fastdb/fastdb.h" #include "xmlparser/tinyxml.h" #include "sipdb/SIPDBManager.h" #include "sipdb/ResultSet.h" #include "sipdb/CallerAliasRow.h" #include "sipdb/CallerAliasDB.h" #define CALLERALIAS_XML_NAMESPACE_URL "http://www.sipfoundry.org/sipX/schema/xml/caller-alias-00-00" REGISTER( CallerAliasRow ); // STATIC INITIALIZERS CallerAliasDB* CallerAliasDB::spInstance = NULL; OsMutex CallerAliasDB::sLockMutex (OsMutex::Q_FIFO); const UtlString CallerAliasDB::IdentityKey("identity"); const UtlString CallerAliasDB::DomainKey("domain"); const UtlString CallerAliasDB::AliasKey("alias"); const UtlString CallerAliasDB::DbName("caller-alias"); /* ============================ CREATORS ================================== */ CallerAliasDB::CallerAliasDB( const UtlString& name ) { // Access the shared table databse SIPDBManager* pSIPDBManager = SIPDBManager::getInstance(); mpFastDB = 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(); } } CallerAliasDB::~CallerAliasDB() { } CallerAliasDB* CallerAliasDB::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 CallerAliasDB( name ); } return spInstance; } void CallerAliasDB::releaseInstance() { // 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 ( DbName ); // NULL out the fastDB pointer also spInstance->mpFastDB = NULL; delete spInstance; spInstance = NULL; } } // Add a single mapping to the database. void CallerAliasDB::insertRow( const UtlString identity, ///< identity of caller in 'user@domain' form (no scheme) const UtlString domain, /**< domain and optional port for target * ( 'example.com' or 'example.com:5099' ) */ const UtlString alias /// returned alias ) { /* * The identity value may be the null string; this is a wildcard entry that matches * any caller to the given domain. */ if ( !domain.isNull() && !alias.isNull() && (mpFastDB != NULL) ) { // Thread Local Storage mpFastDB->attach(); // Search for a matching row before deciding to update or insert dbCursor< CallerAliasRow > cursor(dbCursorForUpdate); CallerAliasRow row; dbQuery query; // Primary Key is the urialias's identity query="identity=",identity.data(), " and domain=", domain.data(); if ( cursor.select( query ) > 0 ) { // Should only be one row so update the contact do { cursor->alias = alias.data(); cursor.update(); } while ( cursor.next() ); } else // Insert as the row does not exist { // Fill out the row columns row.identity = identity.data(); row.domain = domain.data(); row.alias = alias.data(); insert (row); } # if VERBOSE_LOGGING OsSysLog::add(FAC_DB,PRI_DEBUG, "CallerAliasDB::insertRow " "identity='%s', domain='%s', alias='%s'", identity.data(), domain.data(), alias.data() ); # endif // Commit rows to memory - multiprocess workaround mpFastDB->detach(0); // Table Data changed SIPDBManager::getInstance()-> setDatabaseChangedFlag(DbName, TRUE); } else { OsSysLog::add(FAC_DB,PRI_CRIT, "CallerAliasDB::insertRow failed " "db=%p, domain='%s', alias='%s'", mpFastDB, domain.data(), alias.data() ); } } OsStatus CallerAliasDB::load() { // Critical Section here OsLock lock( sLockMutex ); OsStatus result = OS_SUCCESS; if ( mpFastDB != NULL ) { // Clean out the existing DB rows before loading // a new set from persistent storage removeAllRows (); UtlString fileName = SIPDBManager::getInstance()->getConfigDirectory() + OsPath::separator + DbName + ".xml"; OsSysLog::add(FAC_DB, PRI_DEBUG, "CallerAliasDB::load loading '%s'", fileName.data()); TiXmlDocument doc ( fileName ); // Verify that we can load the file (i.e it must exist) if( doc.LoadFile() ) { 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" ) ) { UtlString identity; UtlString domain; UtlString alias; 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 column(elementNode->Value()); if (column.compareTo(IdentityKey) == 0) { SIPDBManager::getAttributeValue(*itemNode, column, identity); } else if (column.compareTo(DomainKey) == 0) { SIPDBManager::getAttributeValue(*itemNode, column, domain); } else if (column.compareTo(AliasKey) == 0) { SIPDBManager::getAttributeValue(*itemNode, column, alias); } else { OsSysLog::add(FAC_DB, PRI_ERR, "Unrecognized column '%s' in item: ignored", column.data() ); } } } // Insert the item row into the IMDB insertRow (identity, domain, alias); } } } else { OsSysLog::add(FAC_DB, PRI_WARNING, "CallerAliasDB::load failed to load '%s'", fileName.data()); } } else { OsSysLog::add(FAC_DB, PRI_ERR, "CallerAliasDB::load failed - no DB"); result = OS_FAILED; } return result; } OsStatus CallerAliasDB::store() { UtlString fileName = SIPDBManager::getInstance()-> getConfigDirectory() + OsPath::separator + DbName + ".xml"; // Create an empty document TiXmlDocument document; // Create a hard coded standalone declaration section document.Parse (""); // Create the root node container TiXmlElement itemsElement ( "items" ); itemsElement.SetAttribute( "type", DbName.data() ); itemsElement.SetAttribute( "xmlns", CALLERALIAS_XML_NAMESPACE_URL ); // Critical Section while actually opening and using the database { OsLock lock( sLockMutex ); if ( mpFastDB != NULL ) { // Thread Local Storage mpFastDB->attach(); // Search our memory for rows dbCursor< CallerAliasRow > cursor; // Select everything in the IMDB and add as item elements if present int rowNumber; int rows; for (rowNumber = 0, rows = cursor.select(); rowNumber < rows; rowNumber++, cursor.next() ) { // Create an item container TiXmlElement itemElement ("item"); if ( *cursor->identity ) { // Add an identity element and put the value in it TiXmlElement identityElement(IdentityKey.data()); TiXmlText identityValue(cursor->identity); identityElement.InsertEndChild(identityValue); itemElement.InsertEndChild(identityElement); } // add the domain element and put the value in it TiXmlElement domainElement(DomainKey.data()); TiXmlText domainValue(cursor->domain); domainElement.InsertEndChild(domainValue); itemElement.InsertEndChild(domainElement); // add the alias element and put the value in it TiXmlElement aliasElement(AliasKey.data()); TiXmlText aliasValue(cursor->alias); aliasElement.InsertEndChild(aliasValue); itemElement.InsertEndChild(aliasElement); // add this item (row) to the parent items container itemsElement.InsertEndChild ( itemElement ); } // Commit rows to memory - multiprocess workaround mpFastDB->detach(0); } } // release mutex around database use // Attach the root node to the document document.InsertEndChild ( itemsElement ); document.SaveFile ( fileName ); return OS_SUCCESS; } void CallerAliasDB::removeAllRows () { // Thread Local Storage if (mpFastDB != NULL) { mpFastDB->attach(); dbCursor< CallerAliasRow > cursor(dbCursorForUpdate); if (cursor.select() > 0) { cursor.removeAllSelected(); } // Commit rows to memory - multiprocess workaround mpFastDB->detach(0); // Table Data changed SIPDBManager::getInstance()-> setDatabaseChangedFlag(DbName, TRUE); } } /// Get the caller alias for this combination of caller identity and target domain. bool CallerAliasDB::getCallerAlias ( const UtlString& identity, ///< identity of caller in 'user@domain' form (no scheme) const UtlString& domain, /**< domain and optional port for target * ( 'example.com' or 'example.com:5099' ) */ UtlString& callerAlias /// returned alias ) const { /* * This first looks in the database for an exact match of identity and domain; * if this match is found, the resulting alias is returned in callerAlias. * If no exact match is found, the database is then checked for a row containing * a null (empty string) identity and the domain; this is a domain wildcard entry * and it is returned in callerAlias. * If neither match is found, callerAlias is set to the null string. */ callerAlias.remove(0); if (mpFastDB) { // Thread Local Storage mpFastDB->attach(); // Search to see if we have a row with this exact combination dbQuery exactQuery; exactQuery="identity=",identity.data()," and domain=",domain.data(); dbCursor< CallerAliasRow > exactCursor; if (exactCursor.select(exactQuery)) { // found a match callerAlias.append(exactCursor->alias); } // Commit the rows to memory - multiprocess workaround mpFastDB->detach(0); } // Returns true if an alias was found for this caller, false if not return ! callerAlias.isNull(); }