//
//
// Copyright (C) 2006 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2006 Pingtel Corp.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES
#include <stdlib.h>
// APPLICATION INCLUDES
#include "os/OsSysLog.h"
#include <os/OsFS.h>
#include <os/OsDateTime.h>
#include "xmlparser/tinyxml.h"
#include "xmlparser/ExtractContent.h"
#include <utl/UtlHashMapIterator.h>
#include "filereader/OrbitFileReader.h"
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// STATIC VARIABLE INITIALIZATIONS
UtlContainableType OrbitData::TYPE = "OrbitData" ;
const int OrbitData::NO_TIMEOUT = -1;
const int OrbitData::NO_KEYCODE = -1;
const int OrbitData::UNLIMITED_CAPACITY = 1000000;
// String containing the characters used in the orbits.xml file to represent
// the RFC 2833 keycodes in order by their RFC 2833 values.
const static UtlString valid_keycodes = "0123456789*#";
/* //////////////////////////// PUBLIC //////////////////////////////////// */
/* ============================ CREATORS ================================== */
// Constructor
OrbitFileReader::OrbitFileReader()
{
}
void OrbitFileReader::setFileName(const UtlString& fileName)
{
// Set the file name.
mOrbitFileName = fileName;
OsSysLog::add(FAC_DB, PRI_INFO, "OrbitFileReader:: "
"Orbit file is '%s'", mOrbitFileName.data());
// Initialize the state variables.
// 0 is never returned as a value of OsDateTime::getSecsSinceEpoch()..
mOrbitFileLastModTimeCheck = 0;
// OS_INFINITY is never a valid time.
// We use it as a dummy meaning "file does not exist".
mOrbitFileModTime = OsTime::OS_INFINITY;
// Force the caching scheme to read in the orbit file, so that
// if there are any failures, they are reported near the start
// of the application's log file.
UtlString dummy("dummy value");
findInOrbitList(dummy);
}
// Destructor
OrbitFileReader::~OrbitFileReader()
{
}
/* ============================ MANIPULATORS ============================== */
/* ============================ ACCESSORS ================================= */
/* ============================ INQUIRY =================================== */
// Return pointer to the OrbitData structure if the argument is an
// orbit name listed in the orbits.xml file.
OrbitData* OrbitFileReader::findInOrbitList(const UtlString& user)
{
OrbitData* ret;
// Refresh mOrbitList if necessary.
refresh();
// Check to see if 'user' is in it. If so, return a pointer to its
// data.
ret = dynamic_cast <OrbitData*> (mOrbitList.findValue(&user));
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::findInOrbitList "
"user = '%s', ret = %p",
user.data(), ret);
return ret;
}
// Retrieve the "music on hold" file name.
void OrbitFileReader::getMusicOnHoldFile(UtlString& file)
{
// Refresh mMusicOnHoldFile if necessary.
refresh();
// Get the value.
file = mMusicOnHoldFile;
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::getMusicOnHoldFile "
"file = '%s'",
file.data());
return;
}
/* //////////////////////////// PROTECTED ///////////////////////////////// */
// Refresh the data structures if the contents of the file has changed.
// This function takes some care to avoid re-reading orbits.xml when it has
// not changed since the last call.
// The strategy is to check the modification time of the orbits.xml file,
// and only re-read orbits.xml if the modification time has changed since
// the last time we checked it.
// But checking the modification time is relatively slow, and we do not want
// to do it on all calls in a high-usage system. So we check the clock time
// instead, and if it has been 1 second since the last time we checked
// the modification time of orbits.xml, we check it again.
// Checking the clock time is fast (about 1.6 microseconds on a 2GHz
// processor), and checking the modification time of orbits.xml once a
// second is acceptable.
// Any process which changes orbits.xml should wait 1 second before reporting
// that it has succeeded, and before doing any further changes to orbits.xml.
void OrbitFileReader::refresh()
{
// If there is no orbit file name, just return, as there is no file
// to read..
if (!mOrbitFileName.isNull())
{
// Check to see if 1 second has elapsed since the last time we checked
// the modification time of orbits.xml.
unsigned long current_time = OsDateTime::getSecsSinceEpoch();
if (current_time != mOrbitFileLastModTimeCheck)
{
// It has been.
mOrbitFileLastModTimeCheck = current_time;
// Check to see if orbits.xml has a different modification time than
// the last time we checked.
OsFile orbitFile(mOrbitFileName);
OsFileInfo fileInfo;
OsTime mod_time;
if (orbitFile.getFileInfo(fileInfo) == OS_SUCCESS) {
// If the file exists, use its modification time.
fileInfo.getModifiedTime(mod_time);
}
else
{
// If the file does not exist, use OS_INFINITY as a dummy value.
mod_time = OsTime::OS_INFINITY;
}
// Check to see if the modification time of orbits.xml is different
// than the last time we checked.
if (mod_time != mOrbitFileModTime)
{
// It is different.
mOrbitFileModTime = mod_time;
// Clear the list of the previous orbit names.
mOrbitList.destroyAll();
// Forget the music-on-hold file.
mMusicOnHoldFile.remove(0);
if (mOrbitFileModTime != OsTime::OS_INFINITY)
{
// The file exists, so we should read and parse it.
OsStatus status = parseOrbitFile(mOrbitFileName);
OsSysLog::add(FAC_PARK, PRI_INFO,
"OrbitFileReader::findInOrbitList "
"Called parseOrbitFile('%s') returns %s",
mOrbitFileName.data(),
status == OS_SUCCESS ? "SUCCESS" : "FAILURE");
}
else
{
// The file does not exist, that is not an error.
// Take no further action.
OsSysLog::add(FAC_PARK, PRI_INFO,
"OrbitFileReader::findInOrbitList "
"Orbit file '%s' does not exist",
mOrbitFileName.data());
}
}
}
}
}
// Read and parse the orbits.xml file into the data structures.
OsStatus OrbitFileReader::parseOrbitFile(UtlString& fileName)
{
// Initialize Tiny XML document object.
TiXmlDocument document;
TiXmlNode* orbits_element;
if (
// Load the XML into it.
document.LoadFile(fileName.data()) &&
// Find the top element, which should be an <orbits>.
(orbits_element = document.FirstChild("orbits")) != NULL &&
orbits_element->Type() == TiXmlNode::ELEMENT)
{
// Find all the <orbit> elements.
for (TiXmlNode* orbit_element = 0;
(orbit_element = orbits_element->IterateChildren("orbit",
orbit_element));
)
{
// Process each <orbit> element.
bool orbit_valid = true;
// Process the <extension> element.
TiXmlNode* extension_element =
orbit_element->FirstChild("extension");
UtlString extension;
if (extension_element)
{
textContentShallow(extension, extension_element->ToElement());
if (extension.isNull())
{
// Extension had zero length
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Extension was null.");
orbit_valid = false;
}
}
else
{
// Extension was missing.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Extension was missing.");
orbit_valid = false;
}
// Process the <background-audio> element.
TiXmlNode* audio_element =
orbit_element->FirstChild("background-audio");
UtlString audio;
if (audio_element)
{
textContentShallow(audio, audio_element->ToElement());
if (audio.isNull())
{
// Extension had zero length
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Background-audio was null for extension '%s'",
extension.data());
orbit_valid = false;
}
}
else
{
// Background-audio was missing.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Background-audio was missing for extension '%s'",
extension.data());
orbit_valid = false;
}
// Process the <timeout> element to set mTimeout.
int timeout = OrbitData::NO_TIMEOUT; // Assume no value present.
TiXmlNode* timeout_element =
orbit_element->FirstChild("timeout");
if (timeout_element)
{
UtlString temp;
textContentShallow(temp, timeout_element->ToElement());
char *endptr;
timeout = strtol(temp.data(), &endptr, 10);
if (temp.isNull() ||
endptr - temp.data() != temp.length() ||
timeout < 5)
{
// Timeout was null or unparsable.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Timeout '%s' was null, unparsable, or less than 5 for extension '%s'",
temp.data(), extension.data());
orbit_valid = false;
}
}
// Process the <keycode> element to set mKeycode.
int keycode = OrbitData::NO_KEYCODE; // Assume no value present.
TiXmlNode* keycode_element =
orbit_element->FirstChild("keycode");
if (keycode_element)
{
UtlString temp;
textContentShallow(temp, keycode_element->ToElement());
if (temp.length() == 1 &&
(keycode = valid_keycodes.index(temp[0]),
keycode != UTL_NOT_FOUND))
{
/* null */ ;
}
else
{
// Keycode was null or unparsable.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Keycode '%s' was null or unparsable for extension '%s'",
temp.data(), extension.data());
orbit_valid = false;
}
}
// Process the <capcity> element to set mCapacity.
int capacity = OrbitData::UNLIMITED_CAPACITY; // Assume no value present.
TiXmlNode* capacity_element =
orbit_element->FirstChild("capacity");
if (capacity_element)
{
UtlString temp;
textContentShallow(temp, capacity_element->ToElement());
char *endptr;
capacity = strtol(temp.data(), &endptr, 10);
if (temp.isNull() ||
endptr - temp.data() != temp.length() ||
capacity < 0)
{
// Capacity was null or unparsable.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Capacity '%s' was null, unparsable, or negative for extension '%s'",
temp.data(), extension.data());
orbit_valid = false;
}
}
// If no errors were found, create the values to insert into
// mOrbitList.
if (orbit_valid)
{
// Allocate the objects and assign their values.
UtlString* extension_heap = new UtlString;
*extension_heap = extension;
OrbitData* orbit_data_heap = new OrbitData;
orbit_data_heap->mTimeout = timeout;
orbit_data_heap->mAudio = audio;
orbit_data_heap->mKeycode = keycode;
orbit_data_heap->mCapacity = capacity;
// Attempt to insert the user into the orbit list.
if (mOrbitList.insertKeyAndValue(extension_heap, orbit_data_heap))
{
// Insertion succeeded.
// *extension_heap and *orbit_data_heap are now owned
// by mOrbitList.
}
else
{
// Insertion failed, presumably because the extension was
// already in there.
OsSysLog::add(FAC_PARK, PRI_ERR,
"OrbitFileReader::parseOrbitFile "
"Inserting extension '%s' failed -- specified as an orbit twice?",
extension_heap->data());
// mOrbitList does not own the objects, so we must delete them.
delete extension_heap;
delete orbit_data_heap;
}
}
}
if (OsSysLog::willLog(FAC_PARK, PRI_DEBUG))
{
// Output the list of orbits.
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::parseOrbitFile "
"Valid orbits are:");
UtlHashMapIterator itor(mOrbitList);
while (itor())
{
UtlString* key = dynamic_cast<UtlString*> (itor.key());
OrbitData* value = dynamic_cast<OrbitData*> (itor.value());
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::parseOrbitFile "
"Orbit '%s', mTimeout = %d, mAudio = '%s', "
"mKeycode = %d, mCapacity = %d",
key->data(),
value->mTimeout,
value->mAudio.data(),
value->mKeycode,
value->mCapacity);
}
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::parseOrbitFile "
"End of list");
}
// Find the <music-on-hold> element.
TiXmlNode *groupNode = orbits_element->FirstChild("music-on-hold");
if (groupNode != NULL)
{
TiXmlNode* audioNode = groupNode->FirstChild("background-audio");
if ((audioNode != NULL)
&& (audioNode->FirstChild() != NULL))
{
mMusicOnHoldFile = (audioNode->FirstChild())->Value();
}
}
OsSysLog::add(FAC_PARK, PRI_DEBUG,
"OrbitFileReader::parseOrbitFile "
"mMusicOnHoldFile = '%s'",
mMusicOnHoldFile.data());
// In any of these cases, attempt to do call retrieval.
return OS_SUCCESS;
}
else
{
// Report error parsing file.
OsSysLog::add(FAC_PARK, PRI_CRIT,
"OrbitFileReader::parseOrbitFile "
"Orbit file '%s' could not be parsed.", fileName.data());
// No hope of doing call retrieval.
return OS_FAILED;
}
}
syntax highlighted by Code2HTML, v. 0.9.1