📄 vxml.cxx
字号:
opts.SetCallingToken(callingCallToken ); opts.SetDestinationDNR(dest); opts.SetSourceDNR(source); opts.SetTimeout(connectTimeout); opts.SetBridge(bridge); DoTransfer(opts); // Wait for the transfer result signal transferSync.Wait(); return TRUE;}void PVXMLSession::OnTransfer(const PVXMLTransferResult & args){ // transfer has ended, save result SetVar(args.GetName(), args); // Signal transfer initiator that the transfer has ended and the VXML can // continue transferSync.Signal();}BOOL PVXMLSession::TraverseIf(){ // If 'cond' parameter evaluates to true, enter child entities, else // go to next element. PString condition = ((PXMLElement*)currentNode)->GetAttribute("cond"); // Find comparison type PINDEX location = condition.Find("=="); BOOL isEqual = (location < condition.GetSize()); if (isEqual) { // Find var name PString varname = condition.Left(location); // Find value, skip '=' signs PString cond_value = condition.Right(condition.GetSize() - (location + 3)); // check if var value equals value from condition and if not skip child elements PString value = GetVar(varname); if (cond_value == value) { PTRACE( 3, "VXMLSess\t\tCondition matched \"" << condition << "\"" ); } else { PTRACE( 3, "VXMLSess\t\tCondition \"" << condition << "\"did not match, " << varname << " == " << value ); if (currentNode->IsElement()) { PXMLElement* element = (PXMLElement*) currentNode; if (element->HasSubObjects()) { // Step to last child element (really last element is NULL?) currentNode = element->GetElement(element->GetSize() - 1); } } } } else { PTRACE( 1, "\tPVXMLSession, <if> element contains condition with operator other than ==, not implemented" ); return FALSE; } return TRUE;}BOOL PVXMLSession::TraverseExit(){ currentNode = NULL; forceEnd = TRUE; waitForEvent.Signal(); return TRUE;}BOOL PVXMLSession::TraverseSubmit(){ BOOL result = FALSE; // Do HTTP client stuff here // Find out what to submit, for now, only support a WAV file PXMLElement * element = (PXMLElement *)currentNode; if (!element->HasAttribute("namelist")){ PTRACE(1, "VXMLSess\t<submit> does not contain \"namelist\" parameter"); return FALSE; } PString name = element->GetAttribute("namelist"); if (name.Find(" ") < name.GetSize()) { PTRACE(1, "VXMLSess\t<submit> does not support more than one value in \"namelist\" parameter"); return FALSE; } if (!element->HasAttribute("next")) { PTRACE(1, "VXMLSess\t<submit> does not contain \"next\" parameter"); return FALSE; } PString url = element->GetAttribute("next"); if (url.Find( "http://" ) > url.GetSize()) { PTRACE(1, "VXMLSess\t<submit> needs a full url as the \"next\" parameter"); return FALSE; } if (!(GetVar(name + ".type") == "audio/x-wav" )) { PTRACE(1, "VXMLSess\t<submit> does not (yet) support submissions of types other than \"audio/x-wav\""); return FALSE; } PString fileName = GetVar(name + ".filename"); if (!(element->HasAttribute("method"))) { PTRACE(1, "VXMLSess\t<submit> does not (yet) support default method type \"get\""); return FALSE; } if ( !PFile::Exists(fileName )) { PTRACE(1, "VXMLSess\t<submit> cannot find file " << fileName); return FALSE; } PString fileNameOnly; int pos = fileName.FindLast( "/" ); if (pos < fileName.GetLength()) { fileNameOnly = fileName.Right( ( fileName.GetLength() - pos ) - 1 ); } else { pos = fileName.FindLast("\\"); if (pos < fileName.GetSize()) { fileNameOnly = fileName.Right((fileName.GetLength() - pos) - 1); } else { fileNameOnly = fileName; } } PHTTPClient client; PMIMEInfo sendMIME, replyMIME; if (element->GetAttribute("method") *= "post") { // 1 2 3 4123 PString boundary = "--------012345678901234567890123458VXML"; sendMIME.SetAt( PHTTP::ContentTypeTag, "multipart/form-data; boundary=" + boundary); sendMIME.SetAt( PHTTP::UserAgentTag, "PVXML TraverseSubmit" ); sendMIME.SetAt( "Accept", "text/html" ); // After this all boundaries have a "--" prepended boundary = "--" + boundary; // Create the mime header // First set the primary boundary PString mimeHeader = boundary + "\r\n"; // Add content disposition mimeHeader += "Content-Disposition: form-data; name=\"voicemail\"; filename=\"" + fileNameOnly + "\"\r\n"; // Add content type mimeHeader += "Content-Type: audio/wav\r\n\r\n"; // Create the footer and add the closing of the content with a CR/LF PString mimeFooter = "\r\n"; // Copy the header, buffer and footer together in one PString // Load the WAV file into memory PFile file( fileName, PFile::ReadOnly ); int size = file.GetLength(); PString mimeThing; // Make PHP happy? // Anyway, this shows how to add more variables, for when namelist containes more elements PString mimeMaxFileSize = boundary + "\r\nContent-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n3000000\r\n"; // Finally close the body with the boundary again, but also add "--" // to show this is the final boundary boundary = boundary + "--"; mimeFooter += boundary + "\r\n"; mimeHeader = mimeMaxFileSize + mimeHeader; mimeThing.SetSize( mimeHeader.GetSize() + size + mimeFooter.GetSize() ); // Copy the header to the result memcpy( mimeThing.GetPointer(), mimeHeader.GetPointer(), mimeHeader.GetLength()); // Copy the contents of the file to the mime result file.Read( mimeThing.GetPointer() + mimeHeader.GetLength(), size ); // Copy the footer to the result memcpy( mimeThing.GetPointer() + mimeHeader.GetLength() + size, mimeFooter.GetPointer(), mimeFooter.GetLength()); // Send the POST request to the server result = client.PostData( url, sendMIME, mimeThing, replyMIME ); // TODO, Later: // Remove file? // Load reply from server as new VXML docuemnt ala <goto> } else { if (element->GetAttribute("method") != "get") { PTRACE(1, "VXMLSess\t<submit> does not (yet) support method type \"" << element->GetAttribute( "method" ) << "\""); return FALSE; } PString getURL = url + "?" + name + "=" + GetVar( name ); client.GetDocument( url, sendMIME, replyMIME ); // TODO, Later: // Load reply from server as new VXML document ala <goto> } if (!result) { PTRACE( 1, "VXMLSess\t<submit> to server failed with " << client.GetLastResponseCode() << " " << client.GetLastResponseInfo() ); } return result;}BOOL PVXMLSession::TraverseProperty(){ PXMLElement* element = (PXMLElement *) currentNode; if (element->HasAttribute("name")) SetVar(element->GetAttribute("name"), element->GetAttribute("value")); return TRUE;}BOOL PVXMLSession::TraverseMenu(){ BOOL result = FALSE; PVXMLGrammar * newGrammar = new PVXMLDigitsGrammar((PXMLElement*) currentNode, 1, 1, "" ); LoadGrammar(newGrammar); result = TRUE; return result;}BOOL PVXMLSession::TraverseChoice(const PString & grammarResult){ // Iterate over all choice elements starting at currentnode BOOL result = FALSE; PXMLElement* element = (PXMLElement *) currentNode; // Current node is a choice element PString dtmf = element->GetAttribute( "dtmf" ); if (dtmf.IsEmpty()) dtmf = PString(PString::Unsigned, defaultDTMF); // Check if DTMF value for grammarResult matches the DTMF value for the choice if (dtmf == grammarResult) { // Find the form at next parameter PString formID = element->GetAttribute( "next" ); PTRACE(2, "VXMLsess\tFound form id " << formID ); if (!formID.IsEmpty()) { formID = formID.Right( formID.GetLength() - 1 ); currentNode = FindForm( formID ); if (currentNode != NULL) result = TRUE; } } return result;}BOOL PVXMLSession::TraverseVar(){ BOOL result = FALSE; PXMLElement* element = (PXMLElement *) currentNode; PString name = element->GetAttribute( "name" ); PString expr = element->GetAttribute( "expr" ); if (name.IsEmpty() || expr.IsEmpty()) { PTRACE( 1, "VXMLSess\t<var> has a problem with its parameters, name=\"" << name << "\", expr=\"" << expr << "\"" ); } else { SetVar(name, expr); result = TRUE; } return result;}void PVXMLSession::OnEndRecording(const PString & /*channelName*/){ //SetVar(channelName + ".size", PString(incomingChannel->GetWAVFile()->GetDataLength() ) ); //SetVar(channelName + ".type", "audio/x-wav" ); //SetVar(channelName + ".filename", incomingChannel->GetWAVFile()->GetName() );}void PVXMLSession::Trigger(){ waitForEvent.Signal();}/////////////////////////////////////////////////////////////////////////////////////////PVXMLGrammar::PVXMLGrammar(PXMLElement * _field) : field(_field), state(PVXMLGrammar::NOINPUT){}//////////////////////////////////////////////////////////////////PVXMLMenuGrammar::PVXMLMenuGrammar(PXMLElement * _field) : PVXMLGrammar(_field){}//////////////////////////////////////////////////////////////////PVXMLDigitsGrammar::PVXMLDigitsGrammar(PXMLElement * _field, PINDEX _minDigits, PINDEX _maxDigits, PString _terminators) : PVXMLGrammar(_field), minDigits(_minDigits), maxDigits(_maxDigits), terminators(_terminators){ PAssert(_minDigits <= _maxDigits, "Error - invalid grammar parameter");}BOOL PVXMLDigitsGrammar::OnUserInput(const char ch){ // Ignore any other keys if we've already filled the grammar if (state == PVXMLGrammar::FILLED || state == PVXMLGrammar::NOMATCH) return TRUE; // is this char the terminator? if (terminators.Find(ch) != P_MAX_INDEX) { state = (value.GetLength() >= minDigits && value.GetLength() <= maxDigits) ? PVXMLGrammar::FILLED : PVXMLGrammar::NOMATCH; return TRUE; } // Otherwise add to the grammar and check to see if we're done value += ch; if (value.GetLength() == maxDigits) { state = PVXMLGrammar::FILLED; // the grammar is filled! return TRUE; } return FALSE;}void PVXMLDigitsGrammar::Stop(){ // Stopping recognition here may change the state if something was // recognized but it didn't fill the number of digits requested if (!value.IsEmpty()) state = PVXMLGrammar::NOMATCH; // otherwise the state will stay as NOINPUT}//////////////////////////////////////////////////////////////////PVXMLChannel::PVXMLChannel(unsigned _frameDelay, PINDEX frameSize) : PDelayChannel(DelayReadsAndWrites, _frameDelay, frameSize){ vxmlInterface = NULL; sampleFrequency = 8000; closed = FALSE; recording = FALSE; recordable = NULL; playing = FALSE; silentCount = 20; // wait 20 frames before playing the OGM paused = FALSE;}BOOL PVXMLChannel::Open(PVXMLChannelInterface * _vxmlInterface){ vxmlInterface = _vxmlInterface; return TRUE;}PVXMLChannel::~PVXMLChannel(){ EndRecording();}BOOL PVXMLChannel::IsOpen() const{ return !closed;}BOOL PVXMLChannel::Close(){ closed = TRUE; PDelayChannel::Close(); return TRUE; }PString PVXMLChannel::AdjustWavFilename(const PString & ofn){ if (wavFilePrefix.IsEmpty()) return ofn; PString fn = ofn; // add in suffix required for channel format, if any PINDEX pos = ofn.FindLast('.'); if (pos == P_MAX_INDEX) { if (fn.Right(wavFilePrefix.GetLength()) != wavFilePrefix) fn += wavFilePrefix; } else { PString basename = ofn.Left(pos); PString ext = ofn.Mid(pos+1); if (basename.Right(wavFilePrefix.GetLength()) != wavFilePrefix) basename += wavFilePrefix; fn = basename + "." + ext; } return fn;}PWAVFile * PVXMLChannel::CreateWAVFile(const PFilePath & fn, BOOL recording){ PWAVFile * wav = PWAVFile::format(mediaFormat); if (wav == NULL) { PTRACE(1, "VXML\tWAV file format " << mediaFormat << " not known"); return NULL; } wav->SetAutoconvert(); if (!wav->Open(AdjustWavFilename(fn), recording ? PFile::WriteOnly : PFile::ReadOnly, PFile::ModeDefault)) PTRACE(1, "VXML\tCould not open WAV file " << wav->GetName()); else if (recording) { wav->SetChannels(1); wav->SetSampleRate(8000); wav->SetSampleSize(16); return wav; } else if (!wav->IsValid()) PTRACE(1, "VXML\tWAV file header invalid for " << wav->GetName()); else if (wav->GetSampleRate() != sampleFrequency) PTRACE(1, "VXML\tWAV file has unsupported sample frequency " << wav->GetSampleRate()); else if (wav->GetChannels() != 1) PTRACE(1, "VXML\tWAV file has unsupported channel count " << wav->GetChannels()); else { wav->SetAutoconvert(); /// enable autoconvert PTRACE(4, "VXML\tOpened WAV file " << wav->GetName()); return wav; } delete wav; return NULL;}BOOL PVXMLChannel::Write(const void * buf, PINDEX len){ if (closed) return FALSE; channelWriteMutex.Wait(); // let the recordable do silence det
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -