VXI.cpp
上传用户:xqtpzdz
上传日期:2022-05-21
资源大小:1764k
文件大小:151k
- /****************License************************************************
- * Vocalocity OpenVXI
- * Copyright (C) 2004-2005 by Vocalocity, Inc. All Rights Reserved.
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Vocalocity, the Vocalocity logo, and VocalOS are trademarks or
- * registered trademarks of Vocalocity, Inc.
- * OpenVXI is a trademark of Scansoft, Inc. and used under license
- * by Vocalocity.
- ***********************************************************************/
- #include "VXI.hpp"
- #include "DocumentParser.hpp"
- #include "SimpleLogger.hpp"
- #include "ValueLogging.hpp" // for VXIValue dumping
- #include "Scripter.hpp" // for use in ExecutionContext
- #include "GrammarManager.hpp" // for use in ExecutionContext
- #include "Counters.hpp" // for EventCounter
- #include "PropertyList.hpp" // for PropertyList
- #include "PromptManager.hpp" // for PromptManager
- #include "AnswerParser.hpp" // for AnswerParser
- #include "VXML.h" // for node & attribute names
- #include "VXIrec.h" // for parameters from rec answer
- #include "VXIlog.h" // for event IDs
- #include "VXIobject.h" // for Object type
- #include "VXIinet.h"
- #include "DialogEventCounter.hpp"
- #include "TokenList.hpp"
- #include "AccessControl.hpp"
- #include <sstream>
- #include <algorithm> // for sort, set_intersection, etc.
- #include <xercesc/dom/DOMDocument.hpp>
- static const wchar_t * const SCOPE_Session = L"session";
- static const wchar_t * const SCOPE_Defaults = L"$_platDefaults_";
- static const wchar_t * const SCOPE_Application = L"application";
- static const wchar_t * const SCOPE_Document = L"document";
- static const wchar_t * const SCOPE_Dialog = L"dialog";
- static const wchar_t * const SCOPE_Local = L"local";
- static const wchar_t * const SCOPE_Anonymous = L"$_execAnonymous_";
- static const wchar_t * const SESSION_INET = L"com.vocalocity.inet";
- static const wchar_t * const GENERIC_DEFAULTS = L"*";
- const int DEFAULT_MAX_EXE_STACK_DEPTH = 50;
- const int DEFAULT_MAX_EVENT_RETHROWS = 6;
- const int DEFAULT_MAX_EVENT_COUNT = 12;
- // ------*---------*---------*---------*---------*---------*---------*---------
- static vxistring toString(const VXIString * s)
- {
- if (s == NULL) return L"";
- const VXIchar * temp = VXIStringCStr(s);
- if (temp == NULL) return L"";
- return temp;
- }
- static vxistring toString(const VXIchar * s)
- {
- if (s == NULL) return L"";
- return s;
- }
- /*
- * Implementation of the PromptManager PromptTranslator interface.
- */
- class VXIPromptTranslator : public PromptTranslator {
- public:
- virtual VXIValue * EvaluateExpression(const vxistring & expression) {
- if (expression.empty()) return NULL;
- return jsi.EvalScriptToValue(expression);
- }
- virtual void SetVariable(const vxistring & name, const vxistring & value) {
- if (name.empty()) return;
- jsi.MakeVar(name, (VXIValue*)NULL);
- jsi.SetString(name, value);
- }
- virtual bool TestCondition(const vxistring & script) {
- return jsi.TestCondition(script);
- }
- virtual void EvalScript(const vxistring & script) {
- jsi.EvalScript(script);
- }
- VXIPromptTranslator(Scripter & j) : jsi(j) { }
- virtual ~VXIPromptTranslator() { }
- private:
- Scripter & jsi;
- };
- /*
- * Implementation of the AnswerParser AnswerTranslator interface.
- */
- class VXIAnswerTranslator : public AnswerTranslator {
- public:
- virtual void EvaluateExpression(const vxistring & expression) {
- if (expression.empty()) return;
- jsi.EvalScript(expression);
- }
- virtual void SetString(const vxistring & var, const vxistring & val) {
- jsi.SetString(var, val);
- }
- VXIAnswerTranslator(Scripter & j) : jsi(j) { }
- virtual ~VXIAnswerTranslator() { }
- private:
- Scripter & jsi;
- };
- // Thrown after a successful recognition.
- //
- class AnswerInformation {
- public:
- VXMLElement element;
- VXMLElement dialog;
- AnswerInformation(VXMLElement & e, VXMLElement & d)
- : element(e), dialog(d) { }
- };
- class JumpReturn {
- public:
- JumpReturn() { }
- };
- class JumpItem {
- public:
- VXMLElement item;
- JumpItem(const VXMLElement & i) : item(i) { }
- };
- class JumpDialog {
- public:
- VXMLElement dialog;
- JumpDialog(const VXMLElement d) : dialog(d) { }
- };
- class JumpDoc {
- public:
- VXMLElement defaults;
- VXMLDocument application;
- vxistring applicationURI;
- VXMLDocument document;
- vxistring documentURI;
- VXMLElement documentDialog;
- bool isSubdialog;
- bool isSubmitElement;
- PropertyList properties;
-
- JumpDoc(const VXMLElement & def, const VXMLDocument & app,
- const vxistring & appURI, const VXMLDocument & doc, const vxistring & docURI,
- const VXMLElement & docdial, bool issub, bool issubmit, const PropertyList & p)
- : defaults(def), application(app), applicationURI(appURI),
- document(doc), documentURI(docURI), documentDialog(docdial), isSubdialog(issub),
- isSubmitElement(issubmit), properties(p)
- { }
- ~JumpDoc() { }
- };
- //#############################################################################
- // ExecutionContext & Utilities
- //#############################################################################
- class ExecutionContext {
- public:
- // These are used by the main run loops, event handling, and subdialog.
- // Each of these gets initialized by InstallDocument.
- VXMLElement platDefaults;
- VXMLDocument application;
- vxistring applicationName;
- vxistring applicationURI;
- VXIMapHolder appProperties;
-
- VXMLDocument document;
- vxistring documentName;
- vxistring documentURI;
- VXMLElement currentDialog;
- VXMLElement currentFormItem;
- // Limited purpose members.
- VXMLElement eventSource; // For prompting (enumerate) during events
- VXMLElement lastItem; // Set by <subdialog>
- public: // These are used more generally.
- Scripter script;
- GrammarManager gm;
- PromptTracker promptcounts;
- DialogEventCounter eventcounts;
- PropertyList properties;
- TokenList formitems;
- bool playingPrompts;
- ExecutionContext * next;
- ExecutionContext(VXIrecInterface * r, VXIjsiInterface * j,
- const SimpleLogger & l, ExecutionContext * n)
- : script(j), gm(r, l), next(n), playingPrompts(true), properties(l) { }
- // may throw VXIException::OutOfMemory()
- ~ExecutionContext() { }
- };
- //#############################################################################
- // Creation and Run
- //#############################################################################
- VXI::VXI()
- : parser(NULL), log(NULL), inet(NULL), rec(NULL), jsi(NULL), tel(NULL),
- exe(NULL), stackDepth(0), sdParams(NULL), sdResult(NULL), sdEvent(NULL),
- updateDefaultDoc(true), mutex(), uriPlatDefaults(), uriBeep(), uriCurrent(),
- lineHungUp(false), stopRequested(false), haveExternalEvents(false),defAccessControl(false)
- {
- try {
- parser = new DocumentParser();
- }
- catch (const VXIException::OutOfMemory &) {
- parser = NULL;
- throw;
- }
- try {
- pm = new PromptManager();
- }
- catch (...) {
- delete parser;
- parser = NULL;
- pm = NULL;
- throw;
- }
- if (pm == NULL) {
- delete parser;
- parser = NULL;
- throw VXIException::OutOfMemory();
- }
- }
- VXI::~VXI()
- {
- while (exe != NULL) {
- ExecutionContext * temp = exe->next;
- delete exe;
- exe = temp;
- }
- delete pm;
- delete parser;
- }
- // Determine if the user has already hung-up
- // if it is the case, throw the exit element to indicate
- // end of call
- void VXI::CheckLineStatus()
- {
- VXItelStatus status;
- int telResult = tel->GetStatus(tel, &status);
- bool needToThrow = false;
- if (telResult != VXItel_RESULT_SUCCESS) {
- needToThrow = true;
- log->LogError(201);
- }
- else if (status == VXItel_STATUS_INACTIVE)
- needToThrow = true;
- if (needToThrow) {
- mutex.Lock();
- bool alreadyHungup = lineHungUp;
- lineHungUp = true;
- mutex.Unlock();
- if (alreadyHungup) {
- log->LogDiagnostic(1, L"VXI::CheckLineStatus - Call has been hung-up, "
- L"exiting...");
- throw VXIException::Exit(NULL);
- }
- else
- throw VXIException::InterpreterEvent(EV_TELEPHONE_HANGUP);
- }
- }
- bool VXI::SetRuntimeProperty(PropertyID id, const VXIValue * value)
- {
- bool set = false;
- mutex.Lock();
- switch (id) {
- case VXI::BeepURI:
- if (VXIValueGetType(value) == VALUE_STRING) {
- const VXIchar * uri = VXIStringCStr(reinterpret_cast<const VXIString *>(value));
- if (uri == NULL) uriBeep.erase();
- else uriBeep = uri;
- set = true;
- }
- break;
- case VXI::PlatDefaultsURI:
- if (VXIValueGetType(value) == VALUE_STRING) {
- const VXIchar * uri = VXIStringCStr(reinterpret_cast<const VXIString *>(value));
- if (uri == NULL) uriPlatDefaults.erase();
- else uriPlatDefaults = uri;
- updateDefaultDoc = true;
- set = true;
- }
- break;
- case VXI::DefaultAccessControl:
- if (VXIValueGetType(value) == VALUE_INTEGER) {
- int def = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(value));
- defAccessControl = def ? true : false;
- set = true;
- }
- break;
- }
- mutex.Unlock();
- return set;
- }
- void VXI::GetRuntimeProperty(PropertyID id, vxistring & value) const
- {
- mutex.Lock();
- switch (id) {
- case VXI::BeepURI: value = uriBeep; break;
- case VXI::PlatDefaultsURI: value = uriPlatDefaults; break;
- }
- mutex.Unlock();
- }
- void VXI::GetRuntimeProperty(PropertyID id, int &value) const
- {
- mutex.Lock();
- switch (id) {
- case VXI::DefaultAccessControl: value = defAccessControl; break;
- }
- mutex.Unlock();
- }
- void VXI::DeclareStopRequest(bool doStop)
- {
- mutex.Lock();
- stopRequested = doStop;
- mutex.Unlock();
- }
- bool VXI::DeclareExternalEvent(const VXIchar * event, const VXIchar * message)
- {
- // Check event
- if (event == NULL || *event == L' ' || *event == L'.') return 1;
- bool lastWasPeriod = false;
- // This could be stricter
- for (const VXIchar * i = event; *i != L' '; ++i) {
- VXIchar c = *i;
- if (c == L'.') {
- if (lastWasPeriod)
- return false;
- lastWasPeriod = true;
- }
- else {
- if (c == L' ' || c == L't' || c == L'n' || c == L'r')
- return false;
- lastWasPeriod = false;
- }
- }
- mutex.Lock();
- externalEvents.push_back(event);
- if (message == NULL || message == L' ')
- externalMessages.push_back(event);
- else
- externalMessages.push_back(message);
- haveExternalEvents = true;
- mutex.Unlock();
- return true;
- }
- bool VXI::ClearExternalEventQueue()
- {
- mutex.Lock();
- if( !externalEvents.empty() )
- {
- haveExternalEvents = false;
- externalEvents.clear();
- externalMessages.clear();
- }
- mutex.Unlock();
- return true;
- }
- int VXI::Run(const VXIchar * initialDocument,
- const VXIchar * sessionScript,
- SimpleLogger * resourceLog,
- VXIinetInterface * resourceInet,
- VXIcacheInterface * resourceCache,
- VXIjsiInterface * resourceJsi,
- VXIrecInterface * resourceRec,
- VXIpromptInterface * resourcePrompt,
- VXItelInterface * resourceTel,
- VXIobjectInterface * resourceObject,
- VXIValue ** resultValue)
- {
- // (1) Check arguments.
- // (1.1) Check external resources
- if (resourceLog == NULL || resourceInet == NULL || resourceJsi == NULL ||
- resourceRec == NULL || resourcePrompt == NULL || resourceTel == NULL)
- return 1;
- log = resourceLog;
- inet = resourceInet;
- jsi = resourceJsi;
- rec = resourceRec;
- tel = resourceTel;
- if (stopRequested) {
- DeclareStopRequest(false);
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::Run( STOP HAS BEEN REQUESTED ) exiting...";
- log->EndDiagnostic();
- }
- return 4;
- }
- // These may be NULL.
- object = resourceObject;
- cache = resourceCache;
- if (!pm->ConnectResources(log, resourcePrompt, this)) return 1;
-
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::Run(" << initialDocument << L")";
- log->EndDiagnostic();
- }
- // (1.2) Check document
- if (initialDocument == NULL || wcslen(initialDocument) == 0) {
- log->LogError(201);
- return 1;
- }
- // (2) Delegate real work to RunDocumentLoop & handle the serious errors.
- int exitCode;
- try {
- exitCode = RunDocumentLoop(initialDocument,
- (sessionScript ? sessionScript : L""),
- resultValue);
- pm->PlayAll();
- }
- catch (VXIException::InterpreterEvent & e) {
- log->LogError(207, SimpleLogger::EXCEPTION, e.GetValue().c_str());
- exitCode = 0;
- }
- catch (const VXIException::Exit & e) {
- *resultValue = e.exprResult;
- exitCode = 0;
- }
- catch (const VXIException::Fatal &) {
- log->LogError(209);
- exitCode = -2;
- }
- catch (const VXIException::OutOfMemory &) {
- PopExecutionContext();
- log->LogError(202);
- exitCode = 1;
- }
- catch (const VXIException::JavaScriptError &) {
- log->LogError(212);
- exitCode = -2;
- }
- catch (const VXIException::StopRequest &) {
- exitCode = 4;
- }
- catch (const JumpDialog &) {
- log->LogError(999, SimpleLogger::MESSAGE, L"unexpected jump to a dialog");
- exitCode = -2;
- }
- catch (const JumpDoc &) {
- log->LogError(999, SimpleLogger::MESSAGE,L"unexpected jump to a document");
- exitCode = -2;
- }
- catch (const JumpItem &) {
- log->LogError(999, SimpleLogger::MESSAGE, L"unexpected jump to an item");
- exitCode = -2;
- }
- catch (const JumpReturn &) {
- log->LogError(999, SimpleLogger::MESSAGE,
- L"unexpected jump from a return element");
- exitCode = -2;
- }
- // Clean up execution contexts.
- try {
- while (exe != NULL)
- PopExecutionContext();
- }
- catch (const VXIException::JavaScriptError &) {
- log->LogError(212);
- exitCode = -2;
- }
- // Clean up event flags.
- lineHungUp = false;
- stopRequested = false;
- //haveExternalEvents = false;
- //externalEvents.clear();
- //externalMessages.clear();
- return exitCode;
- }
- int VXI::RunDocumentLoop(const vxistring & initialDocument,
- const vxistring & sessionScript,
- VXIValue ** resultValue)
- {
- // (1) Load the document containing the default handlers.
- if (updateDefaultDoc) {
- log->LogDiagnostic(2, L"VXI::RunDocumentLoop - loading defaultDoc.");
- // (1.1) Get URI if possible.
- vxistring defaultsUri;
- GetRuntimeProperty(VXI::PlatDefaultsURI, defaultsUri);
-
- // (1.2) Load platform defaults.
- try {
- VXIMapHolder domDefaultProp;
- if (log->IsLogging(4) && !defaultsUri.empty()) {
- log->StartDiagnostic(4) << L"VXI: Loading platform defaults <"
- << defaultsUri << L">";
- log->EndDiagnostic();
- }
- AttemptDocumentLoad(defaultsUri, VXIMapHolder(NULL), domDefaultDoc,
- domDefaultProp, true);
- }
- catch (const VXIException::InterpreterEvent & e) {
- log->LogError(221, SimpleLogger::EXCEPTION, e.GetValue().c_str());
- return 3;
- }
-
- // Only reset this flag if the load has succeeded
- updateDefaultDoc = false;
- }
- // (2) Create a new execution context and initialize the defaults.
- // (2.1) Create new execution context.
- if (!PushExecutionContext(sessionScript)) return -1;
- // (3) Execute the document.
- bool firstTime = true;
- VXIint32 loopCount = 0;
- while (1) {
- if (stopRequested) throw VXIException::StopRequest();
- try {
- if (firstTime) {
- firstTime = false;
- // Jump to the initial document. Any events which occur while
- // loading the initial document are handled by the defaults.
- log->LogDiagnostic(2, L"VXI::RunDocumentLoop - loading initial "
- L"document.");
- try {
- PerformTransition(exe->platDefaults, initialDocument);
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(exe->platDefaults, e);
- PopExecutionContext();
- throw VXIException::Exit(NULL);
- }
- }
- log->LogDiagnostic(2, L"VXI::RunDocumentLoop - new document");
- RunInnerLoop();
- break;
- }
- // Execute a new document
- catch (JumpDoc & e) {
- // if <subdialog>, we need to create a new context, while
- // storing the current.
- if (e.isSubdialog)
- if (!PushExecutionContext(sessionScript)) return -1;
- if (log->IsLogging(4)) {
- if (e.isSubdialog)
- log->StartDiagnostic(4) << L"VXI: Subdialog transition <"
- << e.documentURI << L">";
- else
- log->StartDiagnostic(4) << L"VXI: Document transition <"
- << e.documentURI << L">";
- log->EndDiagnostic();
- }
- InstallDocument(e);
- }
- // Handle <return>
- catch (JumpReturn &) {
- // clean up the current context, and restore the original.
- PopExecutionContext();
- if (log->IsLogging(4)) {
- log->StartDiagnostic(4) << L"VXI: Subdialog returned to <"
- << exe->documentURI << L">";
- log->EndDiagnostic();
- }
- }
- // Handle <exit> by exiting the document loop.
- catch (const VXIException::Exit & e) {
- if (resultValue != NULL) *resultValue = e.exprResult;
- break;
- }
- }
- // Done with document execution...clean up.
- PopExecutionContext();
- return 0;
- }
- void VXI::InstallDocument(JumpDoc & e)
- {
- // (1) Check to see what needs to be initialized.
- // If the transition is from leaf-to-root occurs in <submit> then
- // the root context is initialized, otherwise it is preserved.
- bool leafToRoot = !e.isSubmitElement && (!e.documentURI.empty())
- && (exe->applicationURI == e.documentURI);
- bool reinitApplication
- = e.isSubdialog ||
- (e.applicationURI.empty() && exe->applicationURI.empty()) ||
- (!leafToRoot && (e.applicationURI != exe->applicationURI));
- // (2) Set the easy stuff.
- exe->platDefaults = e.defaults;
- exe->application = e.application;
- exe->applicationURI = e.applicationURI;
- exe->document = e.document;
- exe->documentURI = e.documentURI;
- exe->currentDialog = e.documentDialog;
- exe->currentFormItem = VXMLElement();
- exe->eventSource = VXMLElement();
- exe->properties = e.properties;
- exe->gm.ReleaseGrammars();
- VXMLElement documentRoot = exe->document.GetRoot();
- VXMLElement applicationRoot = exe->application.GetRoot();
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::InstallDocument(documentURI:[" << e.documentURI
- << L"], appURI:[" << e.applicationURI << L"])";
- log->EndDiagnostic();
- }
-
- // (3) Load grammars. The grammars are reloaded using the current
- // understanding of the properties. During activation, the actual
- // properties may differ slightly.
- VXIPromptTranslator translator(exe->script);
- try {
- exe->documentName = L"";
- exe->gm.LoadGrammars(documentRoot, exe->documentName, exe->properties);
- pm->PreloadPrompts(documentRoot, exe->properties, translator);
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(documentRoot, e);
- throw VXIException::Exit(NULL);
- }
- try {
- vxistring temp;
- exe->gm.LoadGrammars(applicationRoot, temp, exe->properties);
- // don't want to preload prompts if the root application already loaded
- if( reinitApplication )
- pm->PreloadPrompts(applicationRoot, exe->properties, translator);
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(applicationRoot, e);
- throw VXIException::Exit(NULL);
- }
- try {
- vxistring temp;
- exe->gm.LoadGrammars(exe->platDefaults, temp, exe->properties, true);
- pm->PreloadPrompts(exe->platDefaults, exe->properties, translator);
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(exe->platDefaults, e);
- throw VXIException::Exit(NULL);
- }
- // (4) Clear existing ECMA script scopes.
- Scripter & script = exe->script;
- if (script.CurrentScope(SCOPE_Anonymous)) script.PopScope();
- if (script.CurrentScope(SCOPE_Local)) script.PopScope();
- if (script.CurrentScope(SCOPE_Dialog)) script.PopScope();
- if (script.CurrentScope(SCOPE_Document)) script.PopScope();
- if (script.CurrentScope(SCOPE_Application) && reinitApplication)
- script.PopScope();
- if (reinitApplication && !script.CurrentScope(SCOPE_Defaults)) {
- log->LogError(999, SimpleLogger::MESSAGE,
- L"ECMAScript scope inconsistent");
- throw VXIException::Fatal();
- }
- // (5) And set the new ones.
- try {
- if (script.CurrentScope(SCOPE_Defaults)) {
- script.PushScope(SCOPE_Application);
- exe->script.MakeVar(L"lastresult$", L"undefined");
- if (!exe->applicationURI.empty()) {
- ProcessRootScripts(applicationRoot);
- }
- }
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(applicationRoot, e);
- throw VXIException::Exit(NULL);
- }
- try {
- script.PushScope(SCOPE_Document, leafToRoot || exe->applicationURI.empty());
- if (!leafToRoot) {
- ProcessRootScripts(documentRoot);
- }
- }
- catch (const VXIException::InterpreterEvent & e) {
- DoEvent(documentRoot, e);
- throw VXIException::Exit(NULL);
- }
- }
- void VXI::PerformTransition(const VXMLElement & elem,
- const vxistring & rawURI,
- VXIMap * rawSubmitData,
- bool isSubdialog,
- bool isSubmitElement)
- {
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::PerformTransition(" << rawURI << L")";
- log->EndDiagnostic();
- }
- VXIMapHolder submitData(rawSubmitData);
- // (1) Determine fetch properties for document load.
- // (1.1) Create empty fetch object. Now we need to fill this in.
- VXIMapHolder fetchobj;
- if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
-
- // (1.2) Set URIs for the Jump.
- vxistring uri((rawURI.empty() ? L"" : rawURI));
- vxistring fragment;
- const VXIchar * tempStr = L"";
- // (1.2.1) Divide raw URI into uri + fragment.
- exe->properties.GetFetchobjURIs(elem, fetchobj, uri, fragment);
- // (1.2.2) Handle the degenerate case.
- if (uri.empty() && fragment.empty()) {
- log->StartDiagnostic(0) << L"VXI::PerformTransition - invalid URI, ""
- << (rawURI.empty() ? L"NULL" : rawURI.c_str()) << L""";
- log->EndDiagnostic();
- throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
- }
- // (1.2.3) In the fragment only case, just go to the indicated item.
- if (uri.empty()) {
- VXMLElement targetElement = FindDialog(elem, fragment);
- if (targetElement == 0) {
- log->StartDiagnostic(0) << L"VXI::PerformTransition - non-existent "
- L"dialog, "" << (rawURI.empty() ? L"NULL" : rawURI.c_str()) << L""";
- log->EndDiagnostic();
- throw VXIException::InterpreterEvent(EV_ERROR_BADDIALOG);
- }
- if (!isSubdialog)
- throw JumpDialog(targetElement);
- else {
- PropertyList subprop(exe->properties);
- subprop.PopPropertyLevel(FIELD_PROP);
- vxistring absoluteURI(L"");
- tempStr = exe->properties.GetProperty(PropertyList::AbsoluteURI);
- if (tempStr) absoluteURI = tempStr;
-
- throw JumpDoc(exe->platDefaults, exe->application, exe->applicationURI,
- exe->document, absoluteURI, targetElement, isSubdialog, isSubmitElement,
- subprop);
- }
- }
- // (1.3) Get remaining fetch properties. At this point, we now need
- // to assemble seperate fetch properties for fetchaudio, up
- // until now all the prep work is identical.
- VXIMapHolder fetchAudioFetchobj(NULL);
- fetchAudioFetchobj = fetchobj;
- if (fetchAudioFetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
- exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Document,fetchobj);
- exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Audio,
- fetchAudioFetchobj);
- if (submitData.GetValue() != NULL) {
- if (!exe->properties.GetFetchobjSubmitAttributes(elem, submitData,
- fetchobj))
- {
- // This should never occur.
- log->StartDiagnostic(0) << L"VXI::PerformTransition - couldn't set "
- L"the submit attributes.";
- log->EndDiagnostic();
- throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
- }
- submitData.Release(); // This map is now owned by fetchobj.
- }
- // (2) Load Document.
- // (2.1) Start fetch audio.
- vxistring fetchaudio;
- if (!elem.GetAttribute(ATTRIBUTE_FETCHAUDIO, fetchaudio))
- fetchaudio = toString(exe->properties.GetProperty(L"fetchaudio"));
- if (!fetchaudio.empty()) {
- // Get All properties for fetching audio
- VXIMapHolder fillerProp(VXIMapClone(fetchAudioFetchobj.GetValue()));
- exe->properties.GetProperties(fillerProp);
- pm->PlayAll(); // Finish playing already queued prompts
- pm->PlayFiller(fetchaudio, fillerProp);
- }
- // (2.2) Load document and verify that dialog exists.
- // (2.2.1) Load the VoiceXML document.
- VXMLDocument document;
- VXMLElement documentDialog;
- VXIMapHolder documentFetchProps;
- AttemptDocumentLoad(uri, fetchobj, document, documentFetchProps);
- // (2.2.2) If there was a fragment, does the named dialog exist?
- VXMLElement documentRoot = document.GetRoot();
- documentDialog = FindDialog(documentRoot, fragment);
- if (documentDialog == 0) {
- vxistring msg;
- if (fragment.empty())
- msg = L"no dialog element found in " + uri;
- else
- msg = L"named dialog " + fragment + L" not found in " + uri;
- log->StartDiagnostic(0) << L"VXI::PerformTransition - " << msg;
- log->EndDiagnostic();
- throw VXIException::InterpreterEvent(EV_ERROR_BADDIALOG, msg);
- }
-
- // (3) Get Document language & find associated defaults.
- // (3.1) Create a new property list containing the document properties.
- PropertyList newProperties(*log);
- // WARNING: The defaults document has multiple languages and a language-independent '*' group.
- // This should really read the '*' group and then overlay the set specific to the active language.
- // (3.2) Extract the language setting.
- const VXIchar * language = newProperties.GetProperty(PropertyList::Language);
- if (language == NULL) language = GENERIC_DEFAULTS;
- // (3.3) Find the language defaults.
- VXMLElement defaults;
- VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
- for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
- VXMLNode child = *it;
- // Look for a language node.
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
- vxistring id;
- elem.GetAttribute(ATTRIBUTE_ID, id);
- if (id == language || (id == GENERIC_DEFAULTS && defaults == 0))
- defaults = elem;
- }
- if (defaults != 0)
- newProperties.SetProperties(defaults, DEFAULTS_PROP, VXIMapHolder(NULL));
- // newProperties should now contain the properties from the platform defaults plus
- // the properties at document level.
- newProperties.SetProperties(documentRoot, DOC_PROP, documentFetchProps);
- // (3.4) Copy the document properties in and find the absolute URI.
- vxistring absoluteURI(L"");
- vxistring applicationURI(L"");
- tempStr = newProperties.GetProperty(PropertyList::AbsoluteURI, DOC_PROP);
- if (tempStr == NULL)
- absoluteURI = uri.empty() ? L"" : uri.c_str();
- else
- absoluteURI = tempStr;
- // (4) Load the application.
- VXMLDocument application;
- // (4.1) Get the application URI.
- vxistring appuri;
- documentRoot.GetAttribute(ATTRIBUTE_APPLICATION, appuri);
- if (!appuri.empty() ) {
- if ( (exe->applicationName.empty() || appuri != exe->applicationName)) {
- // set applcation name
- exe->applicationName = appuri;
- // (4.2) Initialize application fetch parameters.
- VXIMapHolder appFetchobj;
- if (appFetchobj.GetValue() == NULL)
- throw VXIException::OutOfMemory();
- vxistring appFragment;
- newProperties.GetFetchobjURIs(documentRoot, appFetchobj, appuri,
- appFragment);
- if (appuri.empty()) {
- log->LogError(214);
- throw VXIException::InterpreterEvent(EV_ERROR_APP_BADURI);
- }
- newProperties.GetFetchobjCacheAttrs(documentRoot, PropertyList::Document,
- appFetchobj);
- // (4.3) Load the application and its properties (we must then restore
- // document properties) also let document converter
- // know it's root application
- VXIMapClear(exe->appProperties.GetValue());
- AttemptDocumentLoad(appuri, appFetchobj, application, exe->appProperties,
- false, true);
- // (4.3.1) If an application root document specifies an application root
- // element error.semantic is thrown.
- VXMLElement documentRootCheck = application.GetRoot();
- vxistring appuriCheck;
- documentRootCheck.GetAttribute(ATTRIBUTE_APPLICATION, appuriCheck);
- if (!appuriCheck.empty())
- throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
- L"application root element exists in application root document");
-
- newProperties.SetProperties(application.GetRoot(), APP_PROP, exe->appProperties);
- // (4.4) Get the absolute URI for the application root.
- // transitioning to new application
- tempStr = newProperties.GetProperty(PropertyList::AbsoluteURI, APP_PROP);
- applicationURI = (tempStr == NULL ? L"" : tempStr);
- // (4.4.1) Generate the final event.
- throw JumpDoc(defaults, application, applicationURI,
- document, absoluteURI, documentDialog, isSubdialog,
- isSubmitElement, newProperties);
- }
- else {
- // (4.5) leaf-to-leaf transition with same root application, and since
- // root application has already been loaded, we re-use the previous loaded
- // root document and uri
- newProperties.SetProperties(exe->application.GetRoot(), APP_PROP, exe->appProperties);
- // (4.5.1) Generate the final event.
- throw JumpDoc(defaults, exe->application, exe->applicationURI,
- document, absoluteURI, documentDialog, isSubdialog,
- isSubmitElement, newProperties);
- }
- }
- else {
- // (5) Transition to another app, i.e: root app. uri is not the same or empty then
- // we're no longer the same application, remove the application name
- if (!exe->applicationName.empty())
- exe->applicationName = L"";
-
- // (5.1) Generate the final event.
- throw JumpDoc(defaults, application, applicationURI,
- document, absoluteURI, documentDialog, isSubdialog,
- isSubmitElement, newProperties);
- }
- }
- // Finds the named dialog in the document. If the name is empty, the first
- // item is returned.
- //
- VXMLElement VXI::FindDialog(const VXMLElement & start, const vxistring & name)
- {
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::FindDialog(" << name << L")";
- log->EndDiagnostic();
- }
- // (0) find the root node.
- VXMLElement doc;
- for (doc = start; doc != 0 && doc.GetName() != NODE_VXML;
- doc = doc.GetParent());
- if (doc == 0) return VXMLElement();
- // (1) Walk through all elements at this level and find match.
- for (VXMLNodeIterator it(doc); it; ++it) {
- VXMLNode child = *it;
- // (1.1) Only <form> & <menu> elements are considered.
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- VXMLElementType nodeName = elem.GetName();
- if (nodeName != NODE_FORM && nodeName != NODE_MENU) continue;
- // (1.2) If no dialog was specified, return the first one.
- if (name.empty()) return elem;
- // (1.3) Otherwise, look for an exact match.
- vxistring id;
- if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, id)) continue;
- if (name == id)
- return elem;
- }
- // (2) User attempted to GOTO to non-existant dialog or have an empty doc!
- log->LogDiagnostic(2, L"VXI::FindDialog - no match found.");
- return VXMLElement();
- }
- //#############################################################################
- // Document Loop
- //#############################################################################
- // return success/failure (can't throw error here as caller needs
- // a chance to clean up
- // Also initialize new context (session scope)
- bool VXI::PushExecutionContext(const vxistring & sessionScript)
- {
- log->LogDiagnostic(2, L"VXI::PushExecutionContext()");
- if (log->IsLogging(4)) {
- log->StartDiagnostic(4) << L"VXI::PushExecutionContext - stackDepth: "
- << stackDepth << L", max: " << DEFAULT_MAX_EXE_STACK_DEPTH;
- log->EndDiagnostic();
- }
- // (1) Catch recursive <subdialog> case...
- if (stackDepth >= DEFAULT_MAX_EXE_STACK_DEPTH) {
- log->LogError(211);
- return false;
- }
- // (2) Create new execution context.
- ExecutionContext * ep = new ExecutionContext(rec, jsi, *log, exe);
- if (ep == NULL) throw VXIException::OutOfMemory();
- exe = ep;
- ++stackDepth;
- // (3) Init new context from channel (i.e. set up 'session' scope)
- exe->script.PushScope(SCOPE_Session);
- if (!sessionScript.empty()) exe->script.EvalScript(sessionScript);
- exe->script.SetVarReadOnly(SCOPE_Session);
- log->LogDiagnostic(2, L"VXI::PushExecutionContext - session variables "
- L"initialized");
- // (4) Init new context defaults (i.e. set up platform defaults)
- // (4.1) Find generic language properties.
- VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
- for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
- vxistring id;
- elem.GetAttribute(ATTRIBUTE_ID, id);
- if (id != GENERIC_DEFAULTS) continue;
- exe->platDefaults = elem;
- break;
- }
- if (exe->platDefaults == 0) {
- log->LogError(223);
- PopExecutionContext();
- return false;
- }
- // (4.2) Install defaults. We only need to worry about the properties (for
- // document load) and the ECMA variables and scripts (for catch handlers on
- // load failure). The grammars & prompts may be safely ignored.
- exe->properties.SetProperties(exe->platDefaults, DEFAULTS_PROP,
- VXIMapHolder());
- exe->script.PushScope(SCOPE_Defaults);
- ProcessRootScripts(exe->platDefaults);
- log->LogDiagnostic(2, L"VXI::PushExecutionContext - platform defaults "
- L"initialized");
- return true;
- }
- void VXI::PopExecutionContext()
- {
- if (log->IsLogging(4)) {
- log->StartDiagnostic(4) << L"VXI::PopExecutionContext - current stackDepth: "
- << stackDepth << L", max: " << DEFAULT_MAX_EXE_STACK_DEPTH;
- log->EndDiagnostic();
- }
- if (exe == NULL) return;
- ExecutionContext * current = exe;
- exe = current->next;
- --stackDepth;
- delete current;
- }
- void VXI::AttemptDocumentLoad(const vxistring & uri,
- const VXIMapHolder & uriProperties,
- VXMLDocument & doc,
- VXIMapHolder & docProperties,
- bool isDefaults,
- bool isRootApp)
- {
- // Reset current uri
- uriCurrent = uri;
-
- // (1) Create map to store document properties.
- if (docProperties.GetValue() == NULL)
- throw VXIException::OutOfMemory();
- // (2) Fetch the document
- VXIbyte *cbuffer = NULL;
- VXIulong bufferSize = 0;
- int result = parser->FetchDocument(uri.c_str(), uriProperties, inet, cache,
- *log, doc, docProperties,
- isDefaults, isRootApp,
- &cbuffer, &bufferSize);
-
- // If we didn't have a parse error, dump the VoiceXML document if
- // configured to do so, otherwise free the content buffer
- if ((result != 4) && (cbuffer)) {
- VXIString *key = NULL, *value = NULL;
- if ((log->IsLogging(SimpleLogger::DOC_DUMPS)) &&
- (log->LogContent(VXI_MIME_XML, cbuffer, bufferSize, &key, &value))) {
- vxistring diagUri(uri);
- const VXIValue * val = VXIMapGetProperty(docProperties.GetValue(),
- INET_INFO_ABSOLUTE_NAME);
- if (val != NULL && VXIValueGetType(val) == VALUE_STRING)
- diagUri = VXIStringCStr(reinterpret_cast<const VXIString *>(val));
- vxistring temp(L"URL");
- temp += L": ";
- temp += diagUri.c_str();
- temp += L", ";
- temp += VXIStringCStr(key);
- temp += L": ";
- temp += VXIStringCStr(value);
- log->LogDiagnostic(SimpleLogger::DOC_DUMPS, temp.c_str());
- }
- delete [] cbuffer;
- cbuffer = NULL;
- if (key) VXIStringDestroy(&key);
- if (value) VXIStringDestroy(&value);
- }
-
- // (3) Handle error conditions.
- switch (result) {
- case -1: // Out of memory
- throw VXIException::OutOfMemory();
- case 0: // Success
- break;
- case 1: // Invalid parameter
- case 2: // Unable to open URI
- log->LogError(203);
- // now look at the http status code to throw the
- // compiliant error.badfetch.http.<response code>
- if (docProperties.GetValue()) {
- // retrieve response code
- VXIint iv = 0;
- const VXIValue *v =
- VXIMapGetProperty(docProperties.GetValue(), INET_INFO_HTTP_STATUS);
- if( v && VXIValueGetType(v) == VALUE_INTEGER &&
- ((iv = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(v))) > 0)
- )
- {
- std::basic_stringstream<VXIchar> respStream;
- respStream << EV_ERROR_BADFETCH << L".http.";
- respStream << iv;
-
- throw VXIException::InterpreterEvent(respStream.str().c_str(), uri);
- }
- }
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- case 3: // Unable to read from URI
- log->LogError(204);
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- case 4: // Unable to parse contents of URI
- {
- VXIString *key = NULL, *value = NULL;
- if ((cbuffer) &&
- ((log->IsLogging(SimpleLogger::DOC_PARSE_ERROR_DUMPS)) ||
- (log->IsLogging(SimpleLogger::DOC_DUMPS))) &&
- (log->LogContent(VXI_MIME_XML, cbuffer, bufferSize, &key, &value)))
- log->LogError(205, VXIStringCStr(key), VXIStringCStr(value));
- else
- log->LogError(205);
- if (cbuffer) delete [] cbuffer;
- if (key) VXIStringDestroy(&key);
- if (value) VXIStringDestroy(&value);
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- }
- default:
- log->LogError(206);
- throw VXIException::Fatal();
- }
- if (doc.GetRoot() == 0) {
- log->LogError(999, SimpleLogger::MESSAGE,
- L"able to fetch initial document but node empty");
- throw VXIException::Fatal();
- }
- }
- void VXI::ProcessRootScripts(VXMLElement& doc)
- {
- if (doc == 0) return;
- log->LogDiagnostic(2, L"VXI::ProcessRootScripts()");
- // Do <var> <script> and <meta> <property> elements
- for (VXMLNodeIterator it(doc); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- VXMLElementType nodeName = elem.GetName();
- if (nodeName == NODE_VAR)
- var_element(elem);
- else if (nodeName == NODE_META)
- meta_element(elem);
- else if (nodeName == NODE_SCRIPT)
- script_element(elem);
- else if (nodeName == NODE_DATA)
- data_element(elem);
- }
- log->LogDiagnostic(2, L"VXI::ProcessRootScripts - done");
- }
- //#############################################################################
- // Dialog Loop
- //#############################################################################
- // There are two cases in which this routine may be entered.
- // A. After a new document is loaded (lastItem == 0)
- // B. After a return from <subdialog>
- // and three ways in which the loop may be re-entered.
- // 1. Jump to new dialog.
- // 2. Jump to new form item in the existing dialog.
- // 3. Jump to new dialog after recognizing a document scope grammar.
- //
- // The ECMA script scopes are reset accordingly:
- // anonymous: A B 1 2 3
- // local: A 1 2 3
- // dialog: A 1 3
- void VXI::RunInnerLoop()
- {
- log->LogDiagnostic(2, L"VXI::RunInnerLoop()");
- if (exe->currentDialog == 0) {
- log->LogError(999, SimpleLogger::MESSAGE, L"no current active document");
- return;
- }
- exe->currentFormItem = exe->lastItem; // next item to process; <goto> etc.
- exe->lastItem = VXMLElement(); // reset after <subdialog>
- bool newDialog = (exe->currentFormItem == 0);
- exe->playingPrompts = true; // flag for prompting after events
- // Initialize flags
- bool unprocessedAnswer = false;
- // Run the dialog loop
- while (1) {
- if (stopRequested) throw VXIException::StopRequest();
- try {
- try {
- // (1) Adjust scope if necessary
- if (exe->script.CurrentScope(SCOPE_Anonymous)) exe->script.PopScope();
- if (exe->script.CurrentScope(SCOPE_Local)) exe->script.PopScope();
- // (2) Initialize dialog (if necessary)
- if (newDialog) {
- newDialog = false;
- exe->playingPrompts = true;
- // (2.1) Reset ECMA script scope.
- if (exe->script.CurrentScope(SCOPE_Dialog)) exe->script.PopScope();
- exe->script.PushScope(SCOPE_Dialog);
- // (2.2) Do 'initialization phase' from FIA.
- VXIMapHolder params(sdParams);
- sdParams = NULL;
- FormInit(exe->currentDialog, params);
- // (2.3) Do 'select phase' from FIA if the item is not already known
- if (exe->currentFormItem == 0) {
- DoInnerJump(exe->currentDialog, L"");
- break;
- }
- }
- // (3) The loop cases.
- // (3.1) Re-entering loop with an unprocessed recognition result.
- if (unprocessedAnswer == true) {
- unprocessedAnswer = false;
- HandleRemoteMatch(exe->currentDialog, exe->currentFormItem);
- }
- // (3.2) Re-entering loop after returning from a <subdialog>.
- else if (sdResult != NULL) {
- VXIMapHolder temp(sdResult);
- sdResult = NULL;
- ProcessReturn(exe->currentDialog, exe->currentFormItem, temp);
- }
- else if (sdEvent != NULL) {
- // The sdEvent is deallocated in the catch (below).
- throw VXIException::InterpreterEvent(*sdEvent);
- }
- // (3.3) Each time we enter collect phase, we get fresh local scope.
- // All filled and catched triggered form here will execute in this
- // scope. The final local scope is popped when we leave.
- else {
- if (exe->script.CurrentScope(SCOPE_Local)) exe->script.PopScope();
- exe->script.PushScope(SCOPE_Local);
- mutex.Lock();
- if (haveExternalEvents) {
- vxistring event, message;
- if (externalEvents.size() > 0) {
- event = externalEvents.front();
- externalEvents.pop_front();
- message = externalMessages.front();
- externalMessages.pop_front();
- }
- if (externalEvents.empty()) haveExternalEvents = false;
- mutex.Unlock();
- throw VXIException::InterpreterEvent(event, message,
- exe->currentDialog);
- }
- mutex.Unlock();
-
- // Do the 'collect phase & process phase' from the FIA.
- CollectPhase(exe->currentDialog, exe->currentFormItem);
- }
- }
- // Handles document events.
- catch (const VXIException::InterpreterEvent & e) {
- // Cleanup sdEvent (if necessary).
- if (sdEvent != NULL) {
- delete sdEvent;
- sdEvent = NULL;
- }
-
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::RunInnerLoop - got exception: "
- << e.GetValue();
- log->EndDiagnostic();
- }
- if (exe->currentFormItem != 0)
- DoEvent(exe->currentFormItem, e);
- else
- DoEvent(exe->currentDialog, e);
- }
- DoInnerJump(exe->currentDialog, L"");
- break;
- }
- // Handles a goto a dialog in the same document.
- catch (JumpDialog & e) {
- exe->currentDialog = e.dialog;
- exe->currentFormItem = VXMLElement();
- newDialog = true;
- }
- // Handles <goto nextitem="...">.
- catch (const JumpItem & e) {
- // sets the next form item to execute.
- exe->currentFormItem = e.item;
- }
- // Something down the call stack received a recognition. it
- // will be handled on the next iteration of the loop.
- catch (AnswerInformation & e) {
- if (exe->currentDialog != e.dialog) {
- exe->currentDialog = e.dialog;
- newDialog = true;
- }
- exe->currentFormItem = e.element;
- unprocessedAnswer = true;
- }
- } // while (1)
- log->LogDiagnostic(2, L"VXI::RunInnerLoop - done");
- }
- void VXI::ProcessReturn(const VXMLElement& form, const VXMLElement & item,
- VXIMapHolder & result)
- {
- log->LogDiagnostic(2, L"VXI::ProcessReturn()");
- vxistring filled;
- item.GetAttribute(ATTRIBUTE__ITEMNAME, filled);
- exe->script.SetValue(filled, reinterpret_cast<VXIValue*>(result.GetValue()));
- ProcessFilledElements(filled, form);
- }
- // Perform initialization associated with property tags and form level
- // variables. Reset the event and prompts counts.
- //
- void VXI::FormInit(const VXMLElement & form, VXIMapHolder & params)
- {
- log->LogDiagnostic(2, L"VXI::FormInit()");
- // (1) Set the form properties.
- exe->properties.SetProperties(form, DIALOG_PROP, VXIMapHolder());
- // (2) Clear the prompt & event counts when the form is entered.
- exe->promptcounts.Clear();
- exe->eventcounts.Clear();
- exe->formitems.clear();
- vxistring itemname;
- form.GetAttribute(ATTRIBUTE__ITEMNAME, itemname);
- // (3) Walk through the form nodes. Set up variables as necessary.
- for (VXMLNodeIterator it(form); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
-
- // (3.1) Handle <var> and <script> elements.
- try {
- VXMLElementType nodeName = elem.GetName();
- if (nodeName == NODE_VAR) {
- if (params.GetValue() != NULL) {
- // (3.1.1) Set matching variables to the value in the param list.
- // Each located parameter gets removed from the map.
- vxistring name;
- elem.GetAttribute(ATTRIBUTE_NAME, name);
- if (!name.empty()) {
- const VXIValue * value = VXIMapGetProperty(params.GetValue(),
- name.c_str());
- if (value != NULL) {
- exe->script.MakeVar(name, value);
- VXIMapDeleteProperty(params.GetValue(), name.c_str());
- continue;
- }
- }
- }
- // (3.1.2) Otherwise, follow the normal proceedure.
- var_element(elem);
- continue;
- }
- else if (nodeName == NODE_SCRIPT)
- script_element(elem);
- else if (nodeName == NODE_DATA)
- data_element(elem);
- // (3.2) Ignore anything which is not a form item.
- if (!IsFormItemNode(elem)) continue;
- // (3.3) Initialize variables for each form item.
- vxistring name;
- vxistring expr;
- elem.GetAttribute(ATTRIBUTE__ITEMNAME, name);
- // (3.4) Throw on duplicate form item names
- if (exe->script.IsVarDeclared(name)) {
- vxistring msg = L"Duplicate form item name: " + name;
- log->LogError(217, SimpleLogger::MESSAGE, name.c_str());
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, msg);
- }
- elem.GetAttribute(ATTRIBUTE_EXPR, expr);
- exe->script.MakeVar(name, expr);
- nodeName = elem.GetName();
- if (nodeName == NODE_FIELD || nodeName == NODE_RECORD ||
- nodeName == NODE_TRANSFER || nodeName == NODE_INITIAL)
- exe->script.EvalScript(vxistring(L"var ") + name + L"$ = new Object();");
- exe->formitems.push_back(name);
- }
- catch( VXIException::InterpreterEvent &e ) {
- DoEvent(form, e);
- if (exe->script.CurrentScope(SCOPE_Anonymous))
- exe->script.PopScope();
- }
- }
- // (4) Did all incoming parameters get used?
- if (params.GetValue() != NULL && VXIMapNumProperties(params.GetValue()) != 0) {
- vxistring unused;
- const VXIchar *key;
- const VXIValue *value;
- VXIMapIterator *vi = VXIMapGetFirstProperty(params.GetValue(), &key,
- &value);
- while (vi) {
- if (unused.empty()) unused = vxistring(L"Unused param(s): " ) + key;
- else unused += L", " + vxistring(key);
- if (VXIMapGetNextProperty(vi, &key, &value) != VXIvalue_RESULT_SUCCESS){
- VXIMapIteratorDestroy(&vi);
- vi = NULL;
- }
- }
- log->LogError(218, SimpleLogger::MESSAGE, unused.c_str());
- throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
- }
- log->LogDiagnostic(2, L"VXI::FormInit - Done");
- }
- // Returns true if the element is a form item.
- //
- // This is the list from section 6.2 of the VXML 1.0 specification with one
- // addition - <menu>.
- //
- bool VXI::IsFormItemNode(const VXMLElement & doc)
- {
- VXMLElementType nodeName = doc.GetName();
- if (nodeName == NODE_FIELD || nodeName == NODE_INITIAL ||
- nodeName == NODE_RECORD || nodeName == NODE_TRANSFER ||
- nodeName == NODE_OBJECT || nodeName == NODE_SUBDIALOG ||
- nodeName == NODE_MENU || nodeName == NODE_BLOCK )
- return true;
- return false;
- }
- bool VXI::IsInputItemNode(const VXMLElement & doc)
- {
- VXMLElementType nodeName = doc.GetName();
- if (nodeName == NODE_FIELD || nodeName == NODE_RECORD ||
- nodeName == NODE_TRANSFER || nodeName == NODE_OBJECT ||
- nodeName == NODE_SUBDIALOG || nodeName == NODE_MENU)
- return true;
- return false;
- }
- // Finds the named form item within the dialog. If the name is empty, the
- // first non-filled item is returned.
- //
- void VXI::DoInnerJump(const VXMLElement & elem, const vxistring & item)
- {
- if (elem == 0) return;
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::DoInnerJump(" << item << L")";
- log->EndDiagnostic();
- }
- // find form.
- VXMLElement current = elem;
- while (1) {
- VXMLElementType nodeName = current.GetName();
- if (nodeName == NODE_MENU)
- throw JumpItem(current); // Menu is a special case.
- if (nodeName == NODE_FORM)
- break;
- const VXMLElement & parent = current.GetParent();
- if (parent == 0) {
- log->LogError(225, SimpleLogger::MESSAGE, item.c_str());
- throw VXIException::Fatal();
- }
- current = parent;
- };
- // (2) If the next item is specified (such as from a previous <goto>, look
- // for an exact match.
- if (!item.empty()) {
- for (VXMLNodeIterator it(current); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- if (!IsFormItemNode(elem)) continue;
- vxistring name;
- if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, name)) continue;
- if (item == name) throw JumpItem(elem);
- }
- }
- // (3) Otherwise, find the first non-filled item with a valid condition.
- else {
- for (VXMLNodeIterator it(current); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- if (!IsFormItemNode(elem)) continue;
- // Must use itemname here, as could be implicit name
- vxistring itemname;
- if (!elem.GetAttribute(ATTRIBUTE__ITEMNAME, itemname)) {
- log->LogError(999, SimpleLogger::MESSAGE,
- L"unnamed item found on local jump");
- throw VXIException::Fatal();
- }
- if (exe->script.IsVarDefined(itemname)) continue;
- // OK if var is undefined, check condition
- vxistring cond;
- elem.GetAttribute(ATTRIBUTE_COND, cond);
- if (cond.empty() || exe->script.TestCondition(cond))
- throw JumpItem(elem);
- }
- }
- // log->LogDiagnostic(2, L"VXI::DoInnerJump - no match found.");
- }
- //#############################################################################
- // Utility functions
- //#############################################################################
- // do_event() - top level call into event handler; deals with
- // event counts and defaults.
- //
- void VXI::DoEvent(const VXMLElement & item,
- const VXIException::InterpreterEvent & e)
- {
- // (0) Initial logging
- if (item == 0) {
- log->LogDiagnostic(0, L"VXI::DoEvent - invalid argument, ignoring event");
- return;
- }
- else if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::DoEvent(" << e.GetValue() << L")";
- log->EndDiagnostic();
- }
- // (1) Disable queuing of prompts outside of event handler.
- exe->playingPrompts = false;
- VXIint32 numRethrows = 0;
- VXIException::InterpreterEvent event = e;
- do {
- try {
- // (2) Increments counts associated with this event.
- exe->eventcounts.Increment(item, event.GetValue());
- // (3) Process the current event.
- exe->eventSource = item;
- bool handled = do_event(item, event);
- exe->eventSource = VXMLElement();
- // (4) If the event was handled (a suitable <catch> was found),
- // this will complete event handling, and return to the FIA.
- if (handled) {
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::DoEvent - event processed.";
- log->EndDiagnostic();
- }
- return;
- }
- // (5) No one willing to handle this event. Exit.
- vxistring exitmessage(L"Unhandled exception: ");
- exitmessage += event.GetValue();
- VXIString * val = VXIStringCreate(exitmessage.c_str());
- if (val == NULL) throw VXIException::OutOfMemory();
- throw VXIException::Exit(reinterpret_cast<VXIValue*>(val));
- }
- // (5) The catch had a <throw> element inside. Must process the new event.
- catch (const VXIException::InterpreterEvent & e) {
- event = e;
- }
- } while (++numRethrows < DEFAULT_MAX_EVENT_RETHROWS);
- // (7) Probable loop - catch X throws X? Quit handling after a while.
- log->LogError(216);
- vxistring exitmessage(L"Unhandled exception (suspected infinite loop)");
- VXIString * val = VXIStringCreate(exitmessage.c_str());
- if (val == NULL) throw VXIException::OutOfMemory();
- throw VXIException::Exit(reinterpret_cast<VXIValue*>(val));
- }
- bool VXI::do_event(const VXMLElement & item,
- const VXIException::InterpreterEvent & e)
- {
- vxistring event = e.GetValue();
- if (event.empty()) return false;
- // (1) Define the variables for the best match.
- int bestCount = 0;
- VXMLElement bestMatch;
- DocumentLevel stage = DOCUMENT;
- bool done = false;
- // Start from current item in document.
- VXMLElement currentNode = item;
- // (2) Get the count for the current event.
- int eventCount = exe->eventcounts.GetCount(item, event);
- if (eventCount == 0)
- return false; // this shouldn't happen if increment was called
- if ( event[event.length() - 1] != '.' )
- event += L".";
- do {
- // (3) Walk through all nodes at this level looking for catch elements.
- for (VXMLNodeIterator it(currentNode); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
- // (3.1) Can this node catch events?
- VXMLElementType nodeName = elem.GetName();
- if (nodeName != NODE_CATCH) continue;
- // (3.2) Evaluate catch count;
- vxistring attr;
- int catchCount = 1;
- if (elem.GetAttribute(ATTRIBUTE_COUNT, attr)) {
- VXIchar * temp;
- catchCount = int(wcstol(attr.c_str(), &temp, 10));
- }
- if ( catchCount > eventCount ) continue;
- // (3.3) Evaluate its 'cond' attribute.
- elem.GetAttribute(ATTRIBUTE_COND, attr);
- if (!attr.empty() && !exe->script.TestCondition(attr)) continue;
- // (3.4) Get the catch's event list
- vxistring catchEvent;
- elem.GetAttribute(ATTRIBUTE_EVENT, catchEvent);
- // (3.5) Check if this handler can handle the thrown event
- bool canHandle = false;
- if ( catchEvent.empty() || catchEvent == L"." ) {
- canHandle = true;
- }
- else {
- // Is this handler either an exact match, or a
- // prefix match of the thrown event?
- TokenList catchList(catchEvent);
- TokenList::iterator i;
- for (i = catchList.begin(); i != catchList.end(); ++i) {
- catchEvent = *i;
- if ( catchEvent[catchEvent.length() - 1] != '.' )
- catchEvent += L".";
- if ( wcsncmp( catchEvent.c_str(), event.c_str(), catchEvent.length() ) == 0 ) {
- canHandle = true;
- break;
- }
- }
- }
- // (3.6) Keep the handler with the highest count less than or
- // equal the event count
- if ( canHandle && ( catchCount > bestCount ) ) {
- bestCount = catchCount;
- bestMatch = elem;
- }
- }
- // (4) Decide where to search next.
- const VXMLElement & parent = currentNode.GetParent();
- if (parent != 0)
- currentNode = parent;
- else {
- if (stage == DOCUMENT) {
- stage = APPLICATION;
- currentNode = exe->application.GetRoot();
- if (currentNode != 0) continue;
- // Otherwise, fall through to application level.
- }
- if (stage == APPLICATION) {
- stage = DEFAULTS;
- // We resort to this level _only_ if no match has been found.
- if (bestCount < 1) {
- vxistring language =
- toString(exe->properties.GetProperty(PropertyList::Language));
- // We clear the current node. It will be set either to the global
- // language (*) or to an exact match.
- currentNode = VXMLElement();
- VXMLElement defaultsRoot = domDefaultDoc.GetRoot();
- for (VXMLNodeIterator it(defaultsRoot); it; ++it) {
- VXMLNode child = *it;
- // Look for a language node.
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem
- = reinterpret_cast<const VXMLElement &>(child);
- if (elem.GetName() != DEFAULTS_LANGUAGE) continue;
- vxistring id;
- elem.GetAttribute(ATTRIBUTE_ID, id);
- if (id == language || (id == GENERIC_DEFAULTS && currentNode == 0))
- currentNode = elem;
- }
- if (currentNode != 0) continue;
- }
- }
- done = true;
- }
- } while (!done);
- if (bestCount == 0)
- return false;
- // Compliance Note:
- //
- // Because of the 'as-if-by-copy' semantic, we now execute the catch content,
- // including relative URIs, in the local scope. So nothing special needs to
- // be done.
- VXIMapHolder vars;
- VXIValue * temp = NULL;
- temp = reinterpret_cast<VXIValue *>(VXIStringCreate(e.GetValue().c_str()));
- if (temp == NULL) throw VXIException::OutOfMemory();
- VXIMapSetProperty(vars.GetValue(), L"_event", temp);
- if (e.GetMessage().empty())
- temp = reinterpret_cast<VXIValue *>(VXIPtrCreate(NULL));
- else
- temp =reinterpret_cast<VXIValue*>(VXIStringCreate(e.GetMessage().c_str()));
- if (temp == NULL) throw VXIException::OutOfMemory();
- VXIMapSetProperty(vars.GetValue(), L"_message", temp);
-
- execute_content(bestMatch, vars, e.GetActiveDialog());
- return true;
- }
- // Top level call into executable content section.
- // Called from <block>,<catch>, and <filled>
- //
- void VXI::execute_content(const VXMLElement& doc, const VXIMapHolder & vars,
- const VXMLElement& activeDialog)
- {
- log->LogDiagnostic(2, L"VXI::execute_content()");
- // (1) Due to the complex algorithm for throw element to address
- // "as-if-by-copy" semantics, the anonymous scope must be retained
- if (exe->script.CurrentScope(SCOPE_Anonymous) && doc.GetName() != NODE_CATCH) {
- // (1.1) Add a new scope and allow anonymous variables to be defined.
- exe->script.PopScope();
- exe->script.PushScope(SCOPE_Anonymous);
- }
-
- // (1.2) Add anonymous scope if not exist
- if( !exe->script.CurrentScope(SCOPE_Anonymous) )
- exe->script.PushScope(SCOPE_Anonymous);
-
- // (2) Set externally specified variables (if necessary).
- if (vars.GetValue() != NULL) {
- const VXIchar * key;
- const VXIValue * value;
- VXIMapIterator * i = VXIMapGetFirstProperty(vars.GetValue(), &key, &value);
- if (VXIValueGetType(value) == VALUE_PTR)
- exe->script.MakeVar(key, L""); // Set to ECMAScript undefined
- else
- exe->script.MakeVar(key, value);
- while (VXIMapGetNextProperty(i, &key, &value) == VXIvalue_RESULT_SUCCESS) {
- if (VXIValueGetType(value) == VALUE_PTR)
- exe->script.MakeVar(key, L""); // Set to ECMAScript undefined
- else
- exe->script.MakeVar(key, value);
- }
- VXIMapIteratorDestroy(&i);
- }
- // (3) Walk through the children and execute each node.
- for (VXMLNodeIterator it(doc); it; ++it) {
- VXMLNode child = *it;
- if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
- const VXMLElement & elem = reinterpret_cast<VXMLElement &>(child);
- executable_element(elem, activeDialog);
- }
- }
- // Executable element dispatch
- //
- void VXI::executable_element(const VXMLElement & elem, const VXMLElement & activeDialog)
- {
- if (elem == 0) {
- log->LogError(999, SimpleLogger::MESSAGE, L"empty executable element");
- return;
- }
- VXMLElementType nodeName = elem.GetName();
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::executable_element - " << nodeName;
- log->EndDiagnostic();
- }
- if (nodeName == NODE_VAR)
- var_element(elem);
- else if (nodeName == NODE_ASSIGN)
- assign_element(elem);
- else if (nodeName == NODE_CLEAR)
- clear_element(elem);
- else if (nodeName == NODE_DISCONNECT)
- disconnect_element(elem);
- else if (nodeName == NODE_EXIT)
- exit_element(elem);
- else if (nodeName == NODE_GOTO)
- goto_element(elem);
- else if (nodeName == NODE_IF)
- if_element(elem);
- else if (nodeName == NODE_LOG)
- log_element(elem);
- else if (nodeName == NODE_PROMPT)
- executable_prompt(elem);
- else if (nodeName == NODE_REPROMPT)
- reprompt_element(elem, activeDialog);
- else if (nodeName == NODE_RETURN)
- return_element(elem);
- else if (nodeName == NODE_SCRIPT)
- script_element(elem);
- else if (nodeName == NODE_SUBMIT)
- submit_element(elem);
- else if (nodeName == NODE_THROW)
- throw_element(elem);
- else if (nodeName == NODE_DATA)
- data_element(elem);
- else
- log->LogError(999, SimpleLogger::MESSAGE,L"unexpected executable element");
- }
- /*
- * Process <var> elements in current interp context.
- *
- * This differs from assign in that it makes new var in current scope
- * assign follows scope chain lookup for var name (and throws
- * error if fails)
- *
- * This is also used for initialiation of guard vars in field items
- *
- * <var> processing is compliated by the need to check for <param> values
- * from subdialog calls.
- */
- void VXI::var_element(const VXMLElement & doc)
- {
- vxistring name;
- vxistring expr;
- doc.GetAttribute(ATTRIBUTE_NAME, name);
- doc.GetAttribute(ATTRIBUTE_EXPR, expr);
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::var_element(name="" << name
- << L"" expr = "" << expr << L"")";
- log->EndDiagnostic();
- }
- if (name.empty()) return;
- // VXML 2.0 Spec (5.3.1)
- if( name.find( L"." ) != vxistring::npos )
- {
- log->LogError(219, SimpleLogger::URI, name.c_str());
- throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
- L"variables cannot be declared with scope prefix "
- L"and ECMAScript script variable names must not include a dot");
- }
- exe->script.MakeVar(name, expr);
- }
-
- // Process <assign> elements in current interpreter context
- //
- void VXI::assign_element(const VXMLElement & doc)
- {
- vxistring name;
- doc.GetAttribute(ATTRIBUTE_NAME, name);
- if (name.empty()) return;
- vxistring expr;
- doc.GetAttribute(ATTRIBUTE_EXPR, expr);
- if (log->IsLogging(2)) {
- log->StartDiagnostic(2) << L"VXI::assign_element(name="" << name
- << L"" expr = "" << expr << L""";
- log->EndDiagnostic();
- }
- exe->script.SetVar(name, expr);
- }
- // Handler for meta elements. Do nothing for now.
- //
- void VXI::meta_element(const VXMLElement & doc)
- {
- // vxistring & name = doc.GetAttribute("name");
- // vxistring & name = doc.GetAttribute("content");
- // vxistring & name = doc.GetAttribute("http-equiv");
- }
- // Handler for clear elements. This may resets all form items or a user
- // specified subset.
- //
- void VXI::clear_element(const VXMLElement & doc)
- {
- log->LogDiagnostic(2, L"VXI::clear_element()");
- // (1) Get the namelist.
- vxistring namelist;
- doc.GetAttribute(ATTRIBUTE_NAMELIST, namelist);
- // (2) Handle the easy case: empty namelist --> clear all
- if (namelist.empty()) {
- // (2.1) Clear prompt and event counts.
- exe->promptcounts.Clear();
- exe->eventcounts.Clear(false);
- // (2.2) The list of form items resides in the slot map.
- TokenList::iterator i;
- TokenList & formitems = exe->formitems;
- for (i = formitems.begin(); i != formitems.end(); ++i)
- exe->script.ClearVar(*i);
- return;
- }
- // (3) Handle case where user specifies form items.
- TokenList names(namelist);
- for (TokenList::const_iterator i = names.begin(); i != names.end(); ++i) {
- // (3.1) Check that the name is a real form item. A linear search should
- // be sufficently fast.
- TokenList::iterator j;
- TokenList & formitems = exe->formitems;
- for (j = formitems.begin(); j != formitems.end(); ++j)
- if (*i == *j) break;
- exe->script.ClearVar(*i);
- if (j != formitems.end()) {
- // (3.2) Clear the associated counters for form items.
- exe->promptcounts.Clear(*i);
- exe->eventcounts.Clear(*i);
- }
- }
- }
- void VXI::data_element(const VXMLElement & elem)
- {
- log->LogDiagnostic(2, L"VXI::data_element()");
- // (1) Get fetch properties
- // (1.1) Get URI
- vxistring uri;
- elem.GetAttribute(ATTRIBUTE_SRC, uri);
- if (uri.empty()) {
- elem.GetAttribute(ATTRIBUTE_SRCEXPR, uri);
- if (!uri.empty())
- exe->script.EvalScriptToString(uri, uri);
- }
- // (1.2) Get Submit-specific properties.
- VXIMapHolder submitData;
- if (submitData.GetValue() == NULL) throw VXIException::OutOfMemory();
- vxistring att;
- elem.GetAttribute(ATTRIBUTE_NAMELIST, att);
- if (!att.empty()) {
- TokenList names(att);
- TokenList::const_iterator i;
- for (i = names.begin(); i != names.end(); ++i) {
- VXIValue * val = exe->script.GetValue(*i);
- if (val != NULL)
- VXIMapSetProperty(submitData.GetValue(), (*i).c_str(), val);
- }
- }
- // (1.3) Get fetch properties.
- VXIMapHolder fetchobj;
- if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
- exe->properties.GetFetchobjBase(fetchobj);
- VXIMapHolder fetchAudioFetchobj(NULL);
- fetchAudioFetchobj = fetchobj;
- if (fetchAudioFetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();
- exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Data, fetchobj);
- exe->properties.GetFetchobjCacheAttrs(elem, PropertyList::Audio, fetchAudioFetchobj);
- // (1.4) Add namelist values
- if (submitData.GetValue() != NULL) {
- if (!exe->properties.GetFetchobjSubmitAttributes(elem, submitData,
- fetchobj))
- {
- // This should never occur.
- log->StartDiagnostic(0) << L"VXI::data_element - couldn't set "
- L"the submit attributes.";
- log->EndDiagnostic();
- throw VXIException::InterpreterEvent(EV_ERROR_BADURI);
- }
- submitData.Release(); // This map is now owned by fetchobj.
- }
- // (2) Start fetch audio.
- vxistring fetchaudio;
- if (!elem.GetAttribute(ATTRIBUTE_FETCHAUDIO, fetchaudio))
- fetchaudio = toString(exe->properties.GetProperty(L"fetchaudio"));
- if (!fetchaudio.empty()) {
- // Get All properties for fetching audio
- VXIMapHolder fillerProp(VXIMapClone(fetchAudioFetchobj.GetValue()));
- exe->properties.GetProperties(fillerProp);
- pm->PlayAll(); // Finish playing already queued prompts
- pm->PlayFiller(fetchaudio, fillerProp);
- }
- // (3) Get the data
- VXIMapHolder fetchStatus;
- DOMDocument *doc = 0;
- int result = parser->FetchXML(
- uri.c_str(), fetchobj, fetchStatus,
- inet, *log, &doc );
- switch (result) {
- case -1: // Out of memory
- throw VXIException::OutOfMemory();
- case 0: // Success
- break;
- case 1: // Invalid parameter
- case 2: // Unable to open URI
- log->LogError(203);
- // now look at the http status code to throw the
- // compiliant error.badfetch.http.<response code>
- if (fetchStatus.GetValue()) {
- // retrieve response code
- VXIint iv = 0;
- const VXIValue *v =
- VXIMapGetProperty(fetchStatus.GetValue(), INET_INFO_HTTP_STATUS);
- if( v && VXIValueGetType(v) == VALUE_INTEGER &&
- ((iv = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(v))) > 0)
- )
- {
- std::basic_stringstream<VXIchar> respStream;
- respStream << EV_ERROR_BADFETCH << L".http.";
- respStream << iv;
-
- throw VXIException::InterpreterEvent(respStream.str().c_str(), uri);
- }
- }
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- case 3: // Unable to read from URI
- log->LogError(204);
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- case 4: // Unable to parse contents of URI
- {
- log->LogError(205);
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- }
- default:
- log->LogError(206);
- throw VXIException::Fatal();
- }
- // (4) Validate per VXML sandboxing
- const VXIValue * absurl =
- VXIMapGetProperty(fetchStatus.GetValue(), INET_INFO_ABSOLUTE_NAME);
- if (absurl == NULL || VXIValueGetType(absurl) != VALUE_STRING) {
- throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH, uri);
- }
- // (5) Check access
- AccessControl ac(defAccessControl, doc);
- if (!ac.canAccess(VXIStringCStr(reinterpret_cast<const VXIString*>(absurl))))
- throw VXIException::InterpreterEvent(EV_ERROR_NOAUTHORIZ);
- // (6) Assign var
- vxistring varname;
- elem.GetAttribute(ATTRIBUTE_NAME, varname);
- if (!varname.empty()) {
- exe->script.MakeVar(varname, doc);
- }
- }
- // This implementation returns the values as VXIObjects.
- //
- void VXI::exit_element(const VXMLElement & doc)
- {
- log->LogDiagnostic(2, L"VXI::exit_element()");
- VXIMapHolder exprMap(NULL);
- vxistring namelist;
- doc.GetAttribute(ATTRIBUTE_NAMELIST, namelist);
- if (!namelist.empty()) {
- exprMap.Acquire(VXIMapCreate());
- if (exprMap.GetValue() == NULL) throw VXIException::OutOfMemory();
- TokenList names(namelist);
- TokenList::const_iterator i;
- for (i = names.begin(); i != names.end(); ++i) {
- if (!exe->script.IsVarDeclared(*i))
- throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
- VXIValue * val = exe->script.GetValue(*i);
- if (val != NULL)
- VXIMapSetProperty(exprMap.GetValue(), (*i).c_str(), val);