提交 ee7fb354 authored 作者: Mitch Capper's avatar Mitch Capper
......@@ -27,7 +27,9 @@ if freeswitch then
local api = freeswitch.API()
base_dir = api:executeString("global_getvar base_dir")
sounds_dir = api:executeString("global_getvar sounds_dir")
jester_dir = base_dir .. "/scripts/jester"
-- Override this if scripts are hosted in a non-standard location.
scripts_dir = base_dir .. "/scripts"
jester_dir = scripts_dir .. "/jester"
-- This value can be overridden per profile.
sequence_path = jester_dir .. "/sequences"
profile_path = jester_dir .. "/profiles"
......@@ -49,6 +51,7 @@ key_order = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "*", "#" }
-- the profile.
modules = {
"core_actions",
"couchdb",
"data",
"dialplan_tools",
"email",
......
......@@ -291,7 +291,7 @@ function set_storage(area, key, value)
-- Make sure the storage area exists.
init_storage(area)
channel.storage[area][key] = value
debug_log("Setting storage: area '%s', key '%s', value '%s'", area, key, value)
debug_log("Setting storage: area '%s', key '%s', value '%s'", area, key, tostring(value))
end
end
......
......@@ -5,15 +5,11 @@ jester.conf.debug = false
require "jester.support.string"
require "jester.support.table"
-- lua-filesystem 1.5 seems to have a bug that prevents using relative paths to
-- open a directory. Since the CLI version already assumes it's being run
-- from the FreeSWITCH 'scripts' directory, provide a default script path
-- that will work with 1.4.2 and higher.
-- The CLI version assumes it's being run from the FreeSWITCH scripts
-- directory, but when run from FreeSWITCH the full path must be specified.
local script_path = "./"
if jester.is_freeswitch then
local api = freeswitch.API()
local base_dir = api:executeString("global_getvar base_dir")
script_path = base_dir .. "/scripts/"
script_path = jester.conf.scripts_dir .. "/"
end
--[[
......@@ -75,9 +71,8 @@ function get_help(...)
require "lfs"
local error_message = {}
local path = script_path .. jester.conf.help_path
local dir = lfs.dir(path)
local file_list = {}
for file in dir do
for file in lfs.dir(path) do
-- Exclude directory references and hidden files.
if not string.match(file, "^%..*") then
table.insert(file_list, file)
......
jester.action_map.couchdb_retrieve_document = {
mod = "couchdb",
func = "retrieve_document",
}
jester.action_map.couchdb_create_document = {
mod = "couchdb",
func = "create_document",
}
jester.action_map.couchdb_update_document = {
mod = "couchdb",
func = "update_document",
}
jester.action_map.couchdb_delete_document = {
mod = "couchdb",
func = "delete_document",
}
jester.action_map.couchdb_add_attachment = {
mod = "couchdb",
func = "add_attachment",
}
jester.action_map.couchdb_retrieve_attachment = {
mod = "couchdb",
func = "retrieve_attachment",
}
jester.action_map.couchdb_delete_attachment = {
mod = "couchdb",
func = "delete_attachment",
}
--[[
Interface to CouchDB via Luchia.
]]
local document = require "luchia.document"
local http_code_ok = 200
module(..., package.seeall)
--[[
Sets up a new document handler.
]]
local function doc_handler(action)
local database = action.database
local server = action.server
if database then
local doc = document:new(database, server)
if type(doc) == "table" then
jester.debug_log("New document handler for database: %s", action.database)
return doc
end
end
end
--[[
CouchDB action handlers.
]]
function retrieve_document(action)
local id = action.document_id
local params = action.query_parameters
local storage_area = action.storage_area or "couchdb_retrieve_document"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp, code = doc:retrieve(id, params)
if code == http_code_ok then
jester.debug_log("Document ID '%s' retrieved from database: %s", resp._id, action.database)
jester.set_storage(storage_area, "id", resp._id)
jester.set_storage(storage_area, "rev", resp._rev)
jester.set_storage(storage_area, "document", resp)
end
end
end
function create_document(action)
local data = action.document
local id = action.document_id
local storage_area = action.storage_area or "couchdb_create_document"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp = doc:create(data, id)
if doc:response_ok(resp) then
jester.debug_log("New document created for database: %s, id: %s", action.database, resp.id)
jester.set_storage(storage_area, "id", resp.id)
jester.set_storage(storage_area, "rev", resp.rev)
end
end
end
function update_document(action)
local data = action.document
local id = action.document_id
local rev = action.document_rev
local storage_area = action.storage_area or "couchdb_update_document"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp = doc:update(data, id, rev)
if doc:response_ok(resp) then
jester.debug_log("Document id '%s' updated for database: %s", resp.id, action.database)
jester.set_storage(storage_area, "id", resp.id)
jester.set_storage(storage_area, "rev", resp.rev)
end
end
end
function delete_document(action)
local id = action.document_id
local rev = action.document_rev
local storage_area = action.storage_area or "couchdb_delete_document"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp = doc:delete(id, rev)
if doc:response_ok(resp) then
jester.debug_log("Document id '%s' deleted from database: %s", resp.id, action.database)
jester.set_storage(storage_area, "id", resp.id)
jester.set_storage(storage_area, "rev", resp.rev)
end
end
end
function retrieve_attachment(action)
local id = action.document_id
local name = action.attachment_name
local file_path = action.file_path
local storage_area = action.storage_area or "couchdb_retrieve_attachment"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local file_data, code = doc:retrieve_attachment(name, id)
if code == http_code_ok then
-- Write out retrieved file data.
local file = io.open(file_path, "w")
if file then
if file:write(file_data) then
jester.debug_log("Attachment '%s' retrieved from document ID '%s' in database '%s', written to file '%s'.", name, id, action.database, file_path)
jester.set_storage(storage_area, "attachment_name", name)
jester.set_storage(storage_area, "file_path", file_path)
end
file:close()
end
end
end
end
function add_attachment(action)
local file_path = action.file_path
local content_type = action.content_type
local name = action.attachment_name
local id = action.document_id
local rev = action.document_rev
local storage_area = action.storage_area or "couchdb_add_attachment"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp = doc:add_standalone_attachment(file_path, content_type, name, id, rev)
if doc:response_ok(resp) then
jester.debug_log("File '%s' added as attachment '%s' to document ID '%s' in database: %s", file_path, name, resp.id, action.database)
jester.set_storage(storage_area, "id", resp.id)
jester.set_storage(storage_area, "rev", resp.rev)
end
end
end
function delete_attachment(action)
local name = action.attachment_name
local id = action.document_id
local rev = action.document_rev
local storage_area = action.storage_area or "couchdb_delete_attachment"
jester.clear_storage(storage_area)
local doc = doc_handler(action)
if doc then
local resp = doc:delete_attachment(name, id, rev)
if doc:response_ok(resp) then
jester.debug_log("Attachment '%s' deleted from document ID '%s' in database: %s", name, resp.id, action.database)
jester.set_storage(storage_area, "id", resp.id)
jester.set_storage(storage_area, "rev", resp.rev)
end
end
end
jester.help_map.couchdb = {}
jester.help_map.couchdb.description_short = [[Access to CouchDB.]]
jester.help_map.couchdb.description_long = [[This module provides actions that allow access to CouchDB.
The module uses Luchia (https://github.com/thehunmonkgroup/luchia) to interface with CouchDB, so it must be installed and configured correctly.
For all actions below that support the 'server' parameter:
The 'server' parameter is a table of connection parameters to the CouchDB server, see http://thehunmonkgroup.github.com/luchia/modules/luchia.core.server.html#new_params for details.
If things aren't working, be sure to check the log file, as configured in luchia/conf.lua.
]]
jester.help_map.couchdb.actions = {}
jester.help_map.couchdb.actions.couchdb_retrieve_document = {}
jester.help_map.couchdb.actions.couchdb_retrieve_document.description_short = [[Retrieve a document.]]
jester.help_map.couchdb.actions.couchdb_retrieve_document.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
document_id = [[The document ID.]],
query_parameters = [[(Optional) A table of query parameters to pass to the server, eg. '{revs = "true"}']],
storage_area = [[(Optional) The storage area to store the document in after loading. Defaults to 'couchdb_retrieve_document'. This will contain 'document', 'id' and 'rev' keys on success, 'document' is a table representing the document.]],
}
jester.help_map.couchdb.actions.couchdb_create_document = {}
jester.help_map.couchdb.actions.couchdb_create_document.description_short = [[Create a document.]]
jester.help_map.couchdb.actions.couchdb_create_document.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
document = [[A table of data representing the docuement to create, eg. '{hello = "world", foo = "bar"}']],
document_id = [[(Optional) The document ID, one will be generated by the server if not provided.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_create_document'. This will contain 'id' and 'rev' keys of the created document on success.]],
}
jester.help_map.couchdb.actions.couchdb_update_document = {}
jester.help_map.couchdb.actions.couchdb_update_document.description_short = [[Update a document.]]
jester.help_map.couchdb.actions.couchdb_update_document.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
document = [[A table of data representing the docuement to update, eg. '{hello = "world", foo = "bar"}']],
document_id = [[The document ID.]],
document_rev = [[The current document revision.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_update_document'. This will contain 'id' and 'rev' keys of the updated document on success.]],
}
jester.help_map.couchdb.actions.couchdb_delete_document = {}
jester.help_map.couchdb.actions.couchdb_delete_document.description_short = [[Delete a document.]]
jester.help_map.couchdb.actions.couchdb_delete_document.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
document_id = [[The document ID.]],
document_rev = [[The current document revision.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_delete_document'. This will contain 'id' and 'rev' keys of the deleted document on success.]],
}
jester.help_map.couchdb.actions.couchdb_retrieve_attachment = {}
jester.help_map.couchdb.actions.couchdb_retrieve_attachment.description_short = [[Retrieve an attachment from a document.]]
jester.help_map.couchdb.actions.couchdb_retrieve_attachment.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
attachment_name = [[The attachment name, eg. 'attachment.txt'.]],
document_id = [[The document ID.]],
file_path = [[File path to write the retrieved attachment to, eg. 'tmp/attachment.txt'.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_retrieve_attachment'. This will contain 'attachment_name' and 'file_path' keys of the retrieved attachment on success.]],
}
jester.help_map.couchdb.actions.couchdb_add_attachment = {}
jester.help_map.couchdb.actions.couchdb_add_attachment.description_short = [[Add an attachment to a document.]]
jester.help_map.couchdb.actions.couchdb_add_attachment.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
file_path = [[File path to the attachment, eg. 'tmp/attachment.txt'.]],
content_type = [[MIME type of the attachment, eg. 'text/plain'.]],
attachment_name = [[(Optional) File name for the attachment, eg. 'attachment.txt'.]],
document_id = [[(Optional) The document ID. If not provided, a new empty document will be created.]],
document_rev = [[The current document revision, required if attaching to an existing document.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_add_attachment'. This will contain 'id' and 'rev' keys of the updated document on success.]],
}
jester.help_map.couchdb.actions.couchdb_delete_attachment = {}
jester.help_map.couchdb.actions.couchdb_delete_attachment.description_short = [[Delete an attachment from a document.]]
jester.help_map.couchdb.actions.couchdb_delete_attachment.params = {
server = [[(Optional) Table of server connection parameters.]],
database = [[Database to connect to.]],
attachment_name = [[The attachment name, eg. 'attachment.txt'.]],
document_id = [[The document ID.]],
document_rev = [[The current document revision.]],
storage_area = [[(Optional) The storage area to store the server response in. Defaults to 'couchdb_delete_attachment'. This will contain 'id' and 'rev' keys of the updated document on success.]],
}
......@@ -4,14 +4,16 @@ module(..., package.seeall)
Fires a custom event.
]]
function fire_event(action)
local subclass = action.subclass or "jester"
local event_type = action.event_type
local headers = action.headers
local header_prefix = action.header_prefix or "Jester-"
local body = action.body
if event_type then
local event = freeswitch.Event("custom", "jester::" .. event_type)
local event = freeswitch.Event("custom", subclass .. "::" .. event_type)
if headers then
for k, v in pairs(headers) do
event:addHeader("Jester-" .. k, v)
event:addHeader(header_prefix .. k, v)
end
end
if body then
......
......@@ -6,12 +6,14 @@ jester.help_map.event.actions = {}
jester.help_map.event.actions.fire_event = {}
jester.help_map.event.actions.fire_event.description_short = [[Fires a custom event.]]
jester.help_map.event.actions.fire_event.description_long = [[Fires a custom event. The event name will be 'CUSTOM', and the event type will be 'jester::[event_type]'.
jester.help_map.event.actions.fire_event.description_long = [[Fires a custom event. Event-Name will be 'CUSTOM', and Event-Subclass will be '[subclass]::[event_type]'.
All headers will be prefixed with 'Jester-', and the body will automatically have two newline characters appended to it.]]
The body will automatically have two newline characters appended to it.]]
jester.help_map.event.actions.fire_event.params = {
event_type = [[The event type.]],
subclass = [[(Optional) The first portion of the Event-Subclass header (before the double colons). Default is 'jester'.]],
event_type = [[The second portion of the Event-Subclass header (after the double colons).]],
headers = [[(Optional) A table of event headers, key = header name, value = header description. Note that some headers will need to use the full table key syntax, eg. 'headers = {["Custom-Value"] = "foo"}'.]],
header_prefix = [[(Optional) Prefix all header keys with this string. Defaults to 'Jester-'.]],
body = [[(Optional) The event body.]],
}
......@@ -18,8 +18,8 @@ jester.help_map.play.actions.play.params = {
1. A full file path
2. A relative file path from the FreeSWITCH 'sounds' directory
3. A phrase prefixed with 'phrase:', eg, 'phrase:invalid_sound'.
To play several files together in a group, pass a table of names instead, eg. 'file = { "foo", "bar", "phrase:goodbye" }'.]],
To play several files together in a group, pass a table of names instead, eg. 'file = { "/tmp/foo.wav", "bar.wav", "phrase:goodbye" }'. Internally, Jester uses an ampersand (&) as the default delimiter for playback of mulitple files; to override this, set the 'playback_delimiter' variable in either the global or profile configuration file.]],
repetitions = [[(Optional) How many times to repeat the file(s). Default is 1.]],
wait = [[(Optional) How long to wait between repetitions, in milliseconds. Default is no wait.]],
keys = [[(Optional) See 'help sequences keys'.]],
......@@ -74,7 +74,7 @@ jester.help_map.play.actions.play_valid_file.params = {
1. Full file paths
2. Relative file paths from the FreeSWITCH 'sounds' directory
3. A phrase prefixed with 'phrase:', eg, 'phrase:invalid_sound' (note that this will always be considered a valid file)
List the files in the order you would prefer them to be searched, eg. 'file = { "mycustomgreeting", "phrase:invalid_entry" }'.]],
repetitions = [[(Optional) How many times to repeat the file(s). Default is 1.]],
wait = [[(Optional) How long to wait between repetitions, in milliseconds. Default is no wait.]],
......
......@@ -24,7 +24,11 @@ function play_file(action)
if action.file then
-- A list of files was passed, join them in order.
if type(action.file) == "table" then
action.file = table.concat(action.file, "&")
local delimiter = jester.conf.playback_delimiter or "&"
-- Ensure this channel variable is properly set, or playback of multiple
-- files will not work.
jester.set_variable("playback_delimiter", delimiter)
action.file = table.concat(action.file, delimiter)
end
local rep = reps(action)
for i = 1, rep do
......
......@@ -20,6 +20,7 @@ function record_file(action)
filename = os.date("%Y-%m-%d_%H:%M:%S") .. "-" .. jester.channel.uuid .. ".wav"
end
local filepath = dir .. "/" .. filename
jester.clear_storage("record")
jester.set_storage("record", "last_recording_name", filename)
jester.set_storage("record", "last_recording_path", filepath)
jester.set_storage("record", "last_recording_timestamp", timestamp)
......@@ -50,7 +51,7 @@ function record_file(action)
-- Capture recording duration by getting timestamps immediately before and
-- after the recording.
local startstamp = os.time()
session:recordFile(filepath, max_length, silence_threshold, silence_secs)
local recorded = session:recordFile(filepath, max_length, silence_threshold, silence_secs)
local endstamp = os.time()
if append then
......@@ -58,13 +59,15 @@ function record_file(action)
jester.set_variable("record_append", record_append)
end
jester.set_storage("record", "last_recording_duration", endstamp - startstamp)
-- Store the recording settings in a custom set of keys if given.
if area then
jester.set_storage(area, "name", filename)
jester.set_storage(area, "path", filepath)
jester.set_storage(area, "timestamp", timestamp)
jester.set_storage(area, "duration", endstamp - startstamp)
if recorded then
jester.set_storage("record", "last_recording_duration", endstamp - startstamp)
-- Store the recording settings in a custom set of keys if given.
if area then
jester.set_storage(area, "name", filename)
jester.set_storage(area, "path", filepath)
jester.set_storage(area, "timestamp", timestamp)
jester.set_storage(area, "duration", endstamp - startstamp)
end
end
end
......
......@@ -2,7 +2,7 @@ jester.help_map.speech_to_text = {}
jester.help_map.speech_to_text.description_short = [[Speech to text translation. WARNING: experimental.]]
jester.help_map.speech_to_text.description_long = [[This module provides speech to text translation. WARNING: totally experimental, no guarantees it will work!
The module requires the Lua JSON package, more details at http://luaforge.net/projects/json]]
The module requires the Lua lua-cjson package, more details at http://luarocks.org/repositories/rocks]]
jester.help_map.speech_to_text.handlers = {
google = [[Uses Google's Speech to Text service.]],
}
......
......@@ -4,7 +4,7 @@ local io = require("io")
local http = require("socket.http")
local ltn12 = require("ltn12")
require "jester.support.file"
require("json.json")
require("cjson")
--[[
Speech to text using Google's API.
......@@ -44,7 +44,7 @@ function speech_to_text_from_file_google(action)
if status_code == 200 then
response_string = table.concat(response)
data = json.decode(response_string)
data = cjson.decode(response_string)
jester.set_storage(area, "status", data.status or 1)
......
......@@ -114,7 +114,7 @@ class sofia_conf extends fs_configuration {
$this -> xmlw -> startElement('gateways');
for ($i=0; $i<$gateway_count; $i++) {
$this_gateway = $gateway_array[$i]['gateway_name'];
if ($this_gateway != $gateway_array[$i-1]['gateway_name']) {
if (!array_key_exists($i-1, $gateway_array) || $this_gateway != $gateway_array[$i-1]['gateway_name']) {
$this -> xmlw -> startElement('gateway');
$this -> xmlw -> writeAttribute('name', $this_gateway);
}
......
......@@ -13,7 +13,7 @@ if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) {
/**
* Defines the default dsn for the FS_PDO class
*/
define('DEFAULT_DSN', 'pgsql:dbname=freeswitch;host=127.0.0.1');
define('DEFAULT_DSN', 'mysql:dbname=freeswitch;host=127.0.0.1');
/**
* Defines the default dsn login for the PDO class
*/
......
<?php
/**
* FS_CURL was originally written using the MDB2 pear class for database interactions.
* MDB2 was deprecated in favor or PHP's PDO.
*
* This has been tested with MySQL but should work with many other RDBMSs
* @author Michael Phillips <michael.j.phillips@gmail.com>
* @license bsd http://www.opensource.org/licenses/bsd-license.php
*/
class FS_PDO extends PDO {
/**
* Default fetch mode. Associative arrays seem to be used most often
*/
private $fetch_mode = PDO::FETCH_ASSOC;
/**
* FS_CURL was originally written using the MDB2 pear class for database interactions.
* MDB2 was deprecated in favor or PHP's PDO.
*
* This has been tested with MySQL but should work with many other RDBMSs
* @author Michael Phillips <michael.j.phillips@gmail.com>
* @license bsd http://www.opensource.org/licenses/bsd-license.php
*/
if (class_exists('PDO')) {
class FS_PDO extends PDO {
/**
* Default fetch mode. Associative arrays seem to be used most often
*/
private $fetch_mode = PDO::FETCH_ASSOC;
/**
* Basic counter for profiling the number of queries executed.
*/
public $counter = 0;
/**
* Basic counter for profiling the number of queries executed.
*/
public $counter = 0;
public function __construct($dsn, $login = NULL, $password = NULL, $options = NULL) {
parent::__construct($dsn, $login, $password, $options);
$this->setAttribute( PDO::ATTR_STATEMENT_CLASS, array( 'FS_PDOStatement', array() ) );
}
public function __construct($dsn, $login = NULL, $password = NULL, $options = NULL) {
parent::__construct($dsn, $login, $password, $options);
$this->setAttribute( PDO::ATTR_STATEMENT_CLASS, array( 'FS_PDOStatement', array() ) );
}
/**
* Set the fetch mode i.e. PDO::FETCH_ASSOC
* @param string $fetch_mode
*/
public function setFetchMode($fetch_mode) {
$this->fetch_mode = $fetch_mode;
}
/**
* Set the fetch mode i.e. PDO::FETCH_ASSOC
* @param string $fetch_mode
*/
public function setFetchMode($fetch_mode) {
$this->fetch_mode = $fetch_mode;
}
/**
* Return an associative array instead of a PDO statement object
* @param string $query Query to be executed
* @return mixed Associative array result set
*/
public function queryAll($query) {
$this->counter++;
$res = $this->query($query);
if(!$res) {
return false;
} else {
return $res->fetchAll($this->fetch_mode);
}
}
/**
* Return an associative array instead of a PDO statement object
* @param string $query Query to be executed
* @return mixed Associative array result set
*/
public function queryAll($query) {
$this->counter++;
$res = $this->query($query);
if(!$res) {
return false;
} else {
return $res->fetchAll($this->fetch_mode);
}
}
/**
* Backwards compatibility for error checking with MDB2
* @static
* @param string $result The return status of a previous query operation
* @return bool
*/
public static function isError($result) {
if($result === false) {
return true;
} else {
return false;
/**
* Backwards compatibility for error checking with MDB2
* @static
* @param string $result The return status of a previous query operation
* @return bool
*/
public static function isError($result) {
if($result === false) {
return true;
} else {
return false;
}
}
}
/**
*
* @param string $query Query to be executed
* @return PDOStatement
*/
public function query($query) {
$this->counter++;
return PDO::query($query);
}
/**
*
* @param string $query Query to be executed
* @return PDOStatement
*/
public function query($query) {
$this->counter++;
return PDO::query($query);
}
/**
* Takes and array and converts to a string
* @return string $full_error A formated message
*/
public function getMessage() {
$full_error = "";
foreach($this->errorInfo() as $error) {
$full_error .= $error . "\n";
/**
* Takes and array and converts to a string
* @return string $full_error A formated message
*/
public function getMessage() {
$full_error = "";
foreach($this->errorInfo() as $error) {
$full_error .= $error . "\n";
}
return $full_error;
}
return $full_error;
}
}
/**
* PDOStatment was extended to provide backwards compatible functions such as
* fetchRow and rowCount. At some point all references to numRow() should be
* replaced with rowCount()
*/
/**
* PDOStatment was extended to provide backwards compatible functions such as
* fetchRow and rowCount. At some point all references to numRow() should be
* replaced with rowCount()
*/
class FS_PDOStatement extends PDOStatement {
class FS_PDOStatement extends PDOStatement {
protected function __construct() {
protected function __construct() {
}
}
public function fetchRow() {
public function fetchRow() {
return $this->fetch(PDO::FETCH_ASSOC);
}
return $this->fetch(PDO::FETCH_ASSOC);
}
public function numRows() {
if (preg_match('/^select/i', $this->queryString)) {
$results = $this->fetchAll();
$this->execute();
return count($results);
} else {
return $this->rowCount();
public function numRows() {
if (preg_match('/^select/i', $this->queryString)) {
$results = $this->fetchAll();
$this->execute();
return count($results);
} else {
return $this->rowCount();
}
}
}
}
?>
\ No newline at end of file
} else {
trigger_error("PDO doesn't seem to be installed, make sure it's installed and loaded", E_USER_ERROR);
}
BASE=../../../..
include $(BASE)/build/modmake.rules
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2009, 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):
*
* Leon de Rooij <leon@scarlet-internet.nl>
*
*
* mod_mycommands
*
* several commands apis that I need
*
* - pg_prepare_copy remove all carriage return (\r) and replace newlines (\n) by literal \ and n
* - generate_xml_cdr if run on a session, returns an xml cdr
*
*/
#include <switch.h>
#define er(x) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", x);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_mycommands_shutdown);
SWITCH_MODULE_LOAD_FUNCTION(mod_mycommands_load);
SWITCH_MODULE_DEFINITION(mod_mycommands, mod_mycommands_load, mod_mycommands_shutdown, NULL);
/*
* SWITCH_STANDARD_API
*/
SWITCH_STANDARD_API(pg_prepare_copy_function)
{
const char *ptr, *pptr = cmd;
const char chars[6] = { '\n', '\t', '\r', '\b', '\f', '\v' }; /* characters that need to be replaced */
if (zstr(cmd)) {
return SWITCH_STATUS_FALSE;
}
/* optimize still:
* - set stream->alloc_chunk or alloc_len so that it's at least larger than cmd see switch_console.h :46
*/
while ((ptr = strpbrk(pptr, chars))) {
if (ptr > pptr) {
stream->raw_write_function(stream, (uint8_t *) pptr, ptr - pptr);
}
switch (ptr[0]) {
case '\n':
stream->raw_write_function(stream, (uint8_t *) "\\n", 2);
break;
case '\t':
stream->raw_write_function(stream, (uint8_t *) "\\t", 2);
break;
case '\r':
/* just remove this character */
break;
case '\b':
stream->raw_write_function(stream, (uint8_t *) "\\b", 2);
break;
case '\f':
stream->raw_write_function(stream, (uint8_t *) "\\f", 2);
break;
case '\v':
stream->raw_write_function(stream, (uint8_t *) "\\v", 2);
break;
}
pptr = ptr + 1;
}
stream->raw_write_function(stream, (uint8_t *) pptr, strlen(pptr) + 1);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(generate_xml_cdr_function)
{
switch_xml_t cdr = NULL;
char *xml_text = NULL;
if (session) {
if (switch_ivr_generate_xml_cdr(session, &cdr) == SWITCH_STATUS_SUCCESS) {
xml_text = switch_xml_toxml(cdr, SWITCH_TRUE);
if (xml_text) {
switch_assert(xml_text);
stream->raw_write_function(stream, (unsigned char *) xml_text, strlen(xml_text));
free(xml_text);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
}
switch_xml_free(cdr);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Generating XML CDR!\n");
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Session Available!\n");
}
return SWITCH_STATUS_SUCCESS;
}
/*
* LOAD & SHUTDOWN
*/
SWITCH_MODULE_LOAD_FUNCTION(mod_mycommands_load)
{
switch_api_interface_t *api_interface;
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_API(api_interface, "pg_prepare_copy", "mod_mycommands pg_prepare_copy function", pg_prepare_copy_function, "<string>");
SWITCH_ADD_API(api_interface, "generate_xml_cdr", "mod_mycommands generate_xml_cdr function", generate_xml_cdr_function, "");
switch_console_set_complete("add pg_prepare_copy");
switch_console_set_complete("add generate_xml_cdr");
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_mycommands_shutdown)
{
/* remove auto completion */
switch_console_set_complete("del pg_prepare_copy");
switch_console_set_complete("del generate_xml_cdr");
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
*/
......@@ -77,7 +77,7 @@ my $htmlfile = $htmldir . "/chanvars.html";
my $site = 'http://fisheye.freeswitch.org/browse/FreeSWITCH.git';
my $obvious_exceptions; # regex match for channel variable names we don't care about
$obvious_exceptions = '^(|argv|v?var|var_?name|v?buf|char.*|inner_var_array.*|arg.*|string|tmp_name|[^_]*_var|\(char \*\) vvar)$';
$obvious_exceptions = '^(|argv|v?var|var_?name|v?buf|(const )?char.*|inner_var_array.*|arg.*|string|tmp_name|[^_]*_var|\(char \*\) vvar)$';
my %switch_types; # key = switch type def, val = chan var name
my %channel_vars; # key = chan var name, val = HoA
......@@ -210,7 +210,54 @@ while(<FILEIN>){
}
}
close(FILEIN);
## Handle the switch_channel_execute_on vars...
$datafile = $tmpdir . "/execute-on-vars.txt";
open(FILEIN,'<',$datafile) or die "$datafile - $!\n";
while(<FILEIN>){
chomp;
next unless m/\.c(pp)?:/; # only c and cpp files for now
my @RECIN = split /(\.c(pp)?)/,$_; # split on file name.c or name.cpp
my ($filename,$dir,$ext) = fileparse($RECIN[0]);
$filename .= $RECIN[1]; # append .c or .cpp
# debug
#print "$filename\n";
# trim off the srcdir from beginning of string
my $reldir = substr $dir, $srcdirlen;
#print "$filename $dir $ext ($reldir)\n";
if ( ! exists( $source_files{$filename} ) ) {
$source_files{ $filename } = $site . $reldir . $filename . '?r=HEAD';
}
## Extract line number for this occurrence in this source file
my $linenum = 0;
if ( $RECIN[3] =~ m/:(\d+):/ ) {
$linenum = $1;
}
my @temp = split /,\s*/,$_;
my $channel_variable_name = $temp[1];
## Clean off quotes, parens, semicolons...
$channel_variable_name =~ s/"|;|\)//g;
if ( exists( $switch_types{$channel_variable_name} ) ) {
## This isn't actually a channel variable name but rather a def from switch_types.h
$channel_variable_name = $switch_types{$channel_variable_name};
}
## Skip obvious exceptions...
next if $channel_variable_name =~ m/$obvious_exceptions/;
## populate the hash for this variable, filename and line num & set/get
push @{ $channel_vars{$channel_variable_name}{$filename} }, [$linenum, 'execute_on'];
push @{ $source_idx{$filename}{$channel_variable_name} }, [$linenum, 'execute_on'];
# Debug
#print "var: $channel_variable_name\n";
}
close(FILEIN);
#debug
......
......@@ -12,7 +12,8 @@
# MC 2009-04-02
# Change these vars if your directories are different
SRCDIR=/usr/src/freeswitch.git
#SRCDIR=/usr/src/freeswitch/src
SRCDIR=/Users/michaelcollins/workspace/fs/freeswitch/src
TMPDIR=/tmp
HTMLDIR=/usr/local/freeswitch/htdocs
......@@ -21,11 +22,14 @@ echo "Searching source tree for channel variable set/get..."
grep -rn "[sg]et_variable(" $SRCDIR/* | grep -v Binary | grep -v svn > $TMPDIR/get-set-vars.txt
grep -rn "[sg]et_variable_partner(" $SRCDIR/* | grep -v Binary | grep -v svn >> $TMPDIR/get-set-vars.txt
echo "Searching source tree for switch_channel_execute_on statements..."
grep -rn "switch_channel_execute_on(" $SRCDIR/* | grep -v Binary > $TMPDIR/execute-on-vars.txt
echo "Searching source tree for event_get_header channel variables... "
grep -rn "switch_event_get_header(var_event" $SRCDIR/* | grep -v Binary > $TMPDIR/event-vars.txt
echo "Getting header file definitions..."
grep -n "_VARIABLE" $SRCDIR/src/include/*h > $TMPDIR/header-defs.txt
grep -n "_VARIABLE" $SRCDIR/include/*h > $TMPDIR/header-defs.txt
# launch perl script here
echo "Creating HTML file in $TMPDIR directory"
......
......@@ -118,7 +118,7 @@ void stmtexp_callback (stmtexp_key_t *key, stmtexp_val_t *val, void *userdata)
{
if (nodes->nodeTab[0])
{
if(nodes->nodeTab[0]->type == XML_ELEMENT_NODE)
if(nodes->nodeTab[0]->type == XML_ELEMENT_NODE || XML_ATTRIBUTE_NODE)
{
cur = nodes->nodeTab[0];
if(cur->ns)
......
#ifndef PLUGLDR_H
#define PLUGLDR_H
#include <pcre.h>
#include "plug.h"
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论