]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_httapi.c -- HT-TAPI Hypertext Telephony API (Twillio FreeSWITCH style)
authorAnthony Minessale <anthm@freeswitch.org>
Thu, 5 Jan 2012 21:17:40 +0000 (15:17 -0600)
committerAnthony Minessale <anthm@freeswitch.org>
Thu, 5 Jan 2012 21:17:59 +0000 (15:17 -0600)
12 files changed:
build/modules.conf.in
conf/autoload_configs/httapi.conf.xml [new file with mode: 0644]
conf/autoload_configs/modules.conf.xml
src/mod/applications/mod_httapi/examples/perl/dialer.cgi [new file with mode: 0755]
src/mod/applications/mod_httapi/examples/perl/ext_pin.cgi [new file with mode: 0755]
src/mod/applications/mod_httapi/examples/perl/record_name.cgi [new file with mode: 0755]
src/mod/applications/mod_httapi/examples/perl/speech.cgi [new file with mode: 0755]
src/mod/applications/mod_httapi/httapi.conf.xml [new file with mode: 0644]
src/mod/applications/mod_httapi/mod_httapi.2008.vcproj [new file with mode: 0644]
src/mod/applications/mod_httapi/mod_httapi.2010.vcxproj [new file with mode: 0644]
src/mod/applications/mod_httapi/mod_httapi.c [new file with mode: 0644]
src/mod/applications/mod_httapi/mod_httapi_doc.txt [new file with mode: 0644]

index 8682fb52aa762e569f912b07f38d03c03247d6ff..7b4d4ee04faa39ddb28410a27e9685e1ddaae007 100644 (file)
@@ -44,6 +44,7 @@ applications/mod_valet_parking
 #applications/mod_abstraction
 #applications/mod_esl
 applications/mod_sms
+applications/mod_httapi
 codecs/mod_g723_1
 codecs/mod_amr
 #codecs/mod_amrwb
diff --git a/conf/autoload_configs/httapi.conf.xml b/conf/autoload_configs/httapi.conf.xml
new file mode 100644 (file)
index 0000000..22f23d1
--- /dev/null
@@ -0,0 +1,87 @@
+<configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
+  <settings>
+    <!-- print xml on the consol -->
+    <param name="debug" value="true"/>
+    <!-- time to keep audio files when discoverd they were deleted from the http server -->
+    <param name="file-not-found-expires" value="300"/>
+    <!-- how often to re-check the server to make sure the remote file has not changed -->
+    <param name="file-cache-ttl" value="300"/>
+  </settings>
+  <profiles>
+    <profile name="default">
+
+      <!-- default params for conference action tags -->
+      <conference>
+       <param name="default-profile" value="default"/>
+      </conference>
+
+      <!-- default params for dial action tags -->
+      <dial>
+       <param name="context" value="default"/>
+       <param name="dialplan" value="XML"/>
+      </dial>
+
+      <!-- permissions -->
+      <permissions>
+       <!-- <permission name="all" value="true"/> -->
+       <!--<permission name="none" value="true"/> -->
+       <permission name="set-params" value="true"/>
+       <permission name="set-vars" value="false"/>
+       <permission name="extended-data" value="false"/>
+       <permission name="execute-apps" value="false"/>
+       <permission name="expand-vars-in-tag-body" value="false"/>
+       <permission name="dial" value="true"/>
+       <permission name="dial-set-context" value="false"/>
+       <permission name="dial-set-dialplan" value="false"/>
+       <permission name="dial-set-cid-name" value="false"/>
+       <permission name="dial-set-cid-number" value="false"/>
+       <permission name="dial-full-originate" value="false"/>
+       <permission name="conference" value="true"/>
+       <permission name="conference-set-profile" value="false"/>
+      </permissions>
+      
+      <params>
+       <!-- default url can be overridden by app data -->
+       <param name="gateway-url" value="http://sidious.freeswitch.org/api/index.cgi" />
+       
+       <!-- set this to provide authentication credentials to the server -->
+       <!--<param name="gateway-credentials" value="muser:mypass"/>-->
+       <!--<param name="auth-scheme" value="basic"/>-->
+
+       <!-- optional: this will enable the CA root certificate check by libcurl to
+            verify that the certificate was issued by a major Certificate Authority.
+            note: default value is disabled. only enable if you want this! -->
+       <!--<param name="enable-cacert-check" value="true"/>-->
+       <!-- optional: verify that the server is actually the one listed in the cert -->
+       <!-- <param name="enable-ssl-verifyhost" value="true"/> -->
+
+       <!-- optional: these options can be used to specify custom SSL certificates
+            to use for HTTPS communications. Either use both options or neither.
+            Specify your public key with 'ssl-cert-path' and the private key with
+            'ssl-key-path'. If your private key has a password, specify it with
+            'ssl-key-password'. -->
+       <!-- <param name="ssl-cert-path" value="$${base_dir}/conf/certs/public_key.pem"/> -->
+       <!-- <param name="ssl-key-path" value="$${base_dir}/conf/certs/private_key.pem"/> -->
+       <!-- <param name="ssl-key-password" value="MyPrivateKeyPassword"/> -->
+       <!-- optional timeout -->
+       <!-- <param name="timeout" value="10"/> -->
+
+       <!-- optional: use a custom CA certificate in PEM format to verify the peer
+            with. This is useful if you are acting as your own certificate authority.
+            note: only makes sense if used in combination with "enable-cacert-check." -->
+       <!-- <param name="ssl-cacert-file" value="$${base_dir}/conf/certs/cacert.pem"/> -->
+
+       <!-- optional: specify the SSL version to force HTTPS to use. Valid options are
+            "SSLv3" and "TLSv1". Otherwise libcurl will auto-negotiate the version. -->
+       <!-- <param name="ssl-version" value="TLSv1"/> -->
+
+       <!-- optional: enables cookies and stores them in the specified file. -->
+       <!-- <param name="cookie-file" value="/tmp/cookie-mod_xml_curl.txt"/> -->
+
+       <!-- one or more of these imply you want to pick the exact variables that are transmitted -->
+       <!--<param name="enable-post-var" value="Unique-ID"/>-->
+      </params>
+
+    </profile>
+  </profiles>
+</configuration>
index de4bc40897244205e222f85f9b112fdd51ce2ad2..e4b8422712aeadcc9b57cf22a2d8d380c74982a3 100644 (file)
@@ -62,6 +62,7 @@
     <load module="mod_valet_parking"/>
     <!--<load module="mod_fsk"/>-->
     <!--<load module="mod_spy"/>-->
+    <load module="mod_httapi"/>
 
     <!-- SNOM Module -->
     <!--<load module="mod_snom"/>-->
diff --git a/src/mod/applications/mod_httapi/examples/perl/dialer.cgi b/src/mod/applications/mod_httapi/examples/perl/dialer.cgi
new file mode 100755 (executable)
index 0000000..ce00289
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+# Object initialization:
+use XML::Simple;
+use CGI;
+use Data::Dumper;
+use XML::Writer;
+
+my $q = CGI->new;
+my $exiting = $q->param("exiting");
+
+if ($exiting) {
+    print $q->header(-type => "text/plain");
+    print "OK";
+    exit();
+}
+
+print $q->header(-type => "text/xml");
+
+
+my $writer = new XML::Writer(OUTPUT => STDOUT, DATA_MODE => 1);
+
+$writer->startTag('document', type => 'xml/freeswitch-httapi');
+
+$writer->startTag('work');
+$writer->emptyTag('pause', milliseconds => "1500");
+$writer->startTag('playback', 
+                 name => digits, 
+                 file => "http://sidious.freeswitch.org/sounds/exten.wav",
+                 'error-file' => "http://sidious.freeswitch.org/sounds/invalid.wav",
+                 'input-timeout' => "5000",
+                 action => "dial:default:XML");
+
+$writer->dataElement("bind", "~\\d+\#", strip => "#");
+$writer->endTag('playback');
+$writer->endTag('work');
+
+
+$writer->endTag('document');
+$writer->end();
+
diff --git a/src/mod/applications/mod_httapi/examples/perl/ext_pin.cgi b/src/mod/applications/mod_httapi/examples/perl/ext_pin.cgi
new file mode 100755 (executable)
index 0000000..0ffbc3f
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+# Object initialization:
+use XML::Simple;
+use CGI;
+use Data::Dumper;
+use XML::Writer;
+
+my $q = CGI->new;
+
+my $exten = $q->param("exten");
+my $pin = $q->param("pin");
+my $exiting = $q->param("exiting");
+
+if ($exiting) {
+    print $q->header(-type => "text/plain");
+    print "OK";
+    exit();
+  }
+
+print $q->header(-type => "text/xml");
+
+my $writer = new XML::Writer(OUTPUT => STDOUT, DATA_MODE => 1);
+
+$writer->startTag('document', type => 'xml/freeswitch-httapi');
+
+$writer->startTag('params');
+if ($exten) {
+  $writer->dataElement("exten", $exten);
+}
+if ($pin) {
+  $writer->dataElement("exten", $pin);
+}
+$writer->endTag('params');
+
+if ($exten && $pin) {
+  $writer->startTag('work');
+  $writer->dataElement("playback", "http://sidious.freeswitch.org/sounds/ext_num.wav");
+  $writer->dataElement("say", $exten, language => "en", type => "name_spelled", method => "pronounced");
+  $writer->emptyTag('pause', milliseconds => "1500");
+  $writer->dataElement("say", $pin, language => "en", type => "name_spelled", method => "pronounced");
+  $writer->emptyTag('hangup');
+  $writer->endTag('work');
+} elsif ($exten) {
+  $writer->startTag('work');
+  $writer->startTag('playback', 
+                   name => "pin", 
+                   file => "http://sidious.freeswitch.org/sounds/pin.wav",
+                   'error-file' => "http://sidious.freeswitch.org/sounds/bad-pin.wav",
+                   'input-timeout' => "5000");
+                  
+
+  $writer->dataElement("bind", "~\\d+\#", strip => "#");
+  $writer->endTag('playback');
+  $writer->endTag('work');
+} else {
+  $writer->startTag('work');
+  $writer->startTag('playback', 
+                   name => "exten", 
+                   file => "http://sidious.freeswitch.org/sounds/exten.wav",
+                   'error-file' => "http://sidious.freeswitch.org/sounds/invalid.wav",
+                   'input-timeout' => "5000");
+
+  $writer->dataElement("bind", "~\\d+\#", strip => "#");
+  $writer->endTag('playback');
+  $writer->endTag('work');
+}
+
+$writer->endTag('document');
+$writer->end();
+
diff --git a/src/mod/applications/mod_httapi/examples/perl/record_name.cgi b/src/mod/applications/mod_httapi/examples/perl/record_name.cgi
new file mode 100755 (executable)
index 0000000..ca5b7ba
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+# Object initialization:
+use XML::Simple;
+use CGI;
+use Data::Dumper;
+use XML::Writer;
+
+my $q = CGI->new;
+my $exiting = $q->param("exiting");
+
+my $file = $q->upload("recorded_file");
+
+if ($file) {
+  open(O, ">/tmp/recording.wav");
+  while(<$file>) {
+    print O $_;
+  }
+  close O;
+
+  print $q->header(-type => "text/plain");
+  print "OK\n";
+  exit();
+}
+
+if ($exiting) {
+  print $q->header(-type => "text/plain");
+  print "OK";
+  exit();
+}
+
+
+print $q->header(-type => "text/xml");
+
+my $writer = new XML::Writer(OUTPUT => STDOUT, DATA_MODE => 1);
+
+$writer->startTag('document', type => 'xml/freeswitch-httapi');
+
+$writer->startTag('work');
+$writer->emptyTag('pause', milliseconds => "1500");
+$writer->emptyTag('playback', file => "http://sidious.freeswitch.org/eg/ivr-say_name.wav");
+$writer->startTag('record', 
+                 name => "recorded_file", 
+                 file => "recording.wav",
+                 'error-file' => "http://sidious.freeswitch.org/sounds/invalid.wav",
+                 'input-timeout' => "5000",
+                 'beep-file', => "tone_stream://%(1000,0,460)");
+                  
+
+$writer->dataElement("bind", "~\\d+\#", strip => "#");
+$writer->endTag('record');
+
+$writer->endTag('work');
+
+
+$writer->endTag('document');
+$writer->end();
+
diff --git a/src/mod/applications/mod_httapi/examples/perl/speech.cgi b/src/mod/applications/mod_httapi/examples/perl/speech.cgi
new file mode 100755 (executable)
index 0000000..9ede418
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+# Object initialization:
+use XML::Simple;
+use CGI;
+use Data::Dumper;
+use XML::Writer;
+
+my $q = CGI->new;
+
+my $result = $q->param("result");
+my $type = $q->param("input_type");
+my $exiting = $q->param("exiting");
+
+if ($exiting) {
+    print $q->header(-type => "text/plain");
+    print "OK";
+    exit();
+}
+
+print $q->header(-type => "text/xml");
+
+
+my $writer = new XML::Writer(OUTPUT => STDOUT, DATA_MODE => 1);
+
+$writer->startTag('document', type => 'xml/freeswitch-httapi');
+
+if ($result) {
+    $writer->startTag('work');
+
+    if ($type eq "dtmf") {
+      $writer->dataElement("say", $result, language => "en", type => "name_spelled", method => "pronounced");
+    }
+
+    $writer->dataElement("log", $result, level => "crit");
+
+    $writer->emptyTag('hangup');
+    $writer->endTag('work');
+} else {
+
+    $writer->startTag('work');
+    $writer->emptyTag('pause', milliseconds => "1500");
+    $writer->startTag('playback',
+                     name => "result",
+                     'asr-engine' => "pocketsphinx", 
+                     'asr-grammar' => "pizza_yesno",
+                     file => "http://sidious.freeswitch.org/sounds/ConfirmDelivery.wav",
+                     'error-file' => "http://sidious.freeswitch.org/sounds/invalid.wav"
+       );
+
+    $writer->dataElement("bind", "~\\d+\#", strip => "#");
+    #$writer->dataElement("bind", "1");
+    #$writer->dataElement("bind", "2");
+    $writer->endTag('playback');
+
+    $writer->endTag('work');
+}
+
+$writer->endTag('document');
+$writer->end();
+
diff --git a/src/mod/applications/mod_httapi/httapi.conf.xml b/src/mod/applications/mod_httapi/httapi.conf.xml
new file mode 100644 (file)
index 0000000..22f23d1
--- /dev/null
@@ -0,0 +1,87 @@
+<configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
+  <settings>
+    <!-- print xml on the consol -->
+    <param name="debug" value="true"/>
+    <!-- time to keep audio files when discoverd they were deleted from the http server -->
+    <param name="file-not-found-expires" value="300"/>
+    <!-- how often to re-check the server to make sure the remote file has not changed -->
+    <param name="file-cache-ttl" value="300"/>
+  </settings>
+  <profiles>
+    <profile name="default">
+
+      <!-- default params for conference action tags -->
+      <conference>
+       <param name="default-profile" value="default"/>
+      </conference>
+
+      <!-- default params for dial action tags -->
+      <dial>
+       <param name="context" value="default"/>
+       <param name="dialplan" value="XML"/>
+      </dial>
+
+      <!-- permissions -->
+      <permissions>
+       <!-- <permission name="all" value="true"/> -->
+       <!--<permission name="none" value="true"/> -->
+       <permission name="set-params" value="true"/>
+       <permission name="set-vars" value="false"/>
+       <permission name="extended-data" value="false"/>
+       <permission name="execute-apps" value="false"/>
+       <permission name="expand-vars-in-tag-body" value="false"/>
+       <permission name="dial" value="true"/>
+       <permission name="dial-set-context" value="false"/>
+       <permission name="dial-set-dialplan" value="false"/>
+       <permission name="dial-set-cid-name" value="false"/>
+       <permission name="dial-set-cid-number" value="false"/>
+       <permission name="dial-full-originate" value="false"/>
+       <permission name="conference" value="true"/>
+       <permission name="conference-set-profile" value="false"/>
+      </permissions>
+      
+      <params>
+       <!-- default url can be overridden by app data -->
+       <param name="gateway-url" value="http://sidious.freeswitch.org/api/index.cgi" />
+       
+       <!-- set this to provide authentication credentials to the server -->
+       <!--<param name="gateway-credentials" value="muser:mypass"/>-->
+       <!--<param name="auth-scheme" value="basic"/>-->
+
+       <!-- optional: this will enable the CA root certificate check by libcurl to
+            verify that the certificate was issued by a major Certificate Authority.
+            note: default value is disabled. only enable if you want this! -->
+       <!--<param name="enable-cacert-check" value="true"/>-->
+       <!-- optional: verify that the server is actually the one listed in the cert -->
+       <!-- <param name="enable-ssl-verifyhost" value="true"/> -->
+
+       <!-- optional: these options can be used to specify custom SSL certificates
+            to use for HTTPS communications. Either use both options or neither.
+            Specify your public key with 'ssl-cert-path' and the private key with
+            'ssl-key-path'. If your private key has a password, specify it with
+            'ssl-key-password'. -->
+       <!-- <param name="ssl-cert-path" value="$${base_dir}/conf/certs/public_key.pem"/> -->
+       <!-- <param name="ssl-key-path" value="$${base_dir}/conf/certs/private_key.pem"/> -->
+       <!-- <param name="ssl-key-password" value="MyPrivateKeyPassword"/> -->
+       <!-- optional timeout -->
+       <!-- <param name="timeout" value="10"/> -->
+
+       <!-- optional: use a custom CA certificate in PEM format to verify the peer
+            with. This is useful if you are acting as your own certificate authority.
+            note: only makes sense if used in combination with "enable-cacert-check." -->
+       <!-- <param name="ssl-cacert-file" value="$${base_dir}/conf/certs/cacert.pem"/> -->
+
+       <!-- optional: specify the SSL version to force HTTPS to use. Valid options are
+            "SSLv3" and "TLSv1". Otherwise libcurl will auto-negotiate the version. -->
+       <!-- <param name="ssl-version" value="TLSv1"/> -->
+
+       <!-- optional: enables cookies and stores them in the specified file. -->
+       <!-- <param name="cookie-file" value="/tmp/cookie-mod_xml_curl.txt"/> -->
+
+       <!-- one or more of these imply you want to pick the exact variables that are transmitted -->
+       <!--<param name="enable-post-var" value="Unique-ID"/>-->
+      </params>
+
+    </profile>
+  </profiles>
+</configuration>
diff --git a/src/mod/applications/mod_httapi/mod_httapi.2008.vcproj b/src/mod/applications/mod_httapi/mod_httapi.2008.vcproj
new file mode 100644 (file)
index 0000000..775ce2b
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9.00"\r
+       Name="mod_httapi"\r
+       ProjectGUID="{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}"\r
+       RootNamespace="mod_httapi"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+               <Platform\r
+                       Name="x64"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Debug|x64"\r
+                       OutputDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                               TargetEnvironment="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="17"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|x64"\r
+                       OutputDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                               TargetEnvironment="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               UsePrecompiledHeader="0"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="17"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <File\r
+                       RelativePath=".\mod_httapi.c"\r
+                       >\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/src/mod/applications/mod_httapi/mod_httapi.2010.vcxproj b/src/mod/applications/mod_httapi/mod_httapi.2010.vcxproj
new file mode 100644 (file)
index 0000000..c178254
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectName>mod_httapi</ProjectName>\r
+    <ProjectGuid>{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}</ProjectGuid>\r
+    <RootNamespace>mod_httapi</RootNamespace>\r
+    <Keyword>Win32Proj</Keyword>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_release.props" />\r
+  </ImportGroup>\r
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="..\..\..\..\w32\module_debug.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup>\r
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</OutDir>\r
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</IntDir>\r
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</OutDir>\r
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <Midl>\r
+      <TargetEnvironment>X64</TargetEnvironment>\r
+    </Midl>\r
+    <ClCompile>\r
+      <PrecompiledHeader>\r
+      </PrecompiledHeader>\r
+    </ClCompile>\r
+    <Link>\r
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>\r
+      <DataExecutionPrevention>\r
+      </DataExecutionPrevention>\r
+      <TargetMachine>MachineX64</TargetMachine>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="mod_httapi.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ProjectReference Include="..\..\..\..\w32\Library\FreeSwitchCore.2010.vcxproj">\r
+      <Project>{202d7a4e-760d-4d0e-afa1-d7459ced30ff}</Project>\r
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>\r
+    </ProjectReference>\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c
new file mode 100644 (file)
index 0000000..989eb29
--- /dev/null
@@ -0,0 +1,2297 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * mod_httapi.c -- HT-TAPI Hypertext Telephony API
+ *
+ */
+#include <switch.h>
+#include <switch_curl.h>
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_httapi_shutdown);
+SWITCH_MODULE_DEFINITION(mod_httapi, mod_httapi_load, mod_httapi_shutdown, NULL);
+
+typedef struct profile_perms_s {
+       switch_byte_t set_params;
+       switch_byte_t set_vars;
+       switch_byte_t extended_data;
+       switch_byte_t execute_apps;
+       switch_byte_t expand_vars_in_tag_body;
+       struct {
+               switch_byte_t enabled;
+               switch_byte_t set_context;
+               switch_byte_t set_dp;
+               switch_byte_t set_cid_name;
+               switch_byte_t set_cid_number;
+               switch_byte_t full_originate;
+       } dial;
+       struct {
+               switch_byte_t enabled;
+               switch_byte_t set_profile;
+       } conference;
+} profile_perms_t;
+
+struct client_s;
+
+typedef struct action_binding_s {
+       char *realm;
+       char *input;
+       char *action;
+       char *error_file;
+       char *match_digits;
+       char *strip;
+       struct client_s *client;
+       struct action_binding_s *parent;
+} action_binding_t;
+
+typedef struct client_profile_s {
+       char *name;
+       char *method;
+       char *url;
+       char *cred;
+       char *bind_local;
+       int disable100continue;
+       uint32_t enable_cacert_check;
+       char *ssl_cert_file;
+       char *ssl_key_file;
+       char *ssl_key_password;
+       char *ssl_version;
+       char *ssl_cacert_file;
+       uint32_t enable_ssl_verifyhost;
+       char *cookie_file;
+       switch_hash_t *vars_map;
+       int auth_scheme;
+       int timeout;
+       profile_perms_t perms;
+
+       struct {
+               char *use_profile;
+       } conference_params;
+
+       struct {
+               char *context;
+               char *dp;
+       } dial_params;
+
+} client_profile_t;
+
+#define HTTAPI_MAX_API_BYTES 1024 * 1024
+#define HTTAPI_MAX_FILE_BYTES 1024 * 1024 * 100
+
+typedef struct client_s {
+       switch_memory_pool_t *pool;
+       int fd;
+       switch_buffer_t *buffer;
+       switch_size_t bytes;
+       switch_size_t max_bytes;
+       switch_event_t *headers;
+       switch_event_t *params;
+       switch_event_t *one_time_params;
+       client_profile_t *profile;
+       switch_core_session_t *session;
+       switch_channel_t *channel;
+       action_binding_t *matching_action_binding;
+       action_binding_t *no_matching_action_binding;
+       struct {
+               char *action;
+               char *name;
+               char *file;
+       } record;
+
+       int err;
+       long code;
+} client_t;
+
+typedef struct hash_node {
+       switch_hash_t *hash;
+       struct hash_node *next;
+} hash_node_t;
+
+static struct {
+       switch_memory_pool_t *pool;
+       hash_node_t *hash_root;
+       hash_node_t *hash_tail;
+       switch_hash_t *profile_hash;
+       switch_hash_t *parse_hash;
+       char cache_path[128];
+       int debug;
+       int not_found_expires;
+       int cache_ttl;
+} globals;
+
+
+/* for apr_pstrcat */
+#define DEFAULT_PREBUFFER_SIZE 1024 * 64
+
+struct http_file_source;
+
+struct http_file_context {
+       int samples;
+       switch_file_handle_t fh;
+       char *cache_file;
+       char *meta_file;
+       char *lock_file;
+       char *metadata;
+       time_t expires;
+       switch_file_t *lock_fd;
+       switch_memory_pool_t *pool;
+};
+
+typedef struct http_file_context http_file_context_t;
+typedef switch_status_t (*tag_parse_t)(const char *tag_name, client_t *client, switch_xml_t tag, const char *body);
+
+static void bind_parser(const char *tag_name, tag_parse_t handler)
+{
+       switch_core_hash_insert(globals.parse_hash, tag_name, (void *)(intptr_t)handler);
+}
+
+
+#define HTTAPI_SYNTAX "[debug_on|debug_off]"
+SWITCH_STANDARD_API(httapi_api_function)
+{
+       if (session) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (zstr(cmd)) {
+               goto usage;
+       }
+
+       if (!strcasecmp(cmd, "debug_on")) {
+               globals.debug = 1;
+       } else if (!strcasecmp(cmd, "debug_off")) {
+               globals.debug = 0;
+       } else {
+               goto usage;
+       }
+
+       stream->write_function(stream, "OK\n");
+       return SWITCH_STATUS_SUCCESS;
+
+  usage:
+       stream->write_function(stream, "USAGE: %s\n", HTTAPI_SYNTAX);
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match)
+{
+       action_binding_t *action_binding = (action_binding_t *) match->user_data;
+       
+       action_binding->client->matching_action_binding = action_binding;
+       action_binding->match_digits = switch_core_strdup(action_binding->client->pool, match->match_digits);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t digit_nomatch_action_callback(switch_ivr_dmachine_match_t *match)
+{
+       action_binding_t *action_binding = (action_binding_t *) match->user_data;
+
+       action_binding->client->no_matching_action_binding = action_binding;
+       
+       return SWITCH_STATUS_BREAK;
+}
+
+static switch_status_t parse_break(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       return SWITCH_STATUS_FALSE;
+}
+
+
+static void console_log(const char *level_str, const char *msg)
+{
+       switch_log_level_t level = SWITCH_LOG_DEBUG;
+       if (level_str) {
+               level = switch_log_str2level(level_str);
+               if (level == SWITCH_LOG_INVALID) {
+                       level = SWITCH_LOG_DEBUG;
+               }
+       }
+       switch_log_printf(SWITCH_CHANNEL_LOG, level, "%s", switch_str_nil(msg));
+}
+
+static void console_clean_log(const char *level_str, const char *msg)
+{
+       switch_log_level_t level = SWITCH_LOG_DEBUG;
+       if (level_str) {
+               level = switch_log_str2level(level_str);
+               if (level == SWITCH_LOG_INVALID) {
+                       level = SWITCH_LOG_DEBUG;
+               }
+       }
+       switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, level, "%s", switch_str_nil(msg));
+}
+
+static switch_status_t parse_log(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *level = switch_xml_attr(tag, "level");
+       const char *clean = switch_xml_attr(tag, "clean");
+       
+       if (switch_true(clean)) {
+               console_clean_log(level, body);
+       } else {
+               console_log(level, body);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_playback(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *file = switch_xml_attr(tag, "file");
+       const char *name = switch_xml_attr(tag, "name");
+       const char *error_file = switch_xml_attr(tag, "error-file");
+       const char *action = switch_xml_attr(tag, "action");
+       const char *digit_timeout_ = switch_xml_attr(tag, "digit-timeout");
+       const char *input_timeout_ = switch_xml_attr(tag, "input-timeout");
+       const char *tts_engine = NULL;
+       const char *tts_voice = NULL;
+       char *loops_ = (char *) switch_xml_attr(tag, "loops");
+       int loops = 0;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       switch_ivr_dmachine_t *dmachine = NULL;
+       switch_input_args_t *args = NULL, myargs = { 0 }, nullargs = { 0 };
+       long digit_timeout = 1500;
+       long input_timeout = 5000;
+       long tmp;
+       switch_xml_t bind = NULL;
+       int submit = 0;
+       int input = 0;
+       int speak = 0, say = 0, pause = 0, play = 0, speech = 0;
+       char *sub_action = NULL;
+       action_binding_t *top_action_binding = NULL;
+       const char *say_lang = NULL;
+       const char *say_type = NULL;
+       const char *say_method = NULL;
+       const char *say_gender = NULL;
+       const char *sp_engine = NULL;
+       const char *sp_grammar = NULL;
+
+       if (!strcasecmp(tag_name, "say")) {
+               say_lang = switch_xml_attr(tag, "language");
+               say_type = switch_xml_attr(tag, "type");
+               say_method = switch_xml_attr(tag, "method");
+               say_gender = switch_xml_attr(tag, "gender");
+               
+               if (zstr(say_lang) || zstr(say_type) || zstr(say_method) || zstr(body)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "speak: missing required attributes or text! (language) (type) (method) \n");
+                       return SWITCH_STATUS_FALSE;
+               }
+               
+               say = 1;
+               
+       } else if (!strcasecmp(tag_name, "speak")) {
+               tts_engine = switch_xml_attr(tag, "engine");
+               tts_voice = switch_xml_attr(tag, "voice");
+
+               if (zstr(tts_engine)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "speak: missing engine attribute!\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+               speak = 1;
+       } else if (!strcasecmp(tag_name, "pause")) {
+               const char *ms_ = switch_xml_attr(tag, "milliseconds");
+               pause = atoi(ms_);
+               if (pause < 0) pause = 1000;
+       } else if (!strcasecmp(tag_name, "playback")) {
+               sp_engine = switch_xml_attr(tag, "asr-engine");
+               sp_grammar = switch_xml_attr(tag, "asr-grammar");
+
+               if (sp_grammar && sp_engine) {
+                       speech = 1;
+               } else {
+                       play = 1;
+               }
+       }
+
+
+       if (zstr(name)) name = "dialed_digits";
+
+       if (loops_) {
+               loops = atoi(loops_);
+
+               if (loops < 0) {
+                       loops = -1;
+               }
+       }
+
+       if (digit_timeout_) {
+               tmp = atol(digit_timeout_);
+
+               if (tmp > 0) {
+                       digit_timeout = tmp;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid digit timeout [%s]\n", digit_timeout_);
+               }
+       }
+       
+       if (input_timeout_) {
+               tmp = atol(input_timeout_);
+
+               if (tmp > 0) {
+                       input_timeout = tmp;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid input timeout [%s]\n", input_timeout_);
+               }
+       }
+
+       if ((bind = switch_xml_child(tag, "bind"))) {
+               action_binding_t *action_binding;
+               const char *realm = "default";
+       
+
+               input++;
+
+               top_action_binding = switch_core_session_alloc(client->session, sizeof(*action_binding));
+               top_action_binding->client = client;
+               top_action_binding->action = (char *) action;
+               top_action_binding->error_file = (char *)error_file;
+
+               switch_ivr_dmachine_create(&dmachine, "HTTAPI", NULL, digit_timeout, 0, 
+                                                                  NULL, digit_nomatch_action_callback, top_action_binding);
+               
+               while(bind) {
+                       action_binding = switch_core_session_alloc(client->session, sizeof(*action_binding));
+                       action_binding->realm = (char *) realm;
+                       action_binding->input = bind->txt;
+                       action_binding->client = client;
+                       action_binding->action = (char *) switch_xml_attr(bind, "action");
+                       action_binding->strip = (char *) switch_xml_attr(bind, "strip");
+                       action_binding->error_file = (char *) error_file;
+                       action_binding->parent = top_action_binding;
+                       
+                       switch_ivr_dmachine_bind(dmachine, action_binding->realm, action_binding->input, 0, digit_action_callback, action_binding);
+                       bind = bind->next;
+               }
+               
+               switch_ivr_dmachine_set_realm(dmachine, realm);
+               myargs.dmachine = dmachine;
+               args = &myargs;
+       }
+
+       if (zstr(file) && !input) {
+               file = body;
+       }
+
+       if (zstr(file) && !(speak || say || pause)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "missing file attribute!\n");
+               switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       do {
+               if (speak) {
+                       status = switch_ivr_speak_text(client->session, tts_engine, tts_voice, (char *)body, args);
+               } else if (say) {
+                       status = switch_ivr_say(client->session, body, say_lang, say_type, say_method, say_gender, args);
+               } else if (play) {
+                       status = switch_ivr_play_file(client->session, NULL, file, args);
+               } else if (speech) {
+                       char *result = NULL;
+
+                       status = switch_ivr_play_and_detect_speech(client->session, file, sp_engine, sp_grammar, &result, input_timeout, args);
+                       
+                       if (!zstr(result)) {
+                               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, name, result);
+                               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "input_type", "detected_speech");
+                               submit = 1;
+                               break;
+                       }
+
+                       input_timeout = 0;
+               } else if (pause) {
+                       input_timeout = pause;
+                       status = SWITCH_STATUS_SUCCESS;
+               }
+
+               if (!switch_channel_ready(client->channel)) {
+                       break;
+               }
+
+               if (!input && !pause) {
+                       status = switch_channel_ready(client->channel) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
+                       submit = 1;
+                       break;
+               }
+
+               if (input_timeout && status == SWITCH_STATUS_SUCCESS) {
+                       if ((status = switch_ivr_sleep(client->session, input_timeout, SWITCH_TRUE, args)) == SWITCH_STATUS_SUCCESS) {
+                               status = SWITCH_STATUS_BREAK;
+                       }
+               }
+
+               if (status == SWITCH_STATUS_BREAK) {
+                       if (error_file) {
+                               switch_ivr_play_file(client->session, NULL, error_file, &nullargs);
+                               status = SWITCH_STATUS_SUCCESS;
+                       }
+               } else if (status == SWITCH_STATUS_FOUND) {
+                       status = SWITCH_STATUS_SUCCESS;
+                       submit = 1;
+                       break;
+               } else if (status != SWITCH_STATUS_SUCCESS) {
+                       break;
+               }
+       } while (loops-- > 0);
+       
+
+       if (submit) {
+               if (client->matching_action_binding) {
+                       if (client->matching_action_binding->match_digits) {
+
+                               if (client->matching_action_binding->strip) {
+                                       char *pp, *p;
+
+                                       for(pp = client->matching_action_binding->strip; pp && *pp; pp++) {
+                                               if ((p = strchr(client->matching_action_binding->match_digits, *pp))) {
+                                                       *p = '\0';
+                                               }
+                                       }
+                               }
+                               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, name, client->matching_action_binding->match_digits);
+                               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "input_type", "dtmf");
+                       }
+                       
+                       if (client->matching_action_binding->action) {
+                               sub_action = client->matching_action_binding->action;
+                       } else if (client->matching_action_binding->parent && client->matching_action_binding->parent->action) {
+                               sub_action = client->matching_action_binding->parent->action;
+                       }
+               }
+               
+               if (!sub_action && top_action_binding && top_action_binding->action) {
+                       sub_action = top_action_binding->action;
+               }
+               
+               if (sub_action && client->matching_action_binding && client->matching_action_binding->match_digits) {
+                       if (!strncasecmp(sub_action, "dial:", 5)) {
+                               if (client->profile->perms.dial.set_context) {
+                                       char *context = switch_core_session_strdup(client->session, sub_action + 5);
+                                       char *dp;
+                                       
+                                       if ((dp = strchr(context, ':'))) {
+                                               *dp++ = '\0';
+                                               if (!client->profile->perms.dial.set_dp) {
+                                                       dp = NULL;
+                                               }
+                                       }
+
+                                       switch_ivr_session_transfer(client->session, client->matching_action_binding->match_digits, dp, context);
+                                       status = SWITCH_STATUS_FALSE;
+                               }
+                       } else {
+                               switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, "url", sub_action);
+                       }
+               }
+       }
+
+       if (dmachine) {
+               switch_ivr_dmachine_destroy(&dmachine);
+       }
+
+       return status;
+}
+
+static switch_status_t parse_conference(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       char *str;
+       char *dup, *p;
+       const char *profile_name = switch_xml_attr(tag, "profile");
+       
+       if (!client->profile->perms.conference.enabled) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Permission Denied!\n");
+               switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       dup = switch_core_session_strdup(client->session, body);
+
+       if ((p = strchr(dup, '@'))) {
+               *p = '\0';
+       }
+
+       if (zstr(profile_name) || !client->profile->perms.conference.set_profile) {
+               profile_name = client->profile->conference_params.use_profile;
+       }
+
+       str = switch_core_session_sprintf(client->session, "%s@%s", dup, profile_name);
+       switch_core_session_execute_application(client->session, "conference", str);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_dial(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *context = NULL; 
+       const char *dp = NULL;
+       const char *cid_name = NULL;
+       const char *cid_number = NULL;
+
+       if (!client->profile->perms.dial.enabled) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Permission Denied!\n");
+               switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (client->profile->perms.dial.set_context) {
+               context = switch_xml_attr(tag, "context");
+       }
+
+       if (client->profile->perms.dial.set_dp) {
+               dp = switch_xml_attr(tag, "dialplan");
+       }
+
+       if (client->profile->perms.dial.set_cid_name) {
+               cid_name = switch_xml_attr(tag, "caller-id-name");
+       }
+
+       if (client->profile->perms.dial.set_cid_number) {
+               cid_number = switch_xml_attr(tag, "caller-id-number");
+       }
+
+       if (client->profile->perms.dial.full_originate && strchr(body, '/')) {
+               char *str;
+
+               if (!cid_name) {
+                       cid_name = switch_channel_get_variable(client->channel, "caller_id_name");
+               }
+               if (!cid_number) {
+                       cid_number = switch_channel_get_variable(client->channel, "caller_id_number");
+               }
+
+               str = switch_core_session_sprintf(client->session, 
+                                                                                 "{origination_caller_id_name='%s',origination_caller_id_number='%s'}%s", cid_name, cid_number, body);
+
+               switch_core_session_execute_application(client->session, "bridge", str);
+       } else {
+               switch_ivr_session_transfer(client->session, body, dp, context);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_sms(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       switch_event_t *event;
+       const char *from_proto = "httapi";
+       const char *to_proto = "sip";
+       const char *to = switch_xml_attr(tag, "to");
+
+       if (to && switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", from_proto);
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_proto", to_proto);
+
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", switch_channel_get_variable(client->channel, "caller_id_number"));
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to);
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain");
+               
+               if (body) {
+                       switch_event_add_body(event, "%s", body);
+               }
+
+               switch_core_chat_send(to_proto, event);
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing 'to' Attribute!\n");
+               return SWITCH_STATUS_FALSE;
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_execute(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *app_name = switch_xml_attr(tag, "application");
+
+       if (!client->profile->perms.execute_apps) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Permission Denied!\n");
+               switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (zstr(app_name)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid app\n");
+               switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+               return SWITCH_STATUS_FALSE;
+       } else {
+               switch_core_session_execute_application(client->session, app_name, body);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_hangup(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *cause_str = switch_xml_attr(tag, "cause");
+       switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
+
+       if (zstr(cause_str)) {
+               cause_str = body;
+       }
+
+       if (!zstr(cause_str)) {
+               cause = switch_channel_str2cause(cause_str);
+       }
+
+       switch_channel_hangup(client->channel, cause);
+
+       return SWITCH_STATUS_FALSE;
+}
+
+static switch_status_t parse_record_call(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *limit_ = switch_xml_attr(tag, "limit");
+       const char *name = switch_xml_attr(tag, "name");
+       const char *action = switch_xml_attr(tag, "action");
+       int limit = 0;
+
+       if (client->record.file) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (zstr(name)) name = "recorded_file";
+
+       client->record.action = (char *) action;
+       client->record.name = (char *)name;
+       client->record.file = switch_core_session_sprintf(client->session, "%s%s%s.wav", 
+                                                                                                         SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(client->session));
+       
+       if (limit_) {
+               limit = atoi(limit_);
+               if (limit < 0) limit = 0;
+       }
+       
+
+       switch_ivr_record_session(client->session, client->record.file, limit, NULL);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t parse_record(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
+{
+       const char *file = switch_xml_attr(tag, "file");
+       const char *name = switch_xml_attr(tag, "name");
+       const char *error_file = switch_xml_attr(tag, "error-file");
+       const char *beep_file = switch_xml_attr(tag, "beep-file");
+       const char *action = switch_xml_attr(tag, "action");
+       const char *sub_action = NULL;
+       const char *digit_timeout_ = switch_xml_attr(tag, "digit-timeout");
+       char *loops_ = (char *) switch_xml_attr(tag, "loops");
+       int loops = 0;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       switch_ivr_dmachine_t *dmachine = NULL;
+       switch_input_args_t *args = NULL, myargs = { 0 };
+       long digit_timeout = 1500;
+       long tmp;
+       int thresh = 200;
+       int silence_hits = 2;
+       int record_limit = 0;
+       char *tmp_record_path = NULL;
+       const char *v;
+       int rtmp;
+       char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+       char *fname = NULL;
+       char *p, *ext = "wav";
+       switch_xml_t bind;
+       action_binding_t *top_action_binding = NULL;
+
+       switch_uuid_str(uuid_str, sizeof(uuid_str));
+
+       if (zstr(name)) name = "attached_file";
+
+       if (zstr(file)) {
+               return SWITCH_STATUS_FALSE;
+       }
+       
+       fname = switch_core_strdup(client->pool, file);
+
+       for(p = fname; p && *p; p++) {
+               if (*p == '/' || *p == '\\' || *p == '`') {
+                       *p = '_';
+               }
+       }
+
+       if ((p = strrchr(fname, '.'))) {
+               *p++ = '\0';
+               ext = p;
+       }
+
+       for(p = fname; p && *p; p++) {
+               if (*p == '.') {
+                       *p = '_';
+               }
+       }
+               
+       tmp_record_path = switch_core_sprintf(client->pool, "%s%s%s_%s.%s", 
+                                                                                 SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, uuid_str, fname, ext);
+
+       if ((v = switch_xml_attr(tag, "limit"))) {
+               if ((rtmp = atoi(v)) > -1) {
+                       record_limit = rtmp;
+               }
+       }
+
+       if ((v = switch_xml_attr(tag, "silence-hits"))) {
+               if ((rtmp = atoi(v)) > -1) {
+                       silence_hits = rtmp;
+               }
+       }
+
+       if ((v = switch_xml_attr(tag, "threshold"))) {
+               if ((rtmp = atoi(v)) > -1) {
+                       thresh = rtmp;
+               }
+       }
+
+       if (loops_) {
+               loops = atoi(loops_);
+
+               if (loops < 0) {
+                       loops = -1;
+               }
+       }
+
+       if (digit_timeout_) {
+               tmp = atol(digit_timeout_);
+
+               if (tmp > 0) {
+                       digit_timeout = tmp;
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid digit timeout [%s]\n", digit_timeout_);
+               }
+       }
+       
+       if ((bind = switch_xml_child(tag, "bind"))) {
+               action_binding_t *action_binding;
+               const char *realm = "default";
+               
+               top_action_binding = switch_core_session_alloc(client->session, sizeof(*action_binding));
+               top_action_binding->client = client;
+               top_action_binding->action = (char *) action;
+               top_action_binding->error_file = (char *)error_file;
+
+               switch_ivr_dmachine_create(&dmachine, "HTTAPI", NULL, digit_timeout, 0, 
+                                                                  NULL, digit_nomatch_action_callback, top_action_binding);
+               
+               while(bind) {
+                       action_binding = switch_core_session_alloc(client->session, sizeof(*action_binding));
+                       action_binding->realm = (char *) realm;
+                       action_binding->input = bind->txt;
+                       action_binding->client = client;
+                       action_binding->action = (char *) switch_xml_attr(bind, "action");
+                       action_binding->error_file = (char *) error_file;
+                       action_binding->parent = top_action_binding;
+                       
+                       switch_ivr_dmachine_bind(dmachine, action_binding->realm, action_binding->input, 0, digit_action_callback, action_binding);
+                       bind = bind->next;
+               }
+               
+               switch_ivr_dmachine_set_realm(dmachine, realm);
+               myargs.dmachine = dmachine;
+               args = &myargs;
+       }
+
+       if (beep_file) {
+               status = switch_ivr_play_file(client->session, NULL, beep_file, args);
+       }
+
+       if (!switch_channel_ready(client->channel)) {
+               goto end;
+       }
+
+       if (status == SWITCH_STATUS_SUCCESS) {
+               switch_file_handle_t fh = { 0 };
+               fh.thresh = thresh;
+               fh.silence_hits = silence_hits;
+               
+               status = switch_ivr_record_file(client->session, &fh, tmp_record_path, args, record_limit);
+       }
+
+       if (client->matching_action_binding) {
+               if (client->matching_action_binding->action) {
+                       sub_action = client->matching_action_binding->action;
+               } else if (client->matching_action_binding->parent && client->matching_action_binding->parent->action) {
+                       sub_action = client->matching_action_binding->parent->action;
+               }
+       }
+
+       if (!sub_action && top_action_binding && top_action_binding->action) {
+               sub_action = top_action_binding->action;
+       }
+
+       if (sub_action) {
+               switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, "url", sub_action);
+       }
+
+       if (!zstr(tmp_record_path) && switch_file_exists(tmp_record_path, client->pool) == SWITCH_STATUS_SUCCESS) {
+               char *key = switch_core_sprintf(client->pool, "attach_file:%s:%s.%s", name, fname, ext);
+               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, key, tmp_record_path);
+               status = SWITCH_STATUS_TERM;
+       }
+
+ end:
+
+       if (dmachine) {
+               switch_ivr_dmachine_destroy(&dmachine);
+       }
+
+       return status;
+}
+
+
+static switch_status_t parse_xml(client_t *client)
+{
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       const void *bdata;
+       switch_size_t len;
+
+       if ((len = switch_buffer_peek_zerocopy(client->buffer, &bdata)) && switch_buffer_len(client->buffer) > len) {
+               switch_xml_t xml, tag, category;
+               
+               if (globals.debug) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Debugging Return Data:\n%s\n", (char *)bdata);
+               }
+
+               if ((xml = switch_xml_parse_str((char *)bdata, len))) {
+
+                       if (client->profile->perms.set_params) {
+                               if ((category = switch_xml_child(xml, "params"))) {
+                                       tag = category->child;
+                                       
+                                       while(tag) {
+                                               if (!zstr(tag->name)) {
+                                                       char *val = tag->txt;
+                                                       if (zstr(val)) {
+                                                               val = NULL;
+                                                       }
+                                                       switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, tag->name, val);
+                                               }
+                                               tag = tag->sibling;
+                                       }
+                               }
+                       }
+
+                       if (client->profile->perms.set_vars) {
+                               if ((category = switch_xml_child(xml, "variables"))) {
+                                       tag = category->child;
+                                       
+                                       while(tag) {
+                                               if (!zstr(tag->name)) {
+                                                       char *val = tag->txt;
+                                                       if (zstr(val)) {
+                                                               val = NULL;
+                                                       }
+                                                       switch_channel_set_variable(client->channel, tag->name, val);
+                                               }
+                                               tag = tag->sibling;
+                                       }
+                               }
+                       }
+
+                       if ((category = switch_xml_child(xml, "work"))) {
+                               tag = category->child;
+                               status = SWITCH_STATUS_SUCCESS;
+
+                               while(status == SWITCH_STATUS_SUCCESS && tag) {
+                                       if (!zstr(tag->name)) {
+                                               tag_parse_t handler;
+
+                                               if ((handler = (tag_parse_t)(intptr_t) switch_core_hash_find(globals.parse_hash, tag->name))) {
+                                                       char *expanded = tag->txt;
+                                                       switch_event_t *templ_data;
+
+                                                       if (tag->txt && client->profile->perms.expand_vars_in_tag_body) {
+                                                               switch_channel_get_variables(client->channel, &templ_data);
+                                                               switch_event_merge(templ_data, client->params);
+                                                               expanded = switch_event_expand_headers(templ_data, tag->txt);
+                                                               switch_event_destroy(&templ_data);
+                                                       }
+
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Process Tag: [%s]\n", tag->name);
+                                                       handler(tag->name, client, tag, expanded);
+                                                       
+                                                       if (expanded && expanded != tag->txt) {
+                                                               free(expanded);
+                                                       }
+
+                                               } else {
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported Tag: [%s]\n", tag->name);
+                                                       status = SWITCH_STATUS_FALSE;
+                                               }
+
+                                       }
+                                       tag = tag->ordered;
+                               }
+                       } else {
+                               status = SWITCH_STATUS_FALSE;
+                       }
+
+                       switch_xml_free(xml);
+               }
+       }       
+
+       return status;
+}
+       
+
+static size_t get_header_callback(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+       size_t realsize = (size * nmemb);
+       char *val, *header = NULL;
+       client_t *client = userdata;
+
+       /* validate length... Apache and IIS won't send a header larger than 16 KB */
+       if (realsize == 0 || realsize > 1024 * 16) {
+               return realsize;
+       }
+
+       /* get the header, adding NULL terminator if there isn't one */
+       switch_zmalloc(header, realsize + 1);
+       strncpy(header, (char *)ptr, realsize);
+
+       if ((val = strchr(header, ':'))) {
+               char *cr;
+               *val++ = '\0';
+               while(*val == ' ') val++;
+
+               if ((cr = strchr(val, '\r')) || (cr = strchr(val, '\r'))) {
+                       *cr = '\0';
+               }
+
+               switch_event_add_header_string(client->headers, SWITCH_STACK_BOTTOM, header, val);
+       }
+       
+       switch_safe_free(header);
+       return realsize;
+}
+
+
+static size_t file_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register unsigned int realsize = (unsigned int) (size * nmemb);
+       client_t *client = data;
+
+       client->bytes += realsize;
+
+       if (client->bytes > client->max_bytes) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) client->bytes);
+               client->err = 1;
+               return 0;
+       }
+
+       switch_buffer_write(client->buffer, ptr, realsize);
+       
+       return realsize;
+}
+
+static void client_destroy(client_t **client)
+{
+       if (client && *client) {
+               switch_memory_pool_t *pool;
+               
+               switch_event_destroy(&(*client)->headers);
+               switch_event_destroy(&(*client)->params);
+               switch_event_destroy(&(*client)->one_time_params);
+               switch_buffer_destroy(&(*client)->buffer);
+
+               pool = (*client)->pool;
+               switch_core_destroy_memory_pool(&pool);
+       }
+}
+
+static void client_reset(client_t *client)
+{
+       if (client->headers) {
+               switch_event_destroy(&client->headers);
+       }
+
+       switch_event_destroy(&client->one_time_params);
+       switch_event_create(&client->one_time_params, SWITCH_EVENT_CLONE);
+       client->one_time_params->flags |= EF_UNIQ_HEADERS;
+       
+       switch_event_create(&client->headers, SWITCH_EVENT_CLONE);
+
+
+       switch_buffer_zero(client->buffer);
+
+       client->code = 0;
+       client->err = 0;
+       client->matching_action_binding = NULL;
+       client->no_matching_action_binding = NULL;
+}
+
+static client_t *client_create(switch_core_session_t *session, const char *profile_name, switch_event_t **params)
+{
+       client_t *client;
+       switch_memory_pool_t *pool;
+       client_profile_t *profile;
+
+       if (zstr(profile_name)) {
+               profile_name = "default";
+       }
+
+       profile = (client_profile_t *) switch_core_hash_find(globals.profile_hash, profile_name);
+
+       if (!profile) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find profile [%s]\n", profile_name);
+               return NULL;
+       }
+
+       switch_core_new_memory_pool(&pool);
+       client = switch_core_alloc(pool, sizeof(*client));
+       client->pool = pool;
+
+       switch_event_create(&client->headers, SWITCH_EVENT_CLONE);
+       
+       client->session = session;
+       client->channel = switch_core_session_get_channel(session);
+
+       
+       client->profile = profile;
+
+       client->max_bytes = HTTAPI_MAX_API_BYTES;
+
+       switch_buffer_create_dynamic(&client->buffer, 1024, 1024, 0);
+
+       if (params && *params) {
+               client->params = *params;
+               *params = NULL;
+       } else {
+               switch_event_create(&client->params, SWITCH_EVENT_CLONE);
+               client->params->flags |= EF_UNIQ_HEADERS;
+       }
+
+       switch_event_create(&client->one_time_params, SWITCH_EVENT_CLONE);
+       client->one_time_params->flags |= EF_UNIQ_HEADERS;
+
+       switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, "hostname", switch_core_get_switchname());
+
+       return client;
+}
+
+
+static void cleanup_attachments(client_t *client)
+{
+       switch_event_header_t *hp;
+
+       for (hp = client->params->headers; hp; hp = hp->next) {
+               if (!strncasecmp(hp->name, "attach_file:", 12)) {
+                       if (switch_file_exists(hp->value, client->pool)) {
+                               unlink(hp->value);
+                       }
+               }       
+       }
+}
+
+static switch_status_t process_form_post_params(client_t *client, switch_CURL *curl_handle, struct curl_httppost **formpostp)
+{
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+       switch_event_header_t *hp;
+       int go = 0;
+
+       for (hp = client->params->headers; hp; hp = hp->next) {
+               if (!strncasecmp(hp->name, "attach_file:", 12)) {
+                       go = 1;
+                       break;
+               }
+       }
+
+       if (!go) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       for (hp = client->params->headers; hp; hp = hp->next) {
+
+               if (!strncasecmp(hp->name, "attach_file:", 12)) {
+                       char *pname = switch_core_strdup(client->pool, hp->name + 12);
+                       char *fname = strchr(pname, ':');
+                       
+                       if (fname && pname) {
+                               *fname++ = '\0';
+
+                               switch_curl_formadd(&formpost,
+                                                        &lastptr,
+                                                        CURLFORM_COPYNAME, pname,
+                                                        CURLFORM_FILENAME, fname,
+                                                        CURLFORM_FILE, hp->value,
+                                                        CURLFORM_END);
+                       }
+
+               } else {
+                       switch_curl_formadd(&formpost,
+                                                &lastptr,
+                                                CURLFORM_COPYNAME, hp->name,
+                                                CURLFORM_COPYCONTENTS, hp->value,
+                                                CURLFORM_END);
+
+               }
+       }
+
+       *formpostp = formpost;
+
+       return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status_t httapi_sync(client_t *client)
+                                                                 
+{
+       switch_CURL *curl_handle = NULL;
+       char *data = NULL;
+       switch_curl_slist_t *headers = NULL;
+       char *url = NULL;
+       char *dynamic_url = NULL;
+       const char *session_id = NULL;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       int get_style_method = 0;
+       char *method = NULL;
+       struct curl_httppost *formpost=NULL;
+       switch_event_t *save_params = NULL;
+
+       if (client->one_time_params && client->one_time_params->headers) {
+               save_params = client->params;
+               switch_event_dup(&client->params, save_params);
+               switch_event_merge(client->params, client->one_time_params);
+               switch_event_destroy(&client->one_time_params);
+               switch_event_create(&client->one_time_params, SWITCH_EVENT_CLONE);
+               client->one_time_params->flags |= EF_UNIQ_HEADERS;
+       }
+       
+       if (!(session_id = switch_event_get_header(client->params, "HTTAPI_SESSION_ID"))) {
+               if (!(session_id = switch_channel_get_variable(client->channel, "HTTAPI_SESSION_ID"))) {
+                       session_id = switch_core_session_get_uuid(client->session);
+               }
+       }
+
+       if (client->code || client->err) {
+               client_reset(client);
+       }
+
+
+       if (!(method = switch_event_get_header(client->params, "method"))) {
+               method = client->profile->method;
+       }
+
+       if (!(url = switch_event_get_header(client->params, "url"))) {
+               url = client->profile->url;
+               switch_event_add_header_string(client->params, SWITCH_STACK_BOTTOM, "url", url);
+       }
+
+       get_style_method = method ? strcasecmp(method, "post") : 1;
+       
+       switch_event_add_header_string(client->params, SWITCH_STACK_TOP, "session_id", session_id);
+
+       dynamic_url = switch_event_expand_headers(client->params, url);
+
+       process_form_post_params(client, curl_handle, &formpost);
+
+       if (formpost) {
+               get_style_method = 1;
+       } else {
+               data = switch_event_build_param_string(client->params, NULL, client->profile->vars_map);
+               switch_assert(data);
+
+               if (get_style_method) {
+                       char *tmp = switch_mprintf("%s%c%s", dynamic_url, strchr(dynamic_url, '?') != NULL ? '&' : '?', data);
+               
+                       if (dynamic_url != url) {
+                               free(dynamic_url);
+                       }
+               
+                       dynamic_url = tmp;
+               }
+       }
+
+       curl_handle = switch_curl_easy_init();
+
+       if (session_id) {
+               char *hval = switch_mprintf("HTTAPI_SESSION_ID=%s", session_id);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_COOKIE, hval);
+               free(hval);
+       }
+
+       if (!strncasecmp(dynamic_url, "https", 5)) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+       }
+
+
+       if (!zstr(client->profile->cred)) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, client->profile->auth_scheme);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_USERPWD, client->profile->cred);
+       }
+
+       if (client->profile->disable100continue) {
+               headers = switch_curl_slist_append(headers, "Expect:");
+       }
+
+       if (client->profile->enable_cacert_check) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, TRUE);
+       }
+
+       switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
+
+       if (method != NULL && strcasecmp(method, "get") && strcasecmp(method, "post")) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, method);
+       }
+
+       if (formpost) {
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+       } else {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_POST, !get_style_method);
+       }
+
+       switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10);
+
+       if (!get_style_method && !formpost) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data);
+       }
+
+       switch_curl_easy_setopt(curl_handle, CURLOPT_URL, dynamic_url);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, get_header_callback);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) client);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void *) client);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "mod_httapi/1.0");
+
+       if (client->profile->timeout) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, client->profile->timeout);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+       }
+
+       if (client->profile->ssl_cert_file) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSLCERT, client->profile->ssl_cert_file);
+       }
+
+       if (client->profile->ssl_key_file) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSLKEY, client->profile->ssl_key_file);
+       }
+
+       if (client->profile->ssl_key_password) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSLKEYPASSWD, client->profile->ssl_key_password);
+       }
+
+       if (client->profile->ssl_version) {
+               if (!strcasecmp(client->profile->ssl_version, "SSLv3")) {
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
+               } else if (!strcasecmp(client->profile->ssl_version, "TLSv1")) {
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+               }
+       }
+
+       if (client->profile->ssl_cacert_file) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_CAINFO, client->profile->ssl_cacert_file);
+       }
+
+       if (client->profile->enable_ssl_verifyhost) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2);
+       }
+
+       if (client->profile->cookie_file) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_COOKIEJAR, client->profile->cookie_file);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, client->profile->cookie_file);
+       }
+
+       if (client->profile->bind_local) {
+               curl_easy_setopt(curl_handle, CURLOPT_INTERFACE, client->profile->bind_local);
+       }
+
+       switch_curl_easy_perform(curl_handle);
+       switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &client->code);
+       switch_curl_easy_cleanup(curl_handle);
+       switch_curl_slist_free_all(headers);
+
+       if (formpost) {
+               curl_formfree(formpost);
+       }
+
+       if (client->err) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error encountered! [%s]\ndata: [%s]\n", client->profile->url, data);
+               status = SWITCH_STATUS_FALSE;
+       } else {
+               status = SWITCH_STATUS_SUCCESS;
+       }
+
+       switch_safe_free(data);
+
+       if (dynamic_url != url) {
+               switch_safe_free(dynamic_url);
+       }
+
+       cleanup_attachments(client);
+
+       if (save_params) {
+               switch_event_destroy(&client->params);
+               client->params = save_params;
+               save_params = NULL;
+       }
+
+
+       return status;
+}
+
+#define ENABLE_PARAM_VALUE "enabled"
+static switch_status_t do_config(void)
+{
+       char *cf = "httapi.conf";
+       switch_xml_t cfg, xml, profiles_tag, profile_tag, param, settings, tag;
+       client_profile_t *profile = NULL;
+       int x = 0;
+       int need_vars_map = 0;
+       switch_hash_t *vars_map = NULL;
+
+       if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf);
+               return SWITCH_STATUS_TERM;
+       }
+
+       if ((settings = switch_xml_child(cfg, "settings"))) {
+               for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+                       char *var = (char *) switch_xml_attr_soft(param, "name");
+                       char *val = (char *) switch_xml_attr_soft(param, "value");
+
+                       if (!strcasecmp(var, "debug")) {
+                               globals.debug = switch_true(val);
+                       } else if (!strcasecmp(var, "file-cache-ttl")) {
+                               int tmp = atoi(val);
+
+                               if (tmp > 0) {
+                                       globals.cache_ttl = tmp;
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid value [%s]for file-cache-ttl\n", val);
+                               }
+
+                       } else if (!strcasecmp(var, "file-not-found-expires")) {
+                               globals.not_found_expires = atoi(val);
+
+                               if (globals.not_found_expires < 0) {
+                                       globals.not_found_expires = -1;
+                               }
+                       }
+               }
+       }
+
+       if (!(profiles_tag = switch_xml_child(cfg, "profiles"))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing <profiles> tag!\n");
+               goto done;
+       }
+
+       for (profile_tag = switch_xml_child(profiles_tag, "profile"); profile_tag; profile_tag = profile_tag->next) {
+               char *bname = (char *) switch_xml_attr_soft(profile_tag, "name");
+               char *url = NULL;
+               char *bind_local = NULL;
+               char *bind_cred = NULL;
+               char *method = NULL;
+               int disable100continue = 1;
+               int timeout = 0;
+               uint32_t enable_cacert_check = 0;
+               char *ssl_cert_file = NULL;
+               char *ssl_key_file = NULL;
+               char *ssl_key_password = NULL;
+               char *ssl_version = NULL;
+               char *ssl_cacert_file = NULL;
+               uint32_t enable_ssl_verifyhost = 0;
+               char *cookie_file = NULL;
+               hash_node_t *hash_node;
+               int auth_scheme = CURLAUTH_BASIC;
+               need_vars_map = 0;
+               vars_map = NULL;
+
+               if (zstr(bname)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "profile tag missing name field!\n");
+                       continue;
+               }
+
+               if (switch_core_hash_find(globals.profile_hash, bname)) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile %s already exists\n", (char *)bname);
+                       continue;
+               }
+
+               if ((tag = switch_xml_child(profile_tag, "params"))) {
+                       for (param = switch_xml_child(tag, "param"); param; param = param->next) {
+                               char *var = (char *) switch_xml_attr_soft(param, "name");
+                               char *val = (char *) switch_xml_attr_soft(param, "value");
+
+                               if (!strcasecmp(var, "gateway-url")) {
+                                       if (val) {
+                                               url = val;
+                                       }
+                               } else if (!strcasecmp(var, "gateway-credentials")) {
+                                       bind_cred = val;
+                               } else if (!strcasecmp(var, "auth-scheme")) {
+                                       if (*val == '=') {
+                                               auth_scheme = 0;
+                                               val++;
+                                       }
+
+                                       if (!strcasecmp(val, "basic")) {
+                                               auth_scheme |= CURLAUTH_BASIC;
+                                       } else if (!strcasecmp(val, "digest")) {
+                                               auth_scheme |= CURLAUTH_DIGEST;
+                                       } else if (!strcasecmp(val, "NTLM")) {
+                                               auth_scheme |= CURLAUTH_NTLM;
+                                       } else if (!strcasecmp(val, "GSS-NEGOTIATE")) {
+                                               auth_scheme |= CURLAUTH_GSSNEGOTIATE;
+                                       } else if (!strcasecmp(val, "any")) {
+                                               auth_scheme = CURLAUTH_ANY;
+                                       }
+                               } else if (!strcasecmp(var, "disable-100-continue") && !switch_true(val)) {
+                                       disable100continue = 0;
+                               } else if (!strcasecmp(var, "method")) {
+                                       method = val;
+                               } else if (!strcasecmp(var, "timeout")) {
+                                       int tmp = atoi(val);
+                                       if (tmp >= 0) {
+                                               timeout = tmp;
+                                       } else {
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set a negative timeout!\n");
+                                       }
+                               } else if (!strcasecmp(var, "enable-cacert-check") && switch_true(val)) {
+                                       enable_cacert_check = 1;
+                               } else if (!strcasecmp(var, "ssl-cert-path")) {
+                                       ssl_cert_file = val;
+                               } else if (!strcasecmp(var, "ssl-key-path")) {
+                                       ssl_key_file = val;
+                               } else if (!strcasecmp(var, "ssl-key-password")) {
+                                       ssl_key_password = val;
+                               } else if (!strcasecmp(var, "ssl-version")) {
+                                       ssl_version = val;
+                               } else if (!strcasecmp(var, "ssl-cacert-file")) {
+                                       ssl_cacert_file = val;
+                               } else if (!strcasecmp(var, "enable-ssl-verifyhost") && switch_true(val)) {
+                                       enable_ssl_verifyhost = 1;
+                               } else if (!strcasecmp(var, "cookie-file")) {
+                                       cookie_file = val;
+                               } else if (!strcasecmp(var, "enable-post-var")) {
+                                       if (!vars_map && need_vars_map == 0) {
+                                               if (switch_core_hash_init(&vars_map, globals.pool) != SWITCH_STATUS_SUCCESS) {
+                                                       need_vars_map = -1;
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't init params hash!\n");
+                                                       continue;
+                                               }
+                                               need_vars_map = 1;
+                                       }
+
+                                       if (vars_map && val) {
+                                               if (switch_core_hash_insert(vars_map, val, ENABLE_PARAM_VALUE) != SWITCH_STATUS_SUCCESS) {
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Can't add %s to params hash!\n", val);
+                                               }
+                                       }
+                               } else if (!strcasecmp(var, "bind-local")) {
+                                       bind_local = val;
+                               }
+                       }
+               }
+
+               if (!url) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile has no url!\n");
+                       if (vars_map)
+                               switch_core_hash_destroy(&vars_map);
+                       continue;
+               }
+
+               if (!(profile = switch_core_alloc(globals.pool, sizeof(*profile)))) {
+                       if (vars_map)
+                               switch_core_hash_destroy(&vars_map);
+                       goto done;
+               }
+               memset(profile, 0, sizeof(*profile));
+
+               /* Defaults */
+               profile->conference_params.use_profile = "default";
+               profile->perms.set_params = 1;
+               profile->perms.conference.enabled = 1;
+               profile->perms.dial.enabled = 1;
+
+               if ((tag = switch_xml_child(profile_tag, "conference"))) {
+                       char *var = (char *) switch_xml_attr_soft(param, "name");
+                       char *val = (char *) switch_xml_attr_soft(param, "value");
+
+                       if (!strcasecmp(var, "default-profile")) {
+                               profile->conference_params.use_profile = switch_core_strdup(globals.pool, val);
+                       }
+               }
+
+               if ((tag = switch_xml_child(profile_tag, "dial"))) {
+                       char *var = (char *) switch_xml_attr_soft(param, "name");
+                       char *val = (char *) switch_xml_attr_soft(param, "value");
+
+                       if (!strcasecmp(var, "context")) {
+                               profile->dial_params.context = switch_core_strdup(globals.pool, val);
+                       } else if (!strcasecmp(var, "dialplan")) {
+                               profile->dial_params.dp = switch_core_strdup(globals.pool, val);;
+                       }
+               }
+
+               if ((tag = switch_xml_child(profile_tag, "permissions"))) {
+                       for (param = switch_xml_child(tag, "permission"); param; param = param->next) {
+                               char *var = (char *) switch_xml_attr_soft(param, "name");
+                               char *val = (char *) switch_xml_attr_soft(param, "value");
+
+                               if (!strcasecmp(var, "all")) {
+                                       switch_byte_t all = switch_true(val);
+                                       memset(&profile->perms, all, sizeof(profile->perms));
+                               } else if (!strcasecmp(var, "none")) {
+                                       switch_byte_t none = switch_true(val);
+                                       memset(&profile->perms, none, sizeof(profile->perms));
+                               } else if (!strcasecmp(var, "set-params")) {
+                                       profile->perms.set_params = switch_true(val);
+                               } else if (!strcasecmp(var, "set-vars")) {
+                                       profile->perms.set_vars = switch_true(val);
+                               } else if (!strcasecmp(var, "extended-data")) {
+                                       profile->perms.extended_data = switch_true(val);
+                               } else if (!strcasecmp(var, "execute-apps")) {
+                                       profile->perms.execute_apps = switch_true(val);
+                               } else if (!strcasecmp(var, "expand-vars-in-tag-body")) {
+                                       profile->perms.expand_vars_in_tag_body = switch_true(val);
+                               } else if (!strcasecmp(var, "dial")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                               } else if (!strcasecmp(var, "dial-set-context")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                                       profile->perms.dial.set_context = switch_true(val);
+                               } else if (!strcasecmp(var, "dial-set-dialplan")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                                       profile->perms.dial.set_dp = switch_true(val);
+                               } else if (!strcasecmp(var, "dial-set-cid-name")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                                       profile->perms.dial.set_cid_name = switch_true(val);
+                               } else if (!strcasecmp(var, "dial-set-cid-number")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                                       profile->perms.dial.set_cid_number = switch_true(val);
+                               } else if (!strcasecmp(var, "dial-full-originate")) {
+                                       profile->perms.dial.enabled = switch_true(val);
+                                       profile->perms.dial.full_originate = switch_true(val);
+                               } else if (!strcasecmp(var, "conference")) {
+                                       profile->perms.conference.enabled = switch_true(val);
+                               } else if (!strcasecmp(var, "conference-set-profile")) {
+                                       profile->perms.conference.enabled = switch_true(val);
+                                       profile->perms.conference.set_profile = switch_true(val);
+                               }
+
+                       }
+               }
+
+
+
+               profile->auth_scheme = auth_scheme;
+               profile->timeout = timeout;
+               profile->url = strdup(url);
+               switch_assert(profile->url);
+
+               if (bind_local != NULL) {
+                       profile->bind_local = strdup(bind_local);
+               }
+               if (method != NULL) {
+                       profile->method = strdup(method);
+               } else {
+                       profile->method = NULL;
+               }
+
+               if (bind_cred) {
+                       profile->cred = strdup(bind_cred);
+               }
+
+               profile->disable100continue = disable100continue;
+               profile->enable_cacert_check = enable_cacert_check;
+
+               if (ssl_cert_file) {
+                       profile->ssl_cert_file = strdup(ssl_cert_file);
+               }
+
+               if (ssl_key_file) {
+                       profile->ssl_key_file = strdup(ssl_key_file);
+               }
+
+               if (ssl_key_password) {
+                       profile->ssl_key_password = strdup(ssl_key_password);
+               }
+
+               if (ssl_version) {
+                       profile->ssl_version = strdup(ssl_version);
+               }
+
+               if (ssl_cacert_file) {
+                       profile->ssl_cacert_file = strdup(ssl_cacert_file);
+               }
+
+               profile->enable_ssl_verifyhost = enable_ssl_verifyhost;
+
+               if (cookie_file) {
+                       profile->cookie_file = strdup(cookie_file);
+               }
+
+               profile->vars_map = vars_map;
+
+               if (vars_map) {
+                       switch_zmalloc(hash_node, sizeof(hash_node_t));
+                       hash_node->hash = vars_map;
+                       hash_node->next = NULL;
+
+                       if (!globals.hash_root) {
+                               globals.hash_root = hash_node;
+                               globals.hash_tail = globals.hash_root;
+                       }
+
+                       else {
+                               globals.hash_tail->next = hash_node;
+                               globals.hash_tail = globals.hash_tail->next;
+                       }
+
+               }
+
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Profile [%s] JSON Function [%s]\n",
+                                                 zstr(bname) ? "N/A" : bname, profile->url);
+
+               profile->name = strdup(bname);
+
+               switch_core_hash_insert(globals.profile_hash, bname, profile);
+
+               x++;
+               profile = NULL;
+       }
+
+  done:
+
+       switch_xml_free(xml);
+
+       return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
+}
+
+static switch_status_t my_on_reporting(switch_core_session_t *session)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       client_t *client;
+       const char *var;
+
+       if (!(client = (client_t *) switch_channel_get_private(channel, "_HTTAPI_CLIENT_"))) {
+               return status;
+       }
+
+       switch_channel_set_private(channel, "_HTTAPI_CLIENT_", NULL);
+
+       if (client->profile->perms.extended_data) {
+               switch_channel_event_set_extended_data(channel, client->one_time_params);
+       }
+
+       switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "exiting", "true");
+       
+       if (client->record.file) {
+               char *key = switch_core_sprintf(client->pool, "attach_file:%s:%s.wav", client->record.name, switch_core_session_get_uuid(session));
+               switch_ivr_stop_record_session(client->session, client->record.file);
+               switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, key, client->record.file);
+       }
+
+       var = switch_event_get_header(client->params, "url");
+
+       if (client->record.action) {
+               if (strcmp(var, client->record.action)) {
+                       switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "url", client->record.action);
+                       httapi_sync(client);
+                       if (client->profile->perms.extended_data) {
+                               switch_channel_event_set_extended_data(channel, client->one_time_params);
+                       }
+                       switch_event_add_header_string(client->one_time_params, SWITCH_STACK_BOTTOM, "exiting", "true");
+               }
+       }
+       
+       httapi_sync(client);
+
+       client_destroy(&client);
+
+       return status;
+}
+
+static switch_state_handler_table_t state_handlers = {
+       /*.on_init */ NULL,
+       /*.on_routing */ NULL,
+       /*.on_execute */ NULL,
+       /*.on_hangup */ NULL,
+       /*.on_exchange_media */ NULL,
+       /*.on_soft_execute */ NULL,
+       /*.on_consume_media */ NULL,
+       /*.on_hibernate */ NULL,
+       /*.on_reset */ NULL,
+       /*.on_park */ NULL,
+       /*.on_reporting */ my_on_reporting,
+    /*.on_destroy */ NULL,
+    SSH_FLAG_STICKY
+};
+
+
+SWITCH_STANDARD_APP(httapi_function)
+{
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       char *parsed = NULL;
+       const char *profile_name = NULL;
+       client_t *client;
+       switch_event_t *params = NULL;
+       uint32_t loops = 0, all_extended = 0;
+       
+       if (!zstr(data)) {
+               switch_event_create_brackets((char *)data, '{', '}', ',', &params, &parsed, SWITCH_TRUE);
+       }
+
+       if ((client = (client_t *) switch_channel_get_private(channel, "_HTTAPI_CLIENT_"))) {
+               if (params) {
+                       switch_event_merge(client->params, params);
+                       switch_event_destroy(&params);
+               }
+       } else {
+               if (params) {
+                       profile_name = switch_event_get_header(params, "httapi_profile");
+               }
+
+               if (zstr(profile_name) && !(profile_name = switch_channel_get_variable(channel, "httapi_profile"))) {
+                       profile_name = "default";
+               }
+               
+               if ((client = client_create(session, profile_name, &params))) {
+                       switch_channel_set_private(channel, "_HTTAPI_CLIENT_", client);
+                       switch_channel_add_state_handler(channel, &state_handlers);
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find suitable profile\n");
+                       switch_event_destroy(&params);
+                       return;
+               }
+       }
+
+       if (client->profile->perms.extended_data) {
+               all_extended = switch_true(switch_event_get_header(client->params, "full_channel_data_on_every_req"));
+       }
+
+       while(switch_channel_ready(channel)) {
+               switch_status_t status = SWITCH_STATUS_FALSE;
+
+               if (client->profile->perms.extended_data && (!loops++ || all_extended)) {
+                       switch_channel_event_set_extended_data(channel, client->one_time_params);
+               }
+
+               if ((status = httapi_sync(client)) == SWITCH_STATUS_SUCCESS) {
+                       if (client->code == 200) {
+                               const char *ct = switch_event_get_header(client->headers, "content-type");
+
+                               if (switch_stristr("text/xml", ct)) {
+                                       status = parse_xml(client);
+                               } else {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received unsupported content-type %s\n", ct);
+                                       break;
+                               }
+                       } else {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received HTTP response: %ld.\n", client->code);
+                               break;
+                       }
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error %d!\n", client->err);
+               }
+
+               if (status == SWITCH_STATUS_TERM) {
+                       httapi_sync(client);
+               }
+
+               if (status != SWITCH_STATUS_SUCCESS) {
+                       break;
+               }
+       }
+
+
+       switch_safe_free(parsed);
+       
+}
+
+
+/* HTTP FILE INTERFACE */
+
+static char *load_cache_data(http_file_context_t *context, const char *url)
+{
+       char *ext;
+       char digest[SWITCH_MD5_DIGEST_STRING_SIZE] = { 0 };
+       char meta_buffer[1024] = "";
+       int fd;
+       switch_ssize_t bytes;
+
+       switch_md5_string(digest, (void *) url, strlen(url));
+       
+       if ((ext = strrchr(url, '.'))) {
+               ext++;
+       } else {
+               ext = "wav";
+       }
+
+       context->cache_file = switch_core_sprintf(context->pool, "%s%s%s.%s", globals.cache_path, SWITCH_PATH_SEPARATOR, digest, ext);
+       context->meta_file = switch_core_sprintf(context->pool, "%s.meta", context->cache_file);
+       context->lock_file = switch_core_sprintf(context->pool, "%s.lock", context->cache_file);
+
+       if (switch_file_exists(context->meta_file, context->pool) == SWITCH_STATUS_SUCCESS && ((fd = open(context->meta_file, O_RDONLY, 0)) > -1)) {
+               if ((bytes = read(fd, meta_buffer, sizeof(meta_buffer))) > 0) {
+                       char *p;
+
+                       if ((p = strchr(meta_buffer, ':'))) {
+                               *p++ = '\0';
+                               context->expires = (time_t) atol(meta_buffer);
+                               context->metadata = switch_core_strdup(context->pool, p);
+                       }
+               }
+               close(fd);
+       }
+
+       return context->cache_file;
+}
+
+static size_t save_file_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register unsigned int realsize = (unsigned int) (size * nmemb);
+       client_t *client = data;
+       int x;
+
+       client->bytes += realsize;
+       
+       
+
+       if (client->bytes > client->max_bytes) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) client->bytes);
+               client->err = 1;
+               return 0;
+       }
+
+       x = write(client->fd, ptr, realsize);
+
+       if (x != (int) realsize) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Short write! %d out of %d\n", x, realsize);
+       }
+       return x;
+}
+
+
+
+static switch_status_t fetch_cache_data(const char *url, switch_event_t **headers, const char *save_path)
+{
+       switch_CURL *curl_handle = NULL;
+       client_t client = { 0 };
+       long code;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+
+       client.fd = -1;
+
+       if (save_path) {
+               if ((client.fd = open(save_path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+       
+       curl_handle = switch_curl_easy_init();
+
+       switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+
+       if (!strncasecmp(url, "https", 5)) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+       }
+
+       client.max_bytes = HTTAPI_MAX_FILE_BYTES;
+
+       switch_curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 10);
+       switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url);
+
+       if (save_path) {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, save_file_callback);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &client);
+       } else {
+               switch_curl_easy_setopt(curl_handle, CURLOPT_HEADER, 1);
+               switch_curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1);
+       }
+
+       if (headers) {
+               switch_event_create(&client.headers, SWITCH_EVENT_CLONE);
+               if (save_path) {
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, get_header_callback);
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void *) &client);
+               } else {
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, get_header_callback);
+                       switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &client);
+               }
+       }
+       
+       switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "mod_httapi/1.0");
+       switch_curl_easy_perform(curl_handle);
+       switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &code);
+       switch_curl_easy_cleanup(curl_handle);
+       
+       if (client.fd > -1) {
+               close(client.fd);
+       }
+
+       if (headers && client.headers) {
+               switch_event_add_header(client.headers, SWITCH_STACK_BOTTOM, "http-response-code", "%ld", code);
+               *headers = client.headers;
+       }
+
+       switch(code) {
+       case 200:
+               if (save_path) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "caching: url:%s to %s (%ld bytes)\n", url, save_path, client.bytes);
+               }
+               status = SWITCH_STATUS_SUCCESS;
+               break;
+
+       case 404:
+               status = SWITCH_STATUS_NOTFOUND;
+               break;
+               
+       default:
+               status = SWITCH_STATUS_FALSE;
+               break;
+       }
+
+
+       return status;
+}
+
+static switch_status_t write_meta_file(http_file_context_t *context, const char *data)
+{
+       int fd;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       char write_data[1024];
+
+       if ((fd = open(context->meta_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       if (!zstr(data)) {
+
+               switch_snprintf(write_data, sizeof(write_data), 
+                                               "%" SWITCH_TIME_T_FMT ":%s",
+                                               switch_epoch_time_now(NULL) + globals.cache_ttl,
+                                               data);
+
+
+               status = write(fd, write_data, strlen(write_data) + 1) > 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
+       }
+
+       close(fd);
+
+       return status;
+}
+
+
+static switch_status_t lock_file(http_file_context_t *context, switch_bool_t lock)
+{
+
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+
+       if (lock) {
+               if (switch_file_open(&context->lock_fd,
+                                                        context->lock_file,
+                                                        SWITCH_FOPEN_WRITE | SWITCH_FOPEN_CREATE | SWITCH_FOPEN_TRUNCATE,
+                                                        SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, context->pool) != SWITCH_STATUS_SUCCESS) {
+                       return SWITCH_STATUS_FALSE;
+               }
+               
+
+               if (switch_file_lock(context->lock_fd, SWITCH_FLOCK_EXCLUSIVE) != SWITCH_STATUS_SUCCESS) {
+                       return SWITCH_STATUS_FALSE;
+               }
+       } else {
+               if (context->lock_fd){ 
+                       switch_file_close(context->lock_fd);
+                       status = SWITCH_STATUS_SUCCESS;
+               }
+
+               unlink(context->lock_file);
+       }
+
+       return status;
+}
+
+
+static switch_status_t locate_url_file(http_file_context_t *context, const char *url)
+{
+       switch_event_t *headers = NULL;
+       int unreachable = 0;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       time_t now = switch_epoch_time_now(NULL);
+       char *metadata;
+
+       load_cache_data(context, url);
+
+       if (context->expires && now < context->expires) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       lock_file(context, SWITCH_TRUE);
+
+       if ((status = fetch_cache_data(url, &headers, NULL)) != SWITCH_STATUS_SUCCESS) {
+               if (status == SWITCH_STATUS_NOTFOUND) {
+                       unreachable = 2;
+                       if (now - context->expires < globals.not_found_expires) {
+                               switch_goto_status(SWITCH_STATUS_SUCCESS, end);
+                       }
+               } else {
+                       unreachable = 1;
+               }
+       }
+
+       if (!unreachable && !zstr(context->metadata)) {
+               metadata = switch_core_sprintf(context->pool, "%s:%s:%s:%s",
+                                                                          url,
+                                                                          switch_event_get_header_nil(headers, "last-modified"),
+                                                                          switch_event_get_header_nil(headers, "etag"),
+                                                                          switch_event_get_header_nil(headers, "content-length")
+                                                                          );
+
+               if (!strcmp(metadata, context->metadata)) {
+                       write_meta_file(context, metadata);
+                       switch_goto_status(SWITCH_STATUS_SUCCESS, end);
+               }
+       }
+
+       switch_event_destroy(&headers);
+       fetch_cache_data(url, &headers, context->cache_file);
+       metadata = switch_core_sprintf(context->pool, "%s:%s:%s:%s",
+                                                                  url,
+                                                                  switch_event_get_header_nil(headers, "last-modified"),
+                                                                  switch_event_get_header_nil(headers, "etag"),
+                                                                  switch_event_get_header_nil(headers, "content-length")
+                                                                  );
+       
+       write_meta_file(context, metadata);
+       
+       if (switch_file_exists(context->cache_file, context->pool) == SWITCH_STATUS_SUCCESS) {
+               status = SWITCH_STATUS_SUCCESS;
+       }
+
+ end:
+
+       if (status != SWITCH_STATUS_SUCCESS) {
+               unlink(context->meta_file);
+               unlink(context->cache_file);
+       }
+
+       lock_file(context, SWITCH_FALSE);
+
+       switch_event_destroy(&headers);
+       
+       return status;
+}
+
+
+static switch_status_t http_file_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
+{
+       http_file_context_t *context = handle->private_info;
+
+       if (!handle->seekable) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File is not seekable\n");
+               return SWITCH_STATUS_NOTIMPL;
+       }
+
+       return switch_core_file_seek(&context->fh, cur_sample, samples, whence);
+}
+
+static switch_status_t http_file_file_open(switch_file_handle_t *handle, const char *path)
+{
+       http_file_context_t *context;
+       char *file_dup;
+       switch_status_t status;
+
+       if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing!\n");
+               return SWITCH_STATUS_FALSE;
+       }
+
+       context = switch_core_alloc(handle->memory_pool, sizeof(*context));
+       context->pool = handle->memory_pool;
+       
+       file_dup = switch_core_sprintf(handle->memory_pool, "http://%s", path);
+       
+       if ((status = locate_url_file(context, file_dup)) != SWITCH_STATUS_SUCCESS) {
+               return status;
+       }
+
+       handle->private_info = context;
+       
+       if ((status = switch_core_file_open(&context->fh,
+                                                                               context->cache_file, 
+                                                                               handle->channels, 
+                                                                               handle->samplerate, 
+                                                                               SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL)) != SWITCH_STATUS_SUCCESS) {
+               return status;
+       }
+
+       handle->samples = context->fh.samples;
+       handle->format = context->fh.format;
+       handle->sections = context->fh.sections;
+       handle->seekable = context->fh.seekable;
+       handle->speed = context->fh.speed;
+       handle->interval = context->fh.interval;
+
+       if (switch_test_flag((&context->fh), SWITCH_FILE_NATIVE)) {
+               switch_set_flag(handle, SWITCH_FILE_NATIVE);
+       } else {
+               switch_clear_flag(handle, SWITCH_FILE_NATIVE);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t http_file_file_close(switch_file_handle_t *handle)
+{
+       http_file_context_t *context = handle->private_info;
+
+       if (switch_test_flag((&context->fh), SWITCH_FILE_OPEN)) {
+               switch_core_file_close(&context->fh);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t http_file_file_read(switch_file_handle_t *handle, void *data, size_t *len)
+{
+       http_file_context_t *context = handle->private_info;
+       switch_status_t status;
+
+       if (context->samples > 0) {
+               if (*len > (size_t) context->samples) {
+                       *len = context->samples;
+               }
+
+               context->samples -= *len;
+               memset(data, 255, *len *2);
+               status = SWITCH_STATUS_SUCCESS;
+       } else {
+               status = switch_core_file_read(&context->fh, data, len);
+       }
+
+       return status;
+}
+
+/* Registration */
+
+static char *http_file_supported_formats[SWITCH_MAX_CODECS] = { 0 };
+
+
+/* /HTTP FILE INTERFACE */
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load)
+{
+       switch_api_interface_t *httapi_api_interface;
+       switch_application_interface_t *app_interface;
+       switch_file_interface_t *file_interface;
+       
+       /* connect my internal structure to the blank pointer passed to me */
+       *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+       memset(&globals, 0, sizeof(globals));
+       globals.pool = pool;
+       globals.hash_root = NULL;
+       globals.hash_tail = NULL;
+       globals.cache_ttl = 300;
+       globals.not_found_expires = 300;
+
+
+       http_file_supported_formats[0] = "http";
+
+       file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
+       file_interface->interface_name = modname;
+       file_interface->extens = http_file_supported_formats;
+       file_interface->file_open = http_file_file_open;
+       file_interface->file_close = http_file_file_close;
+       file_interface->file_read = http_file_file_read;
+       file_interface->file_seek = http_file_file_seek;
+       
+       switch_snprintf(globals.cache_path, sizeof(globals.cache_path), "%s%shttp_file_cache", SWITCH_GLOBAL_dirs.storage_dir, SWITCH_PATH_SEPARATOR);
+       switch_dir_make_recursive(globals.cache_path, SWITCH_DEFAULT_DIR_PERMS, pool);
+
+       
+       switch_core_hash_init(&globals.profile_hash, globals.pool);
+       switch_core_hash_init_case(&globals.parse_hash, globals.pool, SWITCH_FALSE);
+
+       bind_parser("execute", parse_execute);
+       bind_parser("sms", parse_sms);
+       bind_parser("dial", parse_dial);
+       bind_parser("pause", parse_playback);
+       bind_parser("hangup", parse_hangup);
+       bind_parser("record", parse_record);
+       bind_parser("recordCall", parse_record_call);
+       bind_parser("playback", parse_playback);
+       bind_parser("speak", parse_playback);
+       bind_parser("say", parse_playback);
+       bind_parser("conference", parse_conference);
+       bind_parser("break", parse_break);
+       bind_parser("log", parse_log);
+
+       if (do_config() != SWITCH_STATUS_SUCCESS) {
+               return SWITCH_STATUS_FALSE;
+       }
+       
+       SWITCH_ADD_API(httapi_api_interface, "HT-TAPI Hypertext Telephony API", "HT-TAPI Hypertext Telephony API", httapi_api_function, HTTAPI_SYNTAX);
+
+       SWITCH_ADD_APP(app_interface, "httapi", 
+                                  "HT-TAPI Hypertext Telephony API", 
+                                  "HT-TAPI Hypertext Telephony API", httapi_function, "{<param1>=<val1>}", SAF_SUPPORT_NOMEDIA);
+                                  
+
+
+       switch_console_set_complete("add httapi debug_on");
+       switch_console_set_complete("add httapi debug_off");
+
+       /* indicate that the module should continue to be loaded */
+       return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_httapi_shutdown)
+{
+       hash_node_t *ptr = NULL;
+
+       switch_core_hash_destroy(&globals.profile_hash);        
+       switch_core_hash_destroy(&globals.parse_hash);  
+               
+       while (globals.hash_root) {
+               ptr = globals.hash_root;
+               switch_core_hash_destroy(&ptr->hash);
+               globals.hash_root = ptr->next;
+               switch_safe_free(ptr);
+       }
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */
+
+
diff --git a/src/mod/applications/mod_httapi/mod_httapi_doc.txt b/src/mod/applications/mod_httapi/mod_httapi_doc.txt
new file mode 100644 (file)
index 0000000..f87ceaf
--- /dev/null
@@ -0,0 +1,279 @@
+HT-TAPI Hyper-Text Telephony API and http file format plugin
+
+This module provides an HTTP based Telephony API using a standard FreeSWITCH application interface as well as a cached http file format interface.
+
+The file format plugin can be used like this:
+<action application="playback" data="http://some.com/sounds/foo.wav"/>
+
+This syntax is valid anywhere a filname parameter is requested in FreeSWITCH.
+
+
+The application is called like this:
+
+<action application="httapi" data="{url=http://some.host.com/app.cgi}"/>
+
+The target url is expected to be a CGI returning text/xml using the documentation below.
+
+The format is roughly as described below (We could use a DTD maybe).
+
+<document type="xml/freeswitch-httapi">
+  <params>
+    <param name="" value=""/>
+  </params>
+  <variables>
+    <variable name="" value=""/>
+  </variables>
+  <work>
+  <...>
+  </work>
+</document>
+
+
+The profile name must be chosen to bind to a preset series of settings and permissions to use as a basis for the client session.
+Its chosen from the specified params or from the channel variable using the keyname 'httapi_profile' and if not specified will default to 'default'
+Any params specified in the initial data of the application encased in brackets {} will be parsed into the initial params similar to originate.
+These params remain set until the call ends and are supplied as form elements on each hit to the HTTP server.
+If the permissions allow, a <params> tag is parsed from the resulting document and set into this data set and will be mirrored back on subsequqent http posts.
+Also if the permissions allow a <variables> tag is parsed the same way setting channel variables.
+The <work> tag is required and contains one or more of the supported command tags that generally manipulate the call behaviour.
+The application will continue to do the task in the work section until an error is encoutered or when an action from the work tag warrants it.
+If the session exits the httapi app without hanging up it can do other tasks and make another call to httapi and the session data will remain preserved.
+
+
+EXAMPLE:
+
+<document type="xml/freeswitch-httapi">
+<params></params>
+<work>
+<playback name="exten" file="http://my.com/sounds/exten.wav" error-file="http://my.com/sounds/invalid.wav" input-timeout="5000">
+<bind strip="#">~\d+#</bind>
+</playback>
+</work>
+</document>
+
+BINDINGS
+Several of the work tags that indicate they support bindings can contain one of more bind tags that function with similar fashion to bind_digit_action
+
+<bind action strip>*EXPR*</bind>
+
+ATTRS:
+action              : a specific url to go to next if the binding is dialed
+strip               : a character to strip from in the result such as #
+
+WORK TAGS:
+<work>*ACTIONS*</work>
+
+
+<playback file name error-file action digit-timeout input-timeout loops asr-engine asr-grammar><bind action strip>*EXPR*</bind></playback>
+                   : Plays a file and optionally collects input.
+
+ATTRS:
+file                : The file
+name                : Param name to save result.
+error-file          : Error file to play on invalid input.
+action              : Change the new target url
+digit-timeout       : Timeout waiting for digits after file plays (when input bindings are present)
+input-timeout       : Timeout waiting for more digits in a multi-digit input.
+loops               : max times to play the file when input bindings are present.
+asr-engine          : ASR engine to use
+asr-grammar         : ASR grammar to use
+
+
+
+
+<record file name error-file action digit-timeout input-timeout><bind action strip>*EXPR*</bind></record>
+                   : Records a file, optionally collects input and posts the file back to the target url
+
+ATTRS:
+file                : The file
+name                : Param name to save result.
+error-file          : Error file to play on invalid input.
+action              : Change the new target url
+digit-timeout       : Timeout waiting for digits after file plays (when input bindings are present)
+input-timeout       : Timeout waiting for more digits in a multi-digit input.
+
+
+
+
+
+<pause name error-file action digit-timeout input-timeout loops milliseconds><bind action strip>*EXPR*</bind></pause>
+                   : Waits for input for a specific amount of time.
+
+ATTRS:
+milliseconds        : Number of milliseconds to pause
+name                : Param name to save result.
+error-file          : Error file to play on invalid input.
+action              : Change the new target url
+digit-timeout       : Timeout waiting for digits after file plays (when input bindings are present)
+input-timeout       : Timeout waiting for more digits in a multi-digit input.
+loops               : max times to play the file when input bindings are present.
+
+
+
+
+
+<speak file name error-file action digit-timeout input-timeout loops engine voice><bind action strip>*EXPR*</bind></speak>
+                   : Read Text to Speech with optional input.
+
+ATTRS:
+file                : The file
+name                : Param name to save result.
+error-file          : Error file to play on invalid input.
+action              : Change the new target url
+digit-timeout       : Timeout waiting for digits after file plays (when input bindings are present)
+input-timeout       : Timeout waiting for more digits in a multi-digit input.
+loops               : max times to play the file when input bindings are present.
+engine              : tts engine to use.
+voice               : tts voice to use.
+
+
+
+
+
+<say file name error-file action digit-timeout input-timeout loops language type method gender><bind action strip>*EXPR*</bind></say>
+                   : Use the FS say engine to iterate sounds to similate a human speaker.
+
+ATTRS:
+file                : The file
+name                : Param name to save result.
+error-file          : Error file to play on invalid input.
+action              : Change the new target url
+digit-timeout       : Timeout waiting for digits after file plays (when input bindings are present)
+input-timeout       : Timeout waiting for more digits in a multi-digit input.
+loops               : max times to play the file when input bindings are present.
+language            : language
+type                : type (fs param)
+method              : method (fs param)
+gender              : gender (fs param)
+
+
+
+
+
+<execute application>*DATA*</execute>
+                   : Execute a FreeSWITCH app.
+ATTRS:
+application         : The app to run
+*DATA*              : The app data
+
+<sms to>DATA</sms>
+                   : Send a SMS message.
+ATTRS:
+to                  : The dest number
+*DATA*              : The message data
+
+
+
+
+
+<dial context dialplan caller-id-name caller-id-number>*DATA*</dial>
+                   : Place an outbound call or transfer.
+
+ATTRS:
+context             : Dialplan context.
+dialplan            : Dialplan dialplan.
+caller-id-name      : Caller ID Name.
+caller-id-number    : Caller ID Number.
+*DATA*              : Number to dial or originate string
+
+
+
+
+
+<recordCall limit name action>
+                   : begin recording the call.  The file will be posted when the call ends.
+
+ATTRS:
+limit               : Timeout in seconds.
+name                : Name to use for input values.
+action              : URL action to use.
+
+
+
+
+
+<conference profile>
+                   : Start a conference call.
+
+ATTRS:
+profile             : Conference profile to use.
+
+
+
+
+
+<hangup cause>
+                   : Hangup the call
+
+ATTRS:
+cause               : Hangup cause
+
+
+
+
+
+<break>
+                   : Exit the httapi application and continue in the dialplan.
+
+
+
+
+
+<log level clean>
+                   : Exit the httapi application and continue in the dialplan.
+ATTRS:
+level               : The log level to use.
+clean               : If true do not pring log prefix.
+
+
+
+
+CONFIGURATION:
+
+<settings>:
+<param name="" value="">
+debug                           : <true|false>          false           Print debug data
+file-cache-ttl                  : <number of sec>       300             How long to wait before checking the server to see if audio file has changed.
+file-not-found-expires          : <number of sec>       300             How long to still preserve cached audio files that are not found by the server.
+
+<profile name="<name>">         : CREATE NEW PROFILE TO REFERENCE BY NAME
+gateway-url                     : <string>              ""              Initial URL to connect to.
+gateway-credentials             : <string>              ""              HTTP credentials.
+auth-scheme                     : <string               >               basic   auth scheme to use. [basic|digest|NTLM|GSS-NEGOTIATE|any]
+disable-100-continue            : <true|false>          true            Disable the 100 continue feature.
+method                          : <string>              ""              METHOD name to send.
+timeout                         : <number>              0               Timeout waiting for response.
+enable-cacert-check             : <true|false>          false           Check CA/CERT.
+ssl-cert-path                   : <string>              ""              path to file.
+ssl-key-path                    : <string>              ""              path to file.
+ssl-key-password                : <string>              ""              password to use.
+ssl-version                     : <string>              ""              ssl version
+ssl-cacert-file                 : <string>              ""              CA/CERT file.
+enable-ssl-verifyhost           : <true|false>          ""              Verify ssl host.
+cookie-file                     : <string>              ""              Path to file to use for cookie.
+enable-post-var                 : <param_name>          ""              Specify specifc param names ok to send.
+bind-local                      : <string>              ""              Interface to bind to.
+default-profile                 : <string>              default         Profile to use when not specified.
+
+
+
+<permissions>: * = default
+<permission name="" value="">
+
+*set-params                     : <params> tag can be parsed for session params.
+set-vars                        : <variables> tag can be parsed to set channel vars.
+extended-data                   : Extended data is sent like full channel event data.
+execute-apps                    : <execute> tag is enabled to execute apps.
+expand-vars-in-tag-body         : body content of tags are run trough variable expansion.
+*dial                           : <dial> tag is enabled allowing outbound dialing.
+dial-set-context                : <dial context=""> context attribute is permitted.
+dial-set-dialplan               : <dial dialplan=""> dialplan attribute is permitted.
+dial-set-cid-name               : <dial cid_name=""> cid_name attribute is permitted.
+dial-set-cid-number             : <dial cid_number=""> cid_number attribute is permitted.
+dial-full-originate             : <dial> full originate syntax is permitted instead of just a number.
+*conference                     : <conference> tag is enabled allowing creation of conferences.
+conference-set-profile          : <conference profile=""> attribure is permitted.
+all                             : all permissions are set
+none                            : no permissions are set
+
+