📄 vxml.cxx
字号:
// Right now we only support one active grammar.
if (activeGrammar != NULL) {
PTRACE(2, "PVXML\tWarning: can only process one grammar at a time, ignoring previous grammar");
delete activeGrammar;
activeGrammar = NULL;
}
PVXMLGrammar * newGrammar = NULL;
// Is this a built-in type?
PString type = ((PXMLElement*)currentNode)->GetAttribute("type");
if (!type.IsEmpty()) {
PStringArray tokens = type.Tokenise("?;", TRUE);
PString builtintype;
if (tokens.GetSize() > 0)
builtintype = tokens[0];
if (builtintype *= "digits") {
PINDEX minDigits(1);
PINDEX maxDigits(100);
// look at each parameter
for (PINDEX i(1); i < tokens.GetSize(); i++) {
PStringArray params = tokens[i].Tokenise("=", TRUE);
if (params.GetSize() == 2) {
if (params[0] *= "minlength") {
minDigits = params[1].AsInteger();
}
else if (params[0] *= "maxlength") {
maxDigits = params[1].AsInteger();
}
else if (params[0] *= "length") {
minDigits = maxDigits = params[1].AsInteger();
}
}
else {
// Invalid parameter skipped
// LATER: throw 'error.semantic'
}
}
newGrammar = new PVXMLDigitsGrammar((PXMLElement*)currentNode, minDigits, maxDigits, "");
}
else {
// LATER: throw 'error.unsupported'
return FALSE;
}
}
if (newGrammar != NULL)
return LoadGrammar(newGrammar);
return TRUE;
}
// Finds the proper event hander for 'noinput', 'filled', 'nomatch' and 'error'
// by searching the scope hiearchy from the current from
PXMLElement * PVXMLSession::FindHandler(const PString & event)
{
PAssert(currentNode->IsElement(), "Expected 'PXMLElement' in PVXMLSession::FindHandler");
PXMLElement * tmp = (PXMLElement *)currentNode;
PXMLElement * handler = NULL;
// Look in all the way up the tree for a handler either explicitly or in a catch
while (tmp != NULL) {
// Check for an explicit hander - i.e. <error>, <filled>, <noinput>, <nomatch>, <help>
if ((handler = tmp->GetElement(event)) != NULL)
return handler;
// Check for a <catch>
if ((handler = tmp->GetElement("catch")) != NULL) {
PString strCond = handler->GetAttribute("cond");
if (strCond.Find(event))
return handler;
}
tmp = tmp->GetParent();
}
return NULL;
}
void PVXMLSession::SayAs(const PString & className, const PString & _text)
{
PString text = _text.Trim();
if (!text.IsEmpty()) {
PTextToSpeech::TextType type = PTextToSpeech::Literal;
if (className *= "digits")
type = PTextToSpeech::Digits;
else if (className *= "literal")
type = PTextToSpeech::Literal;
else if (className *= "number")
type = PTextToSpeech::Number;
else if (className *= "currency")
type = PTextToSpeech::Currency;
else if (className *= "time")
type = PTextToSpeech::Time;
else if (className *= "date")
type = PTextToSpeech::Date;
else if (className *= "phone")
type = PTextToSpeech::Phone;
else if (className *= "ipaddress")
type = PTextToSpeech::IPAddress;
else if (className *= "duration")
type = PTextToSpeech::Duration;
else
PlayText(text, type);
}
}
PTimeInterval PVXMLSession::StringToTime(const PString & str)
{
PTimeInterval timeout;
long msecs = str.AsInteger();
if (str.Find("ms") != P_MAX_INDEX)
;
else if (str.Find("s") != P_MAX_INDEX)
msecs = msecs * 1000;
return PTimeInterval(msecs);
}
BOOL PVXMLSession::TraverseTransfer()
{
PVXMLTransferOptions opts;
PAssert(currentNode != NULL, "TraverseTransfer(): Expected valid node");
if (currentNode == NULL)
return FALSE;
PAssert(currentNode->IsElement(), "TraverseTransfer(): Expected element");
// Retreive parameters
PString dest = ((PXMLElement*)currentNode)->GetAttribute("dest");
PString source = ((PXMLElement*)currentNode)->GetAttribute("source");
PString connectTimeoutStr = ((PXMLElement*)currentNode)->GetAttribute("connecttimeout");
PString bridgeStr = ((PXMLElement*)currentNode)->GetAttribute("dest");
BOOL bridge = bridgeStr *= "true";
PINDEX connectTimeout = connectTimeoutStr.AsInteger();
if ((connectTimeout < 2) && (connectTimeout > 30))
connectTimeout = 30;
if (dest.Find("phone://") == P_MAX_INDEX)
return FALSE;
dest.Delete(0, 8);
if (source.Find("phone://") == P_MAX_INDEX)
return FALSE;
source.Delete(0, 8);
opts.SetCallingToken(callingCallToken );
opts.SetDestinationDNR(dest);
opts.SetSourceDNR(source);
opts.SetTimeout(connectTimeout);
opts.SetBridge(bridge);
DoTransfer(opts);
// Wait for the transfer result signal
transferSync.Wait();
return TRUE;
}
void PVXMLSession::OnTransfer(const PVXMLTransferResult & args)
{
// transfer has ended, save result
SetVar(args.GetName(), args);
// Signal transfer initiator that the transfer has ended and the VXML can
// continue
transferSync.Signal();
}
BOOL PVXMLSession::TraverseIf()
{
// If 'cond' parameter evaluates to true, enter child entities, else
// go to next element.
PString condition = ((PXMLElement*)currentNode)->GetAttribute("cond");
// Find comparison type
PINDEX location = condition.Find("==");
BOOL isEqual = (location < condition.GetSize());
if (isEqual) {
// Find var name
PString varname = condition.Left(location);
// Find value, skip '=' signs
PString cond_value = condition.Right(condition.GetSize() - (location + 3));
// check if var value equals value from condition and if not skip child elements
PString value = GetVar(varname);
if (cond_value == value) {
PTRACE( 3, "VXMLSess\t\tCondition matched \"" << condition << "\"" );
} else {
PTRACE( 3, "VXMLSess\t\tCondition \"" << condition << "\"did not match, " << varname << " == " << value );
if (currentNode->IsElement()) {
PXMLElement* element = (PXMLElement*) currentNode;
if (element->HasSubObjects()) {
// Step to last child element (really last element is NULL?)
currentNode = element->GetElement(element->GetSize() - 1);
}
}
}
}
else {
PTRACE( 1, "\tPVXMLSession, <if> element contains condition with operator other than ==, not implemented" );
return FALSE;
}
return TRUE;
}
BOOL PVXMLSession::TraverseExit()
{
currentNode = NULL;
forceEnd = TRUE;
waitForEvent.Signal();
return TRUE;
}
BOOL PVXMLSession::TraverseSubmit()
{
BOOL result = FALSE;
// Do HTTP client stuff here
// Find out what to submit, for now, only support a WAV file
PXMLElement * element = (PXMLElement *)currentNode;
if (!element->HasAttribute("namelist")){
PTRACE(1, "VXMLSess\t<submit> does not contain \"namelist\" parameter");
return FALSE;
}
PString name = element->GetAttribute("namelist");
if (name.Find(" ") < name.GetSize()) {
PTRACE(1, "VXMLSess\t<submit> does not support more than one value in \"namelist\" parameter");
return FALSE;
}
if (!element->HasAttribute("next")) {
PTRACE(1, "VXMLSess\t<submit> does not contain \"next\" parameter");
return FALSE;
}
PString url = element->GetAttribute("next");
if (url.Find( "http://" ) > url.GetSize()) {
PTRACE(1, "VXMLSess\t<submit> needs a full url as the \"next\" parameter");
return FALSE;
}
if (!(GetVar(name + ".type") == "audio/x-wav" )) {
PTRACE(1, "VXMLSess\t<submit> does not (yet) support submissions of types other than \"audio/x-wav\"");
return FALSE;
}
PString fileName = GetVar(name + ".filename");
if (!(element->HasAttribute("method"))) {
PTRACE(1, "VXMLSess\t<submit> does not (yet) support default method type \"get\"");
return FALSE;
}
if ( !PFile::Exists(fileName )) {
PTRACE(1, "VXMLSess\t<submit> cannot find file " << fileName);
return FALSE;
}
PString fileNameOnly;
int pos = fileName.FindLast( "/" );
if (pos < fileName.GetLength()) {
fileNameOnly = fileName.Right( ( fileName.GetLength() - pos ) - 1 );
}
else {
pos = fileName.FindLast("\\");
if (pos < fileName.GetSize()) {
fileNameOnly = fileName.Right((fileName.GetLength() - pos) - 1);
}
else {
fileNameOnly = fileName;
}
}
PHTTPClient client;
PMIMEInfo sendMIME, replyMIME;
if (element->GetAttribute("method") *= "post") {
// 1 2 3 4123
PString boundary = "--------012345678901234567890123458VXML";
sendMIME.SetAt( PHTTP::ContentTypeTag, "multipart/form-data; boundary=" + boundary);
sendMIME.SetAt( PHTTP::UserAgentTag, "PVXML TraverseSubmit" );
sendMIME.SetAt( "Accept", "text/html" );
// After this all boundaries have a "--" prepended
boundary = "--" + boundary;
// Create the mime header
// First set the primary boundary
PString mimeHeader = boundary + "\r\n";
// Add content disposition
mimeHeader += "Content-Disposition: form-data; name=\"voicemail\"; filename=\"" + fileNameOnly + "\"\r\n";
// Add content type
mimeHeader += "Content-Type: audio/wav\r\n\r\n";
// Create the footer and add the closing of the content with a CR/LF
PString mimeFooter = "\r\n";
// Copy the header, buffer and footer together in one PString
// Load the WAV file into memory
PFile file( fileName, PFile::ReadOnly );
int size = file.GetLength();
PString mimeThing;
// Make PHP happy?
// Anyway, this shows how to add more variables, for when namelist containes more elements
PString mimeMaxFileSize = boundary + "\r\nContent-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n3000000\r\n";
// Finally close the body with the boundary again, but also add "--"
// to show this is the final boundary
boundary = boundary + "--";
mimeFooter += boundary + "\r\n";
mimeHeader = mimeMaxFileSize + mimeHeader;
mimeThing.SetSize( mimeHeader.GetSize() + size + mimeFooter.GetSize() );
// Copy the header to the result
memcpy( mimeThing.GetPointer(), mimeHeader.GetPointer(), mimeHeader.GetLength());
// Copy the contents of the file to the mime result
file.Read( mimeThing.GetPointer() + mimeHeader.GetLength(), size );
// Copy the footer to the result
memcpy( mimeThing.GetPointer() + mimeHeader.GetLength() + size, mimeFooter.GetPointer(), mimeFooter.GetLength());
// Send the POST request to the server
result = client.PostData( url, sendMIME, mimeThing, replyMIME );
// TODO, Later:
// Remove file?
// Load reply from server as new VXML docuemnt ala <goto>
}
else {
if (element->GetAttribute("method") != "get") {
PTRACE(1, "VXMLSess\t<submit> does not (yet) support method type \"" << element->GetAttribute( "method" ) << "\"");
return FALSE;
}
PString getURL = url + "?" + name + "=" + GetVar( name );
client.GetDocument( url, sendMIME, replyMIME );
// TODO, Later:
// Load reply from server as new VXML document ala <goto>
}
if (!result) {
PTRACE( 1, "VXMLSess\t<submit> to server failed with "
<< client.GetLastResponseCode() << " "
<< client.GetLastResponseInfo() );
}
return result;
}
BOOL PVXMLSession::TraverseProperty()
{
PXMLElement* element = (PXMLElement *) currentNode;
if (element->HasAttribute("name"))
SetVar(element->GetAttribute("name"), element->GetAttribute("value"));
return TRUE;
}
BOOL PVXMLSession::TraverseMenu()
{
BOOL result = FALSE;
PVXMLGrammar * newGrammar = new PVXMLDigitsGrammar((PXMLElement*) currentNode, 1, 1, "" );
LoadGrammar(newGrammar);
result = TRUE;
return result;
}
BOOL PVXMLSession::TraverseChoice(const PString & grammarResult)
{
// Iterate over all choice elements starting at currentnode
BOOL result = FALSE;
PXMLElement* element = (PXMLElement *) currentNode;
// Current node is a choice element
PString dtmf = element->GetAttribute( "dtmf" );
if (dtmf.IsEmpty())
dtmf = PString(PString::Unsigned, defaultDTMF);
// Check if DTMF value for grammarResult matches the DTMF value for the choice
if (dtmf == grammarResult) {
// Find the form at next parameter
PString formID = element->GetAttribute( "next" );
PTRACE(2, "VXMLsess\tFound form id " << formID );
if (!formID.IsEmpty()) {
formID = formID.Right( formID.GetLength() - 1 );
currentNode = FindForm( formID );
if (currentNode != NULL)
result = TRUE;
}
}
return result;
}
BOOL PVXMLSession::TraverseVar()
{
BOOL result = FALSE;
PXMLElement* element = (PXMLElement *) currentNode;
PString name = element->GetAttribute( "name" );
PString expr = element->GetAttribute( "expr" );
if (name.IsEmpty() || expr.IsEmpty()) {
PTRACE( 1, "VXMLSess\t<var> has a problem with its parameters, name=\"" << name << "\", expr=\"" << expr << "\"" );
}
else {
SetVar(name, expr);
result = TRUE;
}
return result;
}
void PVXMLSession::OnEndRecording(const PString & /*channelName*/)
{
//SetVar(channelName + ".size", PString(incomingChannel->GetWAVFile(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -