Example #1
0
inline void AddParamValue(VXIMapHolder & m, const vxistring & name,
                          VXIflt32 value)
{
  if (m.GetValue() == NULL) return;
  VXIFloat * val = VXIFloatCreate(value);
  if (val == NULL) throw VXIException::OutOfMemory();
  VXIMapSetProperty(m.GetValue(), name.c_str(),
                    reinterpret_cast<VXIValue *>(val));
}
Example #2
0
inline void AddParamValue(VXIMapHolder & m, const vxistring & name,
                          bool value)
{
  if (m.GetValue() == NULL) return;
  VXIBoolean * val = VXIBooleanCreate((value ? TRUE : FALSE));
  if (val == NULL) throw VXIException::OutOfMemory();
  VXIMapSetProperty(m.GetValue(), name.c_str(),
                    reinterpret_cast<VXIValue *>(val));
}
Example #3
0
VXIMap * GrammarManager::GetRecordProperties(const PropertyList & props,
                                             int timeout) const
{
  VXIMapHolder m;
  if (m.GetValue() == NULL) throw VXIException::OutOfMemory();

  // (1) Retrieve flattened property list.
  props.GetProperties(m);

  // (2) Convert & manipulate the properties.
  const VXIchar * j;
  VXIint time;

  // (2.1) Completion timeout
  j = props.GetProperty(GrammarManager::MaxTime);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_MAX_RECORDING_TIME, time);

  // (2.2) Final silence
  j = props.GetProperty(GrammarManager::FinalSilence);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_TIMEOUT_COMPLETE, time);

  // (2.3) Type
  j = props.GetProperty(GrammarManager::RecordingType);
  if (j != NULL)
    AddParamValue(m, REC_RECORD_MIME_TYPE, j);

  // (2.4) DTMF terminates record?
  j = props.GetProperty(GrammarManager::DTMFTerm);
  if (j != NULL) {
    int dtmfterm;
    if (vxistring(j) == L"false")
      dtmfterm = 0;
    else
      dtmfterm = 1;
    
    AddParamValue(m, REC_TERMINATED_ON_DTMF, dtmfterm);
  }

  // (2.5) Timeout settings
  if (timeout == -1) {
    j = props.GetProperty(PROP_TIMEOUT);
    if (j != NULL)
      PropertyList::ConvertTimeToMilliseconds(log, j, timeout);
  }

  if (timeout != -1) {
    AddParamValue(m, REC_DTMF_TIMEOUT, timeout);
    AddParamValue(m, REC_TIMEOUT, timeout);
  }

  // (3) Done

  return m.Release();
}
Example #4
0
bool GrammarManager::EnableGrammars(const vxistring & documentID,
                                    const vxistring & dialogName,
                                    const vxistring & fieldName,
                                    const VXIMapHolder & properties,
                                    bool isModal)
{
  bool enabled = false;

  for (GRAMMARS::iterator i = grammars.begin(); i != grammars.end(); ++i)
  {
    bool docsMatch    = (*i)->IsDoc(documentID);
    bool dialogsMatch = docsMatch    && (*i)->IsDialog(dialogName);
    bool fieldsMatch  = dialogsMatch && (*i)->IsField(fieldName);

    if ((!fieldName.empty() && fieldsMatch) ||
        // Enable those field grammars matching our (field, form) pair
        (!isModal && (*i)->IsScope(GRS_DIALOG) && dialogsMatch) ||
        // Enable form grammars & dialog scope fields, if not modal
        (!isModal && (*i)->IsScope(GRS_DOC))
        // Enable document level grammars, if not modal
       )
    {
      if (log.IsLogging(2)) {
        log.StartDiagnostic(2) << L"GrammarManager::EnableGrammar(" << (*i)
                               << L")";
        log.EndDiagnostic();
      }

      VXIrecResult err = vxirec->ActivateGrammar(vxirec, properties.GetValue(),
                                                 (*i)->GetRecGrammar());
      if (err != VXIrec_RESULT_SUCCESS)
        throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR);

      (*i)->SetEnabled(true);
      enabled = true;
    }
  }

  return enabled;
}
Example #5
0
// -2: Internal error
int DocumentParser::FetchDocument(const VXIchar * url,
                                  const VXIMapHolder & properties,
                                  VXIinetInterface * inet,
                                  VXIcacheInterface * cache,
                                  SimpleLogger & log,
                                  VXMLDocument & document,
                                  VXIMapHolder & docProperties,
                                  bool isDefaults,
                                  bool isRootApp,
                                  VXIbyte **content,
                                  VXIulong *size)
{
  int result;
  int path_only = 0;
  VXMLDocument *cached_parse_tree = NULL;

  if (log.IsLogging(2)) {
    log.StartDiagnostic(2) << L"DocumentParser::FetchDocument(" << url << L")";
    log.EndDiagnostic();
  }

  // (1) Load the VXML DTD for validation.  This will override an externally
  // specified DTD if the user provides a link.

  try {
    if (isDefaults) {
      MemBufInputSource membuf(
          VALIDATOR_DATA + DUMMY_VXML_DEFAULTS_DOC,
          DUMMY_VXML_DEFAULTS_DOC_SIZE,
          "vxml 1.0 defaults");
      parser->parse(membuf);
      converter->ResetDocument(); // Throw this document away.
    }

    if (!isDefaults && !loadedVXML20) {
      // Preload the VXML 2.1 schema.
      VXIcharToXMLCh name(L"http://www.w3.org/TR/voicexml21/vxml.xsd");
      LockLoadGrammar();
      parser->loadGrammar(name.c_str(), Grammar::SchemaGrammarType, true);
      // Reuse cached grammars if available.
      parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);      
      UnlockLoadGrammar(); 
      loadedVXML20 = true;
    }
  }
  catch (const XMLException & exception) {
    if (!isDefaults && !loadedVXML20) 
      UnlockLoadGrammar();  
      
    XMLChToVXIchar message(exception.getMessage());
    log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - XML parsing "
      L"error from DOM: " << message;
    log.EndDiagnostic();
    log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD");
    return 4;
  }
  catch (const SAXParseException & exception) {
    if (!isDefaults && !loadedVXML20) 
      UnlockLoadGrammar();  
      
    XMLChToVXIchar sysid(exception.getSystemId());
    XMLChToVXIchar message(exception.getMessage());
    log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Parse error "
                           << L"in file \"" 
                           << sysid 
                           << L"\", line " << exception.getLineNumber()
                           << L", column " << exception.getColumnNumber()
                           << L" - " << message;
    log.EndDiagnostic();
    log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD");
    return 4;
  }
  catch (...) {
    if (!isDefaults && !loadedVXML20) 
      UnlockLoadGrammar();  

    log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Unknown Parse error";
    log.EndDiagnostic();
    log.LogError(999, SimpleLogger::MESSAGE, L"unable to load VXML DTD");
    return 4;          
  }

  // (2) Load the url's content into memory.

  const VXIbyte * buffer = NULL;
  VXIulong bufSize = 0;
  vxistring docURL;
  bool isDefaultDoc = false;
  
  if (isDefaults && wcslen(url) == 0)
  {
    buffer  = VALIDATOR_DATA + VXML_DEFAULTS;
    bufSize = VXML_DEFAULTS_SIZE;
    docURL = L"builtin defaults";
    result = 0;
    isDefaultDoc = true;
  }
  else
  {
      path_only = 1;
      result = DocumentParser::FetchBuffer
	  (url, properties, docProperties, inet, log,
	   buffer, bufSize, docURL, 1, &cached_parse_tree);
  }

  if (result != 0) {
    if (log.IsLogging(0)) {
      log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - exiting "
        L"with error result " << result;
      log.EndDiagnostic();
    }
    return result; // may return { -1, 1, 2, 3 }
  }

  // store buffer for reference
  if (content) {
    VXIbyte *tempbuf = new VXIbyte[bufSize];
    if(tempbuf == NULL) {
      log.LogError(202);
      if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer);
      return -1;
    }

    if (voiceglue_loglevel() >= LOG_DEBUG)
    {
	std::ostringstream logstring;
	logstring << "(Copy) Allocated http_get buffer "
		  << Pointer_to_Std_String((const void *) tempbuf)
		  << " of size " << bufSize;
	voiceglue_log ((char) LOG_DEBUG, logstring);
    };

    memcpy(tempbuf, buffer, bufSize);
    if (size != NULL) *size = bufSize;
    *content = tempbuf;
  }
  
  // (3) Pull the document from cache.

  vxistring baseURL;
  VXMLDocument doc;
  // if (!DocumentStorageSingleton::Instance()->Retrieve(doc, buffer, bufSize, docURL.c_str())) {
  if (cached_parse_tree == NULL)
  {
      //  No cached parse tree, must perform my own parse
      //  and return results to voiceglue perl

      // (3.1) Set the base uri for this document, but
      // ignore if it is the default doc. Note: only want to do this if
      // not pulling from the cache otherwise the base is messed up
      if(!isDefaultDoc) 
        converter->SetBaseUri(docURL.c_str());

    // (4) Not in cache; parse buffer into our VXML document representation
    try
    {
      VXIcharToXMLCh membufURL(docURL.c_str());
      // Set Document level
      if( isDefaults ) 
        converter->SetDocumentLevel(DEFAULTS);
      else if( isRootApp )
        converter->SetDocumentLevel(APPLICATION);
      else
        converter->SetDocumentLevel(DOCUMENT);
      // Parse the script
      if (path_only)
      {
	  vxistring vxi_string_path =
	      Std_String_to_vxistring ((const char *) buffer);
	  VXIcharToXMLCh wide_path(vxi_string_path.c_str());
	  LocalFileInputSource filesource(wide_path.c_str());
	  parser->parse(filesource);
      }
      else
      {
	  MemBufInputSource membuf(buffer, bufSize, membufURL.c_str(), false);
	  parser->parse(membuf);
      };
    }
    catch (const XMLException & exception) {
      if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer);
      if (path_only) voiceglue_sendipcmsg ("VXMLParse 0 -\n");
      if (log.IsLogging(0)) {
        XMLChToVXIchar message(exception.getMessage());
        log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - XML "
          L"parsing error from DOM: " << message;
        log.EndDiagnostic();
      }
      return 4;
    }
    catch (const SAXParseException & exception) {
      VXIString *key = NULL, *value = NULL;
      if (log.LogContent(VXI_MIME_XML, buffer, bufSize, &key, &value)) 
      {
        vxistring temp(L"");
        temp += VXIStringCStr(key);
        temp += L": ";
        temp += VXIStringCStr(value);
        log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - buffer is saved in: "
                               << temp;
        log.EndDiagnostic();
      }        
      if (key) VXIStringDestroy(&key);
      if (value) VXIStringDestroy(&value);
      
      if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer);
      if (path_only) voiceglue_sendipcmsg ("VXMLParse 0 -\n");
      if (log.IsLogging(0)) {
        XMLChToVXIchar sysid(exception.getSystemId());
        XMLChToVXIchar message(exception.getMessage());
        log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - Parse "
                               << L"error in file \"" 
                               << sysid
                               << L"\", line " << exception.getLineNumber()
                               << L", column " << exception.getColumnNumber()
                               << L" - " 
                               << message;
        log.EndDiagnostic();
      }
      return 4;
    }

    // (5) Write the parsed tree address out to the voiceglue cache
    doc = converter->GetDocument();
    // Set the current uri as base uri if there isn't a "xml:base" attribute
    doc.GetBaseURL(baseURL);
    if( baseURL.empty() ) doc.SetBaseURL(docURL);
    
    // store default language
    if( isDefaults ) {
      vxistring dlang;
      converter->GetDefaultLang(dlang);
      doc.SetDefaultLang(dlang);
    }        

    // Put into voiceglue cache as standalone persistent VXMLDocument object
    if (path_only)
    {
	VXMLDocument *permanent_doc = new VXMLDocument (doc);
	std::ostringstream ipc_msg_out;
	ipc_msg_out
	    << "VXMLParse 1 "
	    << Pointer_to_Std_String ((void *) permanent_doc)
	    << "\n";
	voiceglue_sendipcmsg (ipc_msg_out);
    }
    // DocumentStorageSingleton::Instance()->Store(doc, buffer, bufSize, docURL.c_str());
  }
  else {
    // The document is already in the memory cache
      doc = *cached_parse_tree;
    // Need to set the base uri
    converter->RestoreBaseURLFromCache(doc);
    // Restore default language
    if( isDefaults ) {
      converter->RestoreDefaultLangFromCache(doc);
    }
  }

  if( !isDefaultDoc ) DocumentParser::ReleaseBuffer(buffer);

  // (6) Parse was successful, process document.  We want only the top level
  // <vxml> node.

  const VXMLElement root = doc.GetRoot();
  VXMLElementType nodeName = root.GetName();

  // If we're looking for the defaults, we can exit early.
  if (isDefaults && nodeName == DEFAULTS_ROOT) {
    log.LogDiagnostic(2, L"DocumentParser::FetchDocument(): Default document - success");
    document = doc;
    return 0;
  }
  else if (nodeName != NODE_VXML) {
    document = VXMLDocument();
    if (log.IsLogging(0)) {
      log.StartDiagnostic(0) << L"DocumentParser::FetchDocument - unable to "
        L"find " << NODE_VXML << L" in document.";
      log.EndDiagnostic();
    }
    return 4;
  }

  vxistring temp;

  // If the properties map is NULL, don't bother with the remaining settings
  if (docProperties.GetValue() != NULL) {
    // Retrieve the properties and put them into the map.

    VXIString * str = NULL;
    temp = docURL;
    str = VXIStringCreate(temp.c_str());
    if (str == NULL) 
      throw VXIException::OutOfMemory();

    VXIMapSetProperty(docProperties.GetValue(), PropertyList::AbsoluteURI,
                      reinterpret_cast<VXIValue *>(str));

    str = VXIStringCreate(converter->GetBaseUri().empty() ? L"" : 
                          converter->GetBaseUri().c_str());
    
    if (str == NULL)
      throw VXIException::OutOfMemory();

    VXIMapSetProperty(docProperties.GetValue(), PropertyList::BaseURI,
                      reinterpret_cast<VXIValue *>(str));

	// store encoding
    str = VXIStringCreate(encoding.empty() ? L"UTF-8" : encoding.c_str());
    if (str == NULL)
      throw VXIException::OutOfMemory();
    VXIMapSetProperty(docProperties.GetValue(), PropertyList::SourceEncoding,
                      reinterpret_cast<VXIValue *>(str));

	// store xml:lang
	if (root.GetAttribute(ATTRIBUTE_XMLLANG, temp)) {
      str = VXIStringCreate(temp.c_str());
      if (str == NULL)
        throw VXIException::OutOfMemory();

      VXIMapSetProperty(docProperties.GetValue(), PropertyList::Language,
                        reinterpret_cast<VXIValue *>(str));
    }
  }

  log.LogDiagnostic(2, L"DocumentParser::FetchDocument(): VXML document - success");

  document = doc;
  return 0;
}
Example #6
0
// 1: Invalid parameter
// 2: Unable to open URL
// 3: Unable to read from URL
int DocumentParser::FetchBuffer(const VXIchar * url,
                                const VXIMapHolder & properties,
                                VXIMapHolder & streamInfo,
                                VXIinetInterface * inet,
                                SimpleLogger & log,
                                const VXIbyte * & result,
                                VXIulong & read,
                                vxistring & docURL,
				int parseVXMLDocument,
				VXMLDocument * * document)
{
  if (log.IsLogging(2)) {
    log.StartDiagnostic(2) << L"DocumentParser::FetchBuffer(" << url
                           << L", " << properties.GetValue() << L")";
    log.EndDiagnostic();
  }

  // Set url for error report
  log.SetUri( url ? url : L"NONE" );
  
  if (inet == NULL || url == NULL || wcslen(url) == 0) return 1;
    
  // (1) Open URL
  VXIinetStream * stream;

  // VXIMapHolder streamInfo;
  if (streamInfo.GetValue() == NULL) 
  {
    return -1;
  }

  VXIinetOpenMode open_mode = INET_MODE_READ;
  if (parseVXMLDocument)
  {
      open_mode = INET_MODE_FILE;
  };
  
  if (inet->Open(inet, L"vxi", url, open_mode, 0, properties.GetValue(),
                 streamInfo.GetValue(), &stream) != 0)
  {
    if (log.IsLogging(0)) {
      log.StartDiagnostic(0) << L"DocumentParser::FetchBuffer - could not "
        L"open URL: " << url;
      log.EndDiagnostic();
    }
    return 2;
  }
  
  // (2) Determine document size & absolute URL
  const VXIValue * tempURL = NULL;
  tempURL = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_ABSOLUTE_NAME);
  if (tempURL == NULL || VXIValueGetType(tempURL) != VALUE_STRING) {
    inet->Close(inet, &stream);
    if (log.IsLogging(0)) {
      log.StartDiagnostic(0) << L"DocumentParser::FetchBuffer - could not "
        L"retrieve absolute path of document at URL: " << url;
      log.EndDiagnostic();
    }
    return 2;
  }
  docURL = VXIStringCStr(reinterpret_cast<const VXIString *>(tempURL));

  VXIbyte * buffer = NULL;
  if (parseVXMLDocument)
  {
      //  Decode parse tree addr
      (* document) = NULL;
      const VXIValue* wide_parse_tree_addr =
	  VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_PARSE_TREE);
      std::string parse_tree_addr =
	  VXIValue_to_Std_String (wide_parse_tree_addr);
      if (parse_tree_addr.length() > 2)
      {
	  void *parse_tree_pointer = Std_String_to_Pointer (parse_tree_addr);
	  if (parse_tree_pointer != NULL)
	  {
	      (* document) = (VXMLDocument *) parse_tree_pointer;

	      if (voiceglue_loglevel() >= LOG_DEBUG)
	      {
		  std::ostringstream logstring;
		  logstring << "Received VXMLDocument parse tree at "
			    << Pointer_to_Std_String ((const void *)
						      (* document));
		  voiceglue_log ((char) LOG_DEBUG, logstring);
	      };
	  };
      };

      //  Decode path
      const VXIValue* wide_path = VXIMapGetProperty(streamInfo.GetValue(),
						    INET_INFO_LOCAL_FILE_PATH);
      std::string path = VXIValue_to_Std_String (wide_path);
      int bufSize = path.length() + 1;
      buffer = new VXIbyte[bufSize];
      read = bufSize;
      if(buffer == NULL) {
	  log.LogError(202);
	  return -1;
      }
      memcpy (buffer, path.c_str(), bufSize);
  }
  else
  {
      //  Read into memory buffer
      const VXIValue * tempSize = NULL;
      tempSize = VXIMapGetProperty(streamInfo.GetValue(), INET_INFO_SIZE_BYTES);
      if (tempSize == NULL || VXIValueGetType(tempSize) != VALUE_INTEGER) {
	  inet->Close(inet, &stream);
	  if (log.IsLogging(0)) {
	      log.StartDiagnostic(0)
		  << L"DocumentParser::FetchBuffer - could not "
		  L"retrieve size of document at URL: " << url;
	      log.EndDiagnostic();
	  }
	  return 2;
      }
      
      VXIint32 bufSize
	  = VXIIntegerValue(reinterpret_cast<const VXIInteger *>(tempSize));
      
      if (bufSize < 2047)
	  bufSize = 2047;
      ++bufSize;

      buffer = new VXIbyte[bufSize];
      if(buffer == NULL) {
	  log.LogError(202);
	  return -1;
      }

      if (voiceglue_loglevel() >= LOG_DEBUG)
      {
	  std::ostringstream logstring;
	  logstring << "Allocated http_get buffer "
		    << Pointer_to_Std_String((const void *) buffer)
		    << " of size " << bufSize;
	  voiceglue_log ((char) LOG_DEBUG, logstring);
      };

      bool reachedEnd = false; 
      read = 0;

      while (!reachedEnd) {
	  VXIulong bytesRead = 0;
	  switch (inet->Read(inet, buffer+read, bufSize-read,
			     &bytesRead, stream))
	  {
	  case VXIinet_RESULT_SUCCESS:
	      read += bytesRead;
	      break;
	  case VXIinet_RESULT_END_OF_STREAM:
	      read += bytesRead;
	      reachedEnd = true;  // exit while
	      break;
	  case VXIinet_RESULT_WOULD_BLOCK:
	      VXItrdThreadYield();
	      break;
	  default:
	      inet->Close(inet, &stream);
	      delete[] buffer;
	      
	      log.LogDiagnostic(0, L"DocumentParser::FetchBuffer - "
				L"could not read from URL.");
	      return 3;
	  }

	  if (read == static_cast<VXIulong>(bufSize)) {
	      // The number of bytes read exceeds the number expected.
	      // Double the size and keep reading.
	      VXIbyte * temp = new VXIbyte[2*bufSize];
	      if(temp == NULL) {
		  log.LogError(202);
		  delete [] buffer;
		  if (voiceglue_loglevel() >= LOG_DEBUG)
		  {
		      std::ostringstream logstring;
		      logstring << "Released http_get buffer "
				<< Pointer_to_Std_String((const void *) buffer);
		      voiceglue_log ((char) LOG_DEBUG, logstring);
		  };
		  return -1;
	      }

	      if (voiceglue_loglevel() >= LOG_DEBUG)
	      {
		  std::ostringstream logstring;
		  logstring << "Allocated http_get buffer "
			    << Pointer_to_Std_String((const void *) temp)
			    << " of size " << (2*bufSize);
		  voiceglue_log ((char) LOG_DEBUG, logstring);
	      };

	      memcpy(static_cast<void *>(temp), static_cast<void *>(buffer),
		     bufSize * sizeof(VXIbyte));
	      delete[] buffer;
	      if (voiceglue_loglevel() >= LOG_DEBUG)
	      {
		  std::ostringstream logstring;
		  logstring << "Released http_get buffer "
			    << Pointer_to_Std_String((const void *) buffer);
		  voiceglue_log ((char) LOG_DEBUG, logstring);
	      };
	      buffer = temp;
	      bufSize *= 2;
	  }
      }

      inet->Close(inet, &stream);
  };

  result = buffer;

  log.LogDiagnostic(2, L"DocumentParser::FetchBuffer - success");

  return 0;
}
Example #7
0
int GrammarManager::Record(const VXIMapHolder & properties,
                           VXIrecRecordResult * & resultStruct)
{
  // (1) Do record.

  VXIrecResult err = vxirec->Record(vxirec, properties.GetValue(),
                                       &resultStruct);
  if (err == VXIrec_RESULT_OUT_OF_MEMORY) {
    log.LogDiagnostic(0, L"GrammarManager::Record - Out of memory.");
    return GrammarManager::OutOfMemory;
  }

  if (err == VXIrec_RESULT_BAD_MIME_TYPE) {
    log.LogDiagnostic(0, L"GrammarManager::Record - Unsupported mime type.");
    return GrammarManager::BadMimeType;
  }

  if (err == VXIrec_RESULT_DISCONNECT) {
    // Here is the case that the call is being disconnected before the 
    // recording even starts
    return GrammarManager::Disconnect;
  }

  if (err != VXIrec_RESULT_SUCCESS) {
    log.StartDiagnostic(0) << L"GrammarManager::Record - "
      L"VXIrecInterface::Record returned " << int (err);
    log.EndDiagnostic();
    log.LogError(421, SimpleLogger::MESSAGE,
                 L"function did not return the expected VXIrecSUCCESS result");
    return GrammarManager::InternalError;
  }

  if (resultStruct == NULL) {
    // Here is the case that the call is being disconnected before the recording even starts
    return GrammarManager::Disconnect;
  }

  // (2) Process all non-Successful results.

  switch (resultStruct->status) {
  case REC_STATUS_SUCCESS:    // Record produced a recording
    break;
  case REC_STATUS_TIMEOUT:    // No signal was detected
    return GrammarManager::Timeout;
  case REC_STATUS_ERROR:      // An error aborted record
    return GrammarManager::Error;
  case REC_STATUS_DISCONNECT: // Caller has disconnected without a recording
    return GrammarManager::Disconnect;
  case REC_STATUS_FAILURE:    // This should never happen
    return GrammarManager::InternalError;
  default:
    log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - "
      L"VXIrecInterface::Recognize returned status "
      << int (resultStruct->status);
    log.EndDiagnostic();
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function returned an invalid VXIrecStatus code");
    return GrammarManager::InternalError;
  }

  // (3) Verify that in the success case, the information is valid.

  if (resultStruct->waveform == NULL) {
    log.LogError(421, SimpleLogger::MESSAGE,
                 L"function did not produce a recording");
    return GrammarManager::InternalError;
  }

  // Technically recordings could exceed an hour.  These users may just
  // change the source.
  if (resultStruct->duration == 0 || resultStruct->duration > 3600000) {
    log.LogError(421, SimpleLogger::MESSAGE,
                 L"function returned invalid recording duration");
    return GrammarManager::InternalError;
  }

  return GrammarManager::Success;
}
Example #8
0
// This function is responsible for calling the VXIrec level Recognize function
// and then mapping the grammar back to the corresponding VXML node.
//
int GrammarManager::Recognize(const VXIMapHolder & properties,
                              RecognitionAnswer & recAnswer,
                              VXMLElement & recNode)
{
  recNode = VXMLElement();
  VXIrecRecognitionResult * answer = NULL;

  // (1) Do VXIrec level Recognitize and process return value.

  // (1.1) Recognize.
  VXIrecResult err = vxirec->Recognize(vxirec, properties.GetValue(),
                                       &answer);
  if (err == VXIrec_RESULT_OUT_OF_MEMORY) {
    log.LogDiagnostic(0,L"GrammarManager::InternalRecognize - Out of memory.");
    return GrammarManager::OutOfMemory;
  }

  if (err == VXIrec_RESULT_DISCONNECT) {
    // Here is the case that the call is being disconnected before the 
    // recognition even starts
    return GrammarManager::Disconnect;
  }

  // (1.2) Anything other than success indicates that recognition failed
  // (badly).  The normal error conditions are returned through the recogntion
  // result structure.
  if (err != VXIrec_RESULT_SUCCESS) {
    log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - "
      L"VXIrecInterface::Recognize returned " << int (err);
    log.EndDiagnostic();
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function did not return the expected VXIrecSUCCESS result");
    return GrammarManager::InternalError;
  }

  // (1.3) The answer structure must be defined.
  if (answer == NULL) {
    // Here is the case that the call is being disconnected before the recording even starts
    return GrammarManager::Disconnect;
  }

  // (1.4) Attach the answer structure returned by VXIrec and the waveform to
  // the recAnswer class.
  recAnswer.Bind(answer);
  recAnswer.waveform = answer->waveform;

  // (2) Process all non-Successful results.

  // (2.1) First the status code.

  switch (answer->status) {
  case REC_STATUS_SUCCESS:    // Recognition returned a hypothesis
  case REC_STATUS_DTMFSUCCESS:    // Recognition returned a hypothesis
    break;
  case REC_STATUS_FAILURE:    // Speech detected, no likely hypothesis
    return GrammarManager::Failure;
  case REC_STATUS_TIMEOUT:    // No speech was detected
    return GrammarManager::Timeout;
  case REC_STATUS_DISCONNECT: // Caller has disconnected; no hypothesis
    return GrammarManager::Disconnect;
  case REC_STATUS_ERROR:      // An error aborted recognition
    return GrammarManager::Error;
  default:
    log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - "
      L"VXIrecInterface::Recognize returned status " << int(answer->status);
    log.EndDiagnostic();
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function returned an invalid VXIrecStatus code");
    return GrammarManager::InternalError;
  }

  // (2.2) Recognition success. Verify that the input mode was set correctly.

  switch (answer->mode) {
  case REC_INPUT_MODE_DTMF:
  case REC_INPUT_MODE_SPEECH:
    break;
  default:
    log.StartDiagnostic(0) << L"GrammarManager::InternalRecognize - "
      L"VXIrecInterface::Recognize returned mode " << int(answer->mode);
    log.EndDiagnostic();
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function returned an invalid VXIrecInputMode value");
    return GrammarManager::InternalError;
  }

  // (3) Walk through results array and sanity check the results.

  // (3.1) How many answers are there?

  if (answer->results == NULL) {
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function returned a null results vector");
    return GrammarManager::InternalError;
  }

  unsigned int NUM_ANSWERS = VXIVectorLength(answer->results);
  if (NUM_ANSWERS == 0) {
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"function returned an empty results vector");
    return GrammarManager::InternalError;
  }

  const VXIMap * bestAnswer = NULL;

  for (unsigned int i = 0; i < NUM_ANSWERS; ++i) {
    // (3.2) Find each element in the result vector.
    
    const VXIValue * temp = VXIVectorGetElement(answer->results, i);
    if (temp == NULL) {
      log.LogError(400, SimpleLogger::MESSAGE,
                   L"VXIVectorGetElement failed to return answer.");
      return GrammarManager::InternalError;
    }
    
    if (VXIValueGetType(temp) != VALUE_MAP) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"function returned an invalid results vector");
      return GrammarManager::InternalError;
    }

    const VXIMap * nthAnswer = reinterpret_cast<const VXIMap*>(temp);
    if (i == 0) bestAnswer = nthAnswer;

    // (3.3) Validate types of member keys.

    temp = VXIMapGetProperty(nthAnswer, REC_KEYS);
    if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"REC_KEYS must be defined with type vector");
      return GrammarManager::InternalError;
    }
    const VXIVector * keys = reinterpret_cast<const VXIVector *>(temp);

    temp = VXIMapGetProperty(nthAnswer, REC_VALUES);
    if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"REC_VALUES must be defined with type vector");
      return GrammarManager::InternalError;
    }
    const VXIVector * values = reinterpret_cast<const VXIVector *>(temp);

    temp = VXIMapGetProperty(nthAnswer, REC_CONF);
    if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"REC_CONF must be defined with type vector");
      return GrammarManager::InternalError;
    }
    const VXIVector * scores = reinterpret_cast<const VXIVector *>(temp);

    temp = VXIMapGetProperty(nthAnswer, REC_RAW);
    if (temp == NULL || VXIValueGetType(temp) != VALUE_VECTOR) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"REC_RAW must be defined with type vector");
      return GrammarManager::InternalError;
    }
    const VXIVector * utts = reinterpret_cast<const VXIVector *>(temp);

    // (3.4) Validate vector lengths.

    const VXIunsigned length = VXIVectorLength(keys);
    if (length < 1) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"result vector must have length of at least one");
      throw VXIException::Fatal();
    }
    if (length != VXIVectorLength(values) ||
        length != VXIVectorLength(scores) ||
        length != VXIVectorLength(utts))
    {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"result vector length must all match");
      return GrammarManager::InternalError;
    }

    temp = VXIVectorGetElement(keys, 0);
    if (temp == NULL || VXIValueGetType(temp) != VALUE_STRING) {
      log.LogError(420, SimpleLogger::MESSAGE,
                   L"first REC_KEYS must be defined with type string");
      return GrammarManager::InternalError;
    }

    // (3.5) Copy into results class.

    recAnswer.keys.push_back(keys);
    recAnswer.values.push_back(values);
    recAnswer.scores.push_back(scores);
    recAnswer.utts.push_back(utts);
  }

  recAnswer.numAnswers = NUM_ANSWERS;

  // (4) Find the VXML element associated with the matched grammar.

  // (4.1) Find the grammar corresponding to the best answer.

  const VXIValue * temp = VXIMapGetProperty(bestAnswer, REC_GRAMMAR);
  if (temp == NULL || VXIValueGetType(temp) != VALUE_PTR) {
    log.LogError(420, SimpleLogger::MESSAGE,
                 L"unable to obtain grammar key from 'results' #0");
    return GrammarManager::InternalError;
  }

  const VXIPtr * tempptr = reinterpret_cast<const VXIPtr *>(temp);
  const VXIrecGrammar * bestGrammar =
    reinterpret_cast<const VXIrecGrammar*>(VXIPtrValue(tempptr));

  // (4.2) Map the VXIrecGrammar pointer back to the source VXMLElement.

  for (GRAMMARS::iterator j = grammars.begin(); j != grammars.end(); ++j) 
  {
    if ((answer->mode == REC_INPUT_MODE_DTMF && REC_STATUS_DTMFSUCCESS != answer->status) 
      && (*j)->GetRecGrammar() != bestGrammar) continue;

    if (!(*j)->IsEnabled()) 
    {
      if (j == grammars.end()) {
        log.LogError(420, SimpleLogger::MESSAGE,
                     L"function returned an inactive grammar");
        return GrammarManager::InternalError;
      }
      continue;
    }

    (*j)->GetElement(recNode);

    if (answer->mode == REC_INPUT_MODE_DTMF)
      return GrammarManager::SuccessDTMF;

    return GrammarManager::Success;
  }

  log.LogError(420, SimpleLogger::MESSAGE,
               L"function returned a non-existent grammar");
  return GrammarManager::InternalError;
}
Example #9
0
// This function is used to recursively walk through the tree, loading and
// speech or dtmf grammars which are found.
//
void GrammarManager::LoadGrammars(const VXMLElement& doc,
                                  vxistring & documentID,
                                  PropertyList & properties)
{
  if (doc == 0) return;

  VXIMapHolder recProps(NULL);  // Initialize an empty holder.

  // (1) Retrieve the ID for this document.  This is important for grammar
  // activation.

  if (doc.GetName() == NODE_VXML)
    doc.GetAttribute(ATTRIBUTE__ITEMNAME, documentID);

  // (2) Look for grammars in current nodes.

  for (VXMLNodeIterator it(doc); it; ++it) {
    VXMLNode child = *it;

    if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
    const VXMLElement & element = reinterpret_cast<const VXMLElement &>(child);

    VXMLElementType elementName = element.GetName();
    VXIrecGrammar * vg = NULL;

    if (recProps.GetValue() == NULL)
      recProps.Acquire(GetRecProperties(properties));

    // (3) Handle <grammar> & <dtmf>

    if (elementName == NODE_GRAMMAR || elementName == NODE_DTMF) {
      vxistring src;
      element.GetAttribute(ATTRIBUTE_SRC, src);

      // (3.1) Override the language setting (if specified as an attribute)
      vxistring lang;
      if (element.GetAttribute(ATTRIBUTE_XMLLANG, lang) == true)
        AddParamValue(recProps, REC_LANGUAGE, lang);

      // (3.2) Does the grammar come from an external URI?
      if (!src.empty()) {
        if (log.IsLogging(2)) {
          log.StartDiagnostic(2) << L"GrammarManager::LoadGrammars - <grammar "
            L"src=\"" << src << L"\">";
          log.EndDiagnostic();
        }

        VXIMapHolder fetchobj;
        if (fetchobj.GetValue() == NULL) throw VXIException::OutOfMemory();

        vxistring fragment;
        properties.GetFetchobjCacheAttrs(element, PropertyList::Grammar,
                                         fetchobj);
        properties.GetFetchobjURIs(element, fetchobj, src, fragment);

        if (!fragment.empty())
          log.LogError(215);

        vxistring mimeType;
        element.GetAttribute(ATTRIBUTE_TYPE, mimeType);

        VXIrecResult err = vxirec->LoadGrammarURI(vxirec, recProps.GetValue(),
                                                  mimeType.c_str(),
                                                  src.c_str(),
                                                  fetchobj.GetValue(), &vg);
        if (err != VXIrec_RESULT_SUCCESS)
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR);

        if (vg) AddGrammar(vg, documentID, element);
      }
      // (3.3) Otherwise this is an inlined grammar.
      else {
        log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <grammar>");

        vxistring text;
        GetEnclosedText(log, element, text);
        
        vxistring mimeType;
        element.GetAttribute(ATTRIBUTE_TYPE, mimeType);

        if (mimeType.empty() && elementName == NODE_DTMF)
          vg = GrammarManager::CreateGrammarFromString(vxirec, log, text,
                                                       REC_MIME_GENERIC_DTMF,
                                                       recProps);
        else
          vg = GrammarManager::CreateGrammarFromString(vxirec, log, text,
                                                       mimeType.c_str(),
                                                       recProps);
        
        if (vg == NULL) // "Error loading in-line grammar %s",text
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_INLINE);

        AddGrammar(vg, documentID, element);
      }
    }

    // (4) Handle <choice>

    else if (elementName == NODE_CHOICE) {
      log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <choice>");

      // (4.1) If there is a <grammar> tag, it overrides any implicit grammar.

      // (4.1.1) Check for <grammar> element.
      bool foundGrammar = false;

      for (VXMLNodeIterator it(element); it; ++it) {
        VXMLNode child = *it;

        if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
        const VXMLElement & temp = reinterpret_cast<const VXMLElement&>(child);
        if (temp.GetName() != NODE_GRAMMAR) continue;
        foundGrammar = true;
        break;
      }
      // (4.1.2) If found, apply recursion.
      if (foundGrammar) {
        // <choice> nodes can't contain properties.  Don't need to call Push.
        LoadGrammars(element, documentID, properties);
      }

      // (4.1.3) Otherwise, construct a grammar from the prompt text.
      else {
        vxistring text;
        GetEnclosedText(log, element, text);

        // The text may be empty, e.g. for a dtmf only grammar.
        if (!text.empty()) {
          VXIrecGrammar * vg = NULL;

          vg = GrammarManager::CreateGrammarFromString(vxirec, log, text,
                                                       REC_MIME_CHOICE,
                                                       recProps);
          if (vg == NULL) // "Error loading in-line grammar %s",text
            throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);

          AddGrammar(vg, documentID, element);
        }
      }

      // (4.2) Create associated DTMF grammar.
      //
      // Either an explict dtmf choice is given or implicit numbers are
      // generated for the first nine.  When both are specified, the explicit
      // choice wins.  This will quite possibly create unintentional (and
      // undetected) duplicates....

      vxistring dtmf;
      element.GetAttribute(ATTRIBUTE_DTMF, dtmf);

      if (!dtmf.empty()) {
        VXIrecGrammar * vg = NULL;

        vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf,
                                                     REC_MIME_CHOICE_DTMF,
                                                     recProps);
        if (vg == NULL)
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);

        AddGrammar(vg, documentID, element);
      }
    }

    // (5) Handle <field>.

    else if (elementName == NODE_FIELD) { 
      log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <field>");

      vxistring gram;
      VXIrecGrammar * vg;

      // Build option grammar (if one exists).
      GrammarManager::BuildOptionGrammar(log, element, false, gram);
      if (!gram.empty()) {
        vg = GrammarManager::CreateGrammarFromString(vxirec, log, gram,
                                                     REC_MIME_OPTION,
                                                     recProps);
        if (vg == NULL)
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_OPTION);
        AddGrammar(vg, documentID, element);
      }

      // Build option dtmf grammar (if one exists).
      gram = L"";
      GrammarManager::BuildOptionGrammar(log, element, true, gram);
      if (gram.length() != 0) {
        vg = GrammarManager::CreateGrammarFromString(vxirec, log, gram,
                                                     REC_MIME_OPTION_DTMF,
                                                     recProps);
        if (vg == NULL)
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_OPTION);
        AddGrammar(vg, documentID, element);
      }

      // Add the built-in grammars (if they exist).
      vg = NULL;
      vxistring type;
      element.GetAttribute(ATTRIBUTE_TYPE, type);
      if (!type.empty()) {
        vxistring newuri(L"builtin:grammar/");
        newuri += type;
        if (vxirec->LoadGrammarURI(vxirec, recProps.GetValue(), NULL,
                                   newuri.c_str(), NULL, &vg)
            != VXIrec_RESULT_SUCCESS)
        {
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR);
        }
        if (vg) AddGrammar(vg, documentID, element);

        newuri = L"builtin:dtmf/";
        newuri += type;
        if (vxirec->LoadGrammarURI(vxirec, recProps.GetValue(), NULL,
                                   newuri.c_str(), NULL, &vg)
            != VXIrec_RESULT_SUCCESS)
        {
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_GRAMMAR);
        }
        if (vg) AddGrammar(vg, documentID, element);
      }

      // Recursively add grammars (this handles <grammar>)
      bool doPop = properties.PushProperties(element);
      LoadGrammars(element, documentID, properties);
      if (doPop) properties.PopProperties();
    }

    // (6) Handle <link>.

    else if (elementName == NODE_LINK) {
      log.LogDiagnostic(2, L"GrammarManager::LoadGrammars - <link>");

      // (6.1) Get properties at this level.
      bool doPop = properties.PushProperties(element);

      // (6.2) Create DTMF grammar is specified.
      vxistring dtmf;
      element.GetAttribute(ATTRIBUTE_DTMF, dtmf);
      if (!dtmf.empty()) {
        VXIrecGrammar * vg = NULL;
        vg = GrammarManager::CreateGrammarFromString(vxirec, log, dtmf,
                                                     REC_MIME_CHOICE_DTMF,
                                                     recProps);
        if (vg == NULL)
          throw VXIException::InterpreterEvent(EV_ERROR_BAD_CHOICE);

        AddGrammar(vg, documentID, element);
      }

      // (6.3) Create child grammars.
      LoadGrammars(element, documentID, properties);
      if (doPop) properties.PopProperties();
    }

    // (7) Handle <record> and <transfer>.

    else if (elementName == NODE_RECORD || elementName == NODE_TRANSFER) { 
      // The DTD allows <grammar> elements, but these don't make sense.  We
      // will therefore ignore them.
    }

    // (8) Handle <transfer>
    else if ( elementName == NODE_TRANSFER ) {
       bool doPop = properties.PushProperties(element);
       LoadGrammars(element, documentID, properties);
       if (doPop) properties.PopProperties();
    }
 
    // (9) Otherwise, nothing was found at this level.  Use recursion to check
    //     the next level down.

    else {
      bool doPop = properties.PushProperties(element);
      LoadGrammars(element, documentID, properties);
      if (doPop) properties.PopProperties();
    }
  }
}
Example #10
0
VXIMap * GrammarManager::GetRecProperties(const PropertyList & props,
                                          int timeout) const
{
  VXIMapHolder m;
  if (m.GetValue() == NULL) throw VXIException::OutOfMemory();

  // (1) Retrieve flattened property list.
  props.GetProperties(m);

  // (2) Convert & manipulate the properties.
  // TBD #pragma message ("GrammarManager::GetRecProperties - handle REC_RESULT_SAVE_WAVEFORM ???")
  // TBD #pragma message ("GrammarManager::GetRecProperties - handle REC_RESULT_NBEST_SIZE ???")

  const VXIchar * j;
  VXIint time;
  VXIflt32 fraction;

  // (2.1) Language
  j = props.GetProperty(PropertyList::Language);
  if (j != NULL)
    AddParamValue(m, REC_LANGUAGE, j);

  // (2.2) Completion timeout
  j = props.GetProperty(PROP_COMPLETETIME);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_TIMEOUT_COMPLETE, time);

  // (2.3) Incompletion timeout
  j = props.GetProperty(PROP_INCOMPLETETIME);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_TIMEOUT_INCOMPLETE, time);

  // (2.4) Inter-Digit timeout
  j = props.GetProperty(PROP_INTERDIGITTIME);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_DTMF_TIMEOUT_INTERDIGIT, time);

  // (2.5) Inter-Digit timeout
  j = props.GetProperty(PROP_TERMTIME);
  if (j != NULL && props.ConvertTimeToMilliseconds(log, j, time))
    AddParamValue(m, REC_DTMF_TIMEOUT_TERMINATOR, time);

  // (2.6) Confidence level
  j = props.GetProperty(PROP_CONFIDENCE);
  if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
    AddParamValue(m, REC_CONFIDENCE_LEVEL, fraction);

  // (2.7) Barge-in sensitivity level
  j = props.GetProperty(PROP_SENSITIVITY);
  if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
    AddParamValue(m, REC_SENSITIVITY, fraction);

  // (2.8) Performance tradeoff - speed vs. accuracy
  j = props.GetProperty(PROP_SPEEDVSACC);
  if (j != NULL && props.ConvertValueToFraction(log, j, fraction))
    AddParamValue(m, REC_SPEED_VS_ACCURACY, fraction);
  
  // (2.9) DTMF terminator character
  j = props.GetProperty(PROP_TERMCHAR);
  if ((j != NULL) && (j[0] != L'\0')) {
    if (wcslen(j) == 1)
      AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, j);
    else {
      log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - "
        L"Unable to set " << REC_DTMF_TERMINATOR_CHAR << L" from value \"" <<
        j << L"\".  Defaulting to '#'.";
      log.EndDiagnostic();
      // Should we use the default, or rather not set anything ?
      AddParamValue(m, REC_DTMF_TERMINATOR_CHAR, L"#");
    }
  }
  
  // (2.10) Input modes
  int mode = REC_INPUT_MODE_DTMF_SPEECH;
  j = props.GetProperty(PROP_INPUTMODES);
  if (j != NULL) { 
    vxistring value(j);
    if (value == L"voice dtmf" || value == L"dtmf voice")
      mode = REC_INPUT_MODE_DTMF_SPEECH;
    else if (value == L"voice")
      mode = REC_INPUT_MODE_SPEECH;
    else if (value == L"dtmf")
      mode = REC_INPUT_MODE_DTMF;
    else {
      log.StartDiagnostic(0) << L"GrammarManager::GetRecProperties - "
        L"Unable to set " << REC_INPUT_MODES << L" from value \"" << value <<
        L"\".";
      log.EndDiagnostic();
    }
  }
  AddParamValue(m, REC_INPUT_MODES, mode);

  // (2.11) Timeout settings
  if (timeout == -1) {
    j = props.GetProperty(PROP_TIMEOUT);
    if (j != NULL)
      PropertyList::ConvertTimeToMilliseconds(log, j, timeout);
  }

  if (timeout != -1) {
    AddParamValue(m, REC_DTMF_TIMEOUT, timeout);
    AddParamValue(m, REC_TIMEOUT, timeout);
  }


  // (3) Done

  return m.Release();
}