980 lines
32 KiB
C
980 lines
32 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005-2014, 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>
|
|
* William King <william.king@quentustech.com>
|
|
* Emmanuel Schmidbauer <eschmidbauer@gmail.com>
|
|
*
|
|
* mod_valet_parking.c -- Valet Parking Module
|
|
*
|
|
*/
|
|
#include <switch.h>
|
|
#define VALET_EVENT "valet_parking::info"
|
|
#define VALET_PROTO "park"
|
|
#define TOKEN_FREQ 5
|
|
|
|
/* Prototypes */
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load);
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_valet_parking_shutdown);
|
|
|
|
/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
|
|
* Defines a switch_loadable_module_function_table_t and a static const char[] modname
|
|
*/
|
|
SWITCH_MODULE_DEFINITION(mod_valet_parking, mod_valet_parking_load, mod_valet_parking_shutdown, NULL);
|
|
|
|
typedef struct {
|
|
char ext[256];
|
|
char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
|
|
time_t timeout;
|
|
int bridged;
|
|
time_t start_time;
|
|
} valet_token_t;
|
|
|
|
typedef struct {
|
|
switch_hash_t *hash;
|
|
switch_mutex_t *mutex;
|
|
switch_memory_pool_t *pool;
|
|
time_t last_timeout_check;
|
|
char *name;
|
|
} valet_lot_t;
|
|
|
|
static valet_lot_t globals = { 0 };
|
|
|
|
|
|
static valet_lot_t *valet_find_lot(const char *name, switch_bool_t create)
|
|
{
|
|
valet_lot_t *lot;
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
lot = switch_core_hash_find(globals.hash, name);
|
|
if (!lot && create) {
|
|
switch_zmalloc(lot, sizeof(*lot));
|
|
lot->name = strdup(name);
|
|
switch_mutex_init(&lot->mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
|
switch_core_hash_init(&lot->hash);
|
|
switch_core_hash_insert(globals.hash, name, lot);
|
|
}
|
|
switch_mutex_unlock(globals.mutex);
|
|
return lot;
|
|
}
|
|
|
|
static switch_status_t valet_on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
|
{
|
|
switch (itype) {
|
|
case SWITCH_INPUT_TYPE_DTMF:
|
|
{
|
|
switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
|
|
|
|
if (dtmf->digit == '#') {
|
|
return SWITCH_STATUS_BREAK;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void check_timeouts(void)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
const void *var;
|
|
void *val;
|
|
time_t now;
|
|
valet_lot_t *lot;
|
|
switch_console_callback_match_t *matches = NULL;
|
|
switch_console_callback_match_node_t *m;
|
|
switch_hash_index_t *i_hi = NULL;
|
|
const void *i_var;
|
|
void *i_val;
|
|
char *i_ext;
|
|
valet_token_t *token;
|
|
|
|
now = switch_epoch_time_now(NULL);
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
if (now - globals.last_timeout_check < TOKEN_FREQ) {
|
|
switch_mutex_unlock(globals.mutex);
|
|
return;
|
|
}
|
|
|
|
globals.last_timeout_check = now;
|
|
for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
|
|
switch_core_hash_this(hi, &var, NULL, &val);
|
|
switch_console_push_match(&matches, (const char *) var);
|
|
}
|
|
switch_mutex_unlock(globals.mutex);
|
|
|
|
|
|
if (matches) {
|
|
for (m = matches->head; m; m = m->next) {
|
|
|
|
lot = valet_find_lot(m->val, SWITCH_FALSE);
|
|
switch_mutex_lock(lot->mutex);
|
|
|
|
top:
|
|
|
|
for (i_hi = switch_core_hash_first_iter( lot->hash, i_hi); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
|
|
switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
|
|
i_ext = (char *) i_var;
|
|
token = (valet_token_t *) i_val;
|
|
|
|
if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) {
|
|
switch_core_hash_delete(lot->hash, i_ext);
|
|
switch_safe_free(token);
|
|
goto top;
|
|
}
|
|
}
|
|
switch_safe_free(i_hi);
|
|
|
|
switch_mutex_unlock(lot->mutex);
|
|
}
|
|
|
|
switch_console_free_matches(&matches);
|
|
}
|
|
|
|
}
|
|
|
|
static int find_longest(valet_lot_t *lot, int min, int max)
|
|
{
|
|
|
|
switch_hash_index_t *i_hi;
|
|
const void *i_var;
|
|
void *i_val;
|
|
valet_token_t *token;
|
|
int longest_ext = 0;
|
|
time_t longest = 0, cur = 0;
|
|
time_t now = switch_epoch_time_now(NULL);
|
|
|
|
switch_mutex_lock(lot->mutex);
|
|
for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
|
|
int i;
|
|
switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
|
|
token = (valet_token_t *) i_val;
|
|
cur = (now - token->start_time);
|
|
i = atoi(token->ext);
|
|
|
|
if (cur > longest && i >= min && i <= max) {
|
|
longest = cur;
|
|
longest_ext = i;
|
|
}
|
|
}
|
|
switch_mutex_unlock(lot->mutex);
|
|
|
|
return longest_ext;
|
|
}
|
|
|
|
static valet_token_t *next_id(switch_core_session_t *session, valet_lot_t *lot, int min, int max, int in)
|
|
{
|
|
int i, r = 0;
|
|
char buf[256] = "";
|
|
valet_token_t *token;
|
|
|
|
if (!min) {
|
|
min = 1;
|
|
}
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
|
|
if (!in) {
|
|
int longest = find_longest(lot, min, max);
|
|
if (longest > 0) {
|
|
switch_snprintf(buf, sizeof(buf), "%d", longest);
|
|
switch_mutex_lock(lot->mutex);
|
|
token = (valet_token_t *) switch_core_hash_find(lot->hash, buf);
|
|
switch_mutex_unlock(lot->mutex);
|
|
if (token) {
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = min; (i <= max || max == 0); i++) {
|
|
switch_snprintf(buf, sizeof(buf), "%d", i);
|
|
switch_mutex_lock(lot->mutex);
|
|
token = (valet_token_t *) switch_core_hash_find(lot->hash, buf);
|
|
switch_mutex_unlock(lot->mutex);
|
|
|
|
if ((!in && token && !token->timeout)) {
|
|
goto end;
|
|
}
|
|
|
|
if (in && !token) {
|
|
r = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
token = NULL;
|
|
|
|
if (r) {
|
|
switch_snprintf(buf, sizeof(buf), "%d", r);
|
|
switch_zmalloc(token, sizeof(*token));
|
|
switch_set_string(token->uuid, switch_core_session_get_uuid(session));
|
|
switch_set_string(token->ext, buf);
|
|
token->start_time = switch_epoch_time_now(NULL);
|
|
switch_mutex_lock(lot->mutex);
|
|
switch_core_hash_insert(lot->hash, buf, token);
|
|
switch_mutex_unlock(lot->mutex);
|
|
}
|
|
|
|
end:
|
|
|
|
switch_mutex_unlock(globals.mutex);
|
|
|
|
return token;
|
|
}
|
|
|
|
static int valet_lot_count(valet_lot_t *lot)
|
|
{
|
|
switch_hash_index_t *i_hi;
|
|
const void *i_var;
|
|
void *i_val;
|
|
valet_token_t *token;
|
|
int count = 0;
|
|
time_t now;
|
|
|
|
now = switch_epoch_time_now(NULL);
|
|
|
|
switch_mutex_lock(lot->mutex);
|
|
for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
|
|
switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
|
|
token = (valet_token_t *) i_val;
|
|
if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) {
|
|
continue;
|
|
}
|
|
count++;
|
|
}
|
|
switch_mutex_unlock(lot->mutex);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int EC = 0;
|
|
|
|
static void valet_send_presence(const char *lot_name, valet_lot_t *lot, valet_token_t *token, switch_bool_t in)
|
|
{
|
|
|
|
char *domain_name, *dup_lot_name = NULL, *dup_domain_name = NULL;
|
|
switch_event_t *event;
|
|
int count;
|
|
|
|
|
|
dup_lot_name = strdup(lot_name);
|
|
lot_name = dup_lot_name;
|
|
|
|
if ((domain_name = strchr(dup_lot_name, '@'))) {
|
|
*domain_name++ = '\0';
|
|
}
|
|
|
|
if (zstr(domain_name)) {
|
|
dup_domain_name = switch_core_get_domain(SWITCH_TRUE);
|
|
domain_name = dup_domain_name;
|
|
}
|
|
|
|
if (zstr(domain_name)) {
|
|
domain_name = "cluecon.com";
|
|
}
|
|
|
|
count = valet_lot_count(lot);
|
|
|
|
if (count > 0) {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", count, count == 1 ? "" : "s");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
switch_event_fire(&event);
|
|
}
|
|
} else {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (in) {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", token->ext);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", token->ext, domain_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", token->bridged == 0 ? "Holding" : "Active");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", token->ext);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", token->bridged == 0 ? "outbound" : "inbound");
|
|
switch_event_fire(&event);
|
|
}
|
|
} else {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", token->ext);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", token->ext, domain_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", token->ext);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
|
|
switch_safe_free(dup_domain_name);
|
|
switch_safe_free(dup_lot_name);
|
|
|
|
}
|
|
|
|
struct read_frame_data {
|
|
const char *dp;
|
|
const char *exten;
|
|
const char *context;
|
|
long to;
|
|
};
|
|
|
|
static switch_status_t read_frame_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
|
|
{
|
|
struct read_frame_data *rf = (struct read_frame_data *) user_data;
|
|
|
|
if (--rf->to <= 0) {
|
|
rf->to = -1;
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
#define VALET_APP_SYNTAX "<lotname> <extension>|[ask [<min>] [<max>] [<to>] [<prompt>]|auto [in|out] [min] [max]]"
|
|
SWITCH_STANDARD_APP(valet_parking_function)
|
|
{
|
|
char *argv[6], *lbuf;
|
|
int argc;
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
switch_event_t *event;
|
|
char dtmf_buf[128] = "";
|
|
int is_auto = 0, play_announce = 1;
|
|
const char *var;
|
|
valet_token_t *token = NULL;
|
|
struct read_frame_data rf = { 0 };
|
|
long to_val = 0;
|
|
|
|
check_timeouts();
|
|
|
|
if ((var = switch_channel_get_variable(channel, "valet_announce_slot"))) {
|
|
play_announce = switch_true(var);
|
|
}
|
|
|
|
if (!zstr(data) && (lbuf = switch_core_session_strdup(session, data))
|
|
&& (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) {
|
|
char *lot_name = argv[0];
|
|
char *ext = argv[1];
|
|
valet_lot_t *lot;
|
|
const char *uuid;
|
|
const char *music = "silence";
|
|
const char *tmp = NULL;
|
|
switch_status_t status;
|
|
switch_input_args_t args = { 0 };
|
|
char dbuf[10];
|
|
char *dest;
|
|
int in = -1;
|
|
|
|
const char *timeout, *orbit_exten, *orbit_dialplan, *orbit_context;
|
|
char *timeout_str = "", *orbit_exten_str = "", *orbit_dialplan_str = "", *orbit_context_str = "";
|
|
|
|
lot = valet_find_lot(lot_name, SWITCH_TRUE);
|
|
switch_assert(lot);
|
|
|
|
if (!strcasecmp(ext, "auto")) {
|
|
const char *io = argv[2];
|
|
const char *min = argv[3];
|
|
const char *max = argv[4];
|
|
int min_i, max_i;
|
|
|
|
if (argc < 5) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
|
|
return;
|
|
}
|
|
|
|
if (io) {
|
|
if (!strcasecmp(io, "in")) {
|
|
in = 1;
|
|
is_auto = 1;
|
|
} else if (!strcasecmp(io, "out")) {
|
|
in = 0;
|
|
}
|
|
}
|
|
|
|
if (in < 0) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
|
|
return;
|
|
}
|
|
|
|
min_i = atoi(min);
|
|
max_i = atoi(max);
|
|
|
|
if (!(token = next_id(session, lot, min_i, max_i, in))) {
|
|
switch_ivr_phrase_macro(session, in ? "valet_lot_full" : "valet_lot_empty", "", NULL, NULL);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s lot is %s.\n", switch_channel_get_name(channel), in ? "full" : "empty");
|
|
return;
|
|
}
|
|
|
|
switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%s", token->ext);
|
|
ext = dtmf_buf;
|
|
|
|
} else if (!strcasecmp(ext, "ask")) {
|
|
const char *prompt = "ivr/ivr-enter_ext_pound.wav";
|
|
int min = 1;
|
|
int max = 11;
|
|
int to = 10000;
|
|
int i;
|
|
|
|
tmp = argv[2] ? argv[2] : switch_channel_get_variable(channel, "valet_ext_min");
|
|
if (tmp) {
|
|
if ((i = atoi(tmp)) > 0) {
|
|
min = i;
|
|
}
|
|
}
|
|
|
|
tmp = argv[3] ? argv[3] : switch_channel_get_variable(channel, "valet_ext_max");
|
|
if (tmp) {
|
|
if ((i = atoi(tmp)) > 0) {
|
|
max = i;
|
|
}
|
|
}
|
|
|
|
tmp = argv[4] ? argv[4] : switch_channel_get_variable(channel, "valet_ext_to");
|
|
if (tmp) {
|
|
if ((i = atoi(tmp)) > 0) {
|
|
to = i;
|
|
}
|
|
}
|
|
|
|
tmp = argv[5] ? argv[5] : switch_channel_get_variable(channel, "valet_ext_prompt");
|
|
if (tmp) {
|
|
prompt = tmp;
|
|
}
|
|
|
|
do {
|
|
status = switch_ivr_read(session, min, max, prompt, NULL, dtmf_buf, sizeof(dtmf_buf), to, "#", 0);
|
|
} while (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_FALSE);
|
|
|
|
if (status == SWITCH_STATUS_SUCCESS) {
|
|
ext = dtmf_buf;
|
|
} else {
|
|
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
|
|
}
|
|
}
|
|
|
|
if (!token || !in) {
|
|
|
|
if (!token) {
|
|
switch_mutex_lock(lot->mutex);
|
|
token = (valet_token_t *) switch_core_hash_find(lot->hash, ext);
|
|
switch_mutex_unlock(lot->mutex);
|
|
}
|
|
|
|
if (token && !token->bridged) {
|
|
switch_core_session_t *b_session;
|
|
|
|
if (token->timeout) {
|
|
const char *var = switch_channel_get_variable(channel, "valet_ticket");
|
|
|
|
if (!zstr(var)) {
|
|
if (!strcmp(var, token->uuid)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Valet ticket %s accepted.\n", var);
|
|
token->timeout = 0;
|
|
switch_channel_set_variable(channel, "valet_ticket", NULL);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid token %s\n", token->uuid);
|
|
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!zstr(token->uuid) && (b_session = switch_core_session_locate(token->uuid))) {
|
|
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session));
|
|
switch_channel_event_set_data(switch_core_session_get_channel(b_session), event);
|
|
switch_event_fire(&event);
|
|
switch_core_session_rwunlock(b_session);
|
|
token->timeout = 0;
|
|
token->bridged = 1;
|
|
|
|
switch_ivr_uuid_bridge(token->uuid, switch_core_session_get_uuid(session));
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (token) {
|
|
switch_mutex_lock(lot->mutex);
|
|
switch_core_hash_delete(lot->hash, token->ext);
|
|
switch_mutex_unlock(lot->mutex);
|
|
memset(token, 0, sizeof(*token));
|
|
} else {
|
|
switch_zmalloc(token, sizeof(*token));
|
|
}
|
|
switch_set_string(token->uuid, switch_core_session_get_uuid(session));
|
|
switch_set_string(token->ext, ext);
|
|
token->start_time = switch_epoch_time_now(NULL);
|
|
switch_mutex_lock(lot->mutex);
|
|
switch_core_hash_insert(lot->hash, ext, token);
|
|
switch_mutex_unlock(lot->mutex);
|
|
}
|
|
|
|
if (!(tmp = switch_channel_get_variable(channel, "valet_hold_music"))) {
|
|
tmp = switch_channel_get_hold_music(channel);
|
|
}
|
|
|
|
if (tmp) {
|
|
music = tmp;
|
|
}
|
|
|
|
if (!strcasecmp(music, "silence")) {
|
|
music = "silence_stream://-1";
|
|
}
|
|
|
|
if ((orbit_exten = switch_channel_get_variable(channel, "valet_parking_orbit_exten"))) {
|
|
orbit_exten_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_exten=%s,", orbit_exten);
|
|
}
|
|
|
|
if ((orbit_dialplan = switch_channel_get_variable(channel, "valet_parking_orbit_dialplan"))) {
|
|
orbit_dialplan_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_dialplan=%s,", orbit_dialplan);
|
|
}
|
|
|
|
if ((orbit_context = switch_channel_get_variable(channel, "valet_parking_orbit_context"))) {
|
|
orbit_context_str = switch_core_session_sprintf(session, "set:valet_parking_orbit_context=%s,", orbit_context);
|
|
}
|
|
|
|
if ((timeout = switch_channel_get_variable(channel, "valet_parking_timeout"))) {
|
|
timeout_str = switch_core_session_sprintf(session, "set:valet_parking_timeout=%s,", timeout);
|
|
}
|
|
|
|
dest = switch_core_session_sprintf(session, "%s%s%s%s"
|
|
"set:valet_ticket=%s,set:valet_hold_music='%s',sleep:1000,valet_park:%s %s",
|
|
timeout_str,
|
|
orbit_exten_str,
|
|
orbit_dialplan_str,
|
|
orbit_context_str,
|
|
token->uuid, music, lot_name, ext);
|
|
switch_channel_set_variable(channel, "inline_destination", dest);
|
|
|
|
if (is_auto) {
|
|
char tmp[512] = "";
|
|
switch_snprintf(tmp, sizeof(tmp), "%s:%s", lot_name, ext);
|
|
|
|
if ((uuid = switch_channel_get_partner_uuid(channel))) {
|
|
switch_core_session_t *b_session;
|
|
|
|
if ((b_session = switch_core_session_locate(uuid))) {
|
|
token->timeout = switch_epoch_time_now(NULL) + TOKEN_FREQ;
|
|
if (play_announce) {
|
|
switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
|
|
switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
|
|
}
|
|
switch_ivr_session_transfer(b_session, dest, "inline", NULL);
|
|
switch_core_session_rwunlock(b_session);
|
|
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
if (play_announce) {
|
|
switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL);
|
|
switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hold");
|
|
switch_channel_event_set_data(channel, event);
|
|
switch_event_fire(&event);
|
|
}
|
|
|
|
switch_channel_set_variable(channel, "valet_lot_extension", ext);
|
|
|
|
valet_send_presence(lot_name, lot, token, SWITCH_TRUE);
|
|
|
|
if ((rf.exten = switch_channel_get_variable(channel, "valet_parking_orbit_exten"))) {
|
|
to_val = 60;
|
|
}
|
|
|
|
if ((var = switch_channel_get_variable(channel, "valet_parking_timeout"))) {
|
|
long tmp = atol(var);
|
|
|
|
if (tmp > 0) {
|
|
to_val = tmp;
|
|
}
|
|
}
|
|
|
|
if (to_val) {
|
|
switch_codec_implementation_t read_impl;
|
|
switch_core_session_get_read_impl(session, &read_impl);
|
|
|
|
rf.to = (1000 / (read_impl.microseconds_per_packet / 1000)) * to_val;
|
|
rf.dp = switch_channel_get_variable(channel, "valet_parking_orbit_dialplan");
|
|
rf.context = switch_channel_get_variable(channel, "valet_parking_orbit_context");
|
|
}
|
|
|
|
|
|
args.input_callback = valet_on_dtmf;
|
|
args.buf = dbuf;
|
|
args.buflen = sizeof(dbuf);
|
|
|
|
if (rf.to) {
|
|
args.read_frame_callback = read_frame_callback;
|
|
args.user_data = &rf;
|
|
}
|
|
|
|
while(switch_channel_ready(channel)) {
|
|
switch_status_t pstatus = switch_ivr_play_file(session, NULL, music, &args);
|
|
|
|
if (rf.to == -1) {
|
|
if (!zstr(rf.exten)) {
|
|
switch_ivr_session_transfer(session, rf.exten, rf.dp, rf.context);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (pstatus == SWITCH_STATUS_BREAK || pstatus == SWITCH_STATUS_TIMEOUT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (token) {
|
|
token->timeout = 1;
|
|
valet_send_presence(lot_name, lot, token, SWITCH_FALSE);
|
|
token = NULL;
|
|
}
|
|
|
|
|
|
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit");
|
|
switch_channel_event_set_data(channel, event);
|
|
switch_event_fire(&event);
|
|
}
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX);
|
|
}
|
|
|
|
end:
|
|
|
|
if (token) {
|
|
token->timeout = 1;
|
|
}
|
|
|
|
}
|
|
|
|
SWITCH_STANDARD_API(valet_info_function)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
const void *var;
|
|
void *val;
|
|
char *name;
|
|
valet_lot_t *lot;
|
|
|
|
stream->write_function(stream, "<lots>\n");
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
|
|
switch_hash_index_t *i_hi;
|
|
const void *i_var;
|
|
void *i_val;
|
|
char *i_ext;
|
|
|
|
switch_core_hash_this(hi, &var, NULL, &val);
|
|
name = (char *) var;
|
|
lot = (valet_lot_t *) val;
|
|
|
|
if (!zstr(cmd) && strcasecmp(cmd, name))
|
|
continue;
|
|
|
|
stream->write_function(stream, " <lot name=\"%s\">\n", name);
|
|
|
|
switch_mutex_lock(lot->mutex);
|
|
for (i_hi = switch_core_hash_first(lot->hash); i_hi; i_hi = switch_core_hash_next(&i_hi)) {
|
|
valet_token_t *token;
|
|
|
|
switch_core_hash_this(i_hi, &i_var, NULL, &i_val);
|
|
i_ext = (char *) i_var;
|
|
token = (valet_token_t *) i_val;
|
|
|
|
if (!token->timeout) {
|
|
stream->write_function(stream, " <extension uuid=\"%s\">%s</extension>\n", token->uuid, i_ext);
|
|
}
|
|
}
|
|
switch_mutex_unlock(lot->mutex);
|
|
|
|
stream->write_function(stream, " </lot>\n");
|
|
}
|
|
|
|
stream->write_function(stream, "</lots>\n");
|
|
|
|
switch_mutex_unlock(globals.mutex);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static void pres_event_handler(switch_event_t *event)
|
|
{
|
|
char *to = switch_event_get_header(event, "to");
|
|
char *dup_to = NULL, *lot_name, *dup_lot_name = NULL, *domain_name;
|
|
valet_lot_t *lot;
|
|
int found = 0;
|
|
const char *call_id;
|
|
|
|
if (!to || strncasecmp(to, "park+", 5) || !strchr(to, '@')) {
|
|
return;
|
|
}
|
|
|
|
if (!(dup_to = strdup(to))) {
|
|
return;
|
|
}
|
|
|
|
lot_name = dup_to + 5;
|
|
|
|
if ((domain_name = strchr(lot_name, '@'))) {
|
|
*domain_name++ = '\0';
|
|
}
|
|
|
|
call_id = switch_event_get_header(event, "sub-call-id");
|
|
|
|
dup_lot_name = switch_mprintf("%q@%q", lot_name, domain_name);
|
|
|
|
if ((lot = valet_find_lot(lot_name, SWITCH_FALSE)) || (dup_lot_name && (lot = valet_find_lot(dup_lot_name, SWITCH_FALSE)))) {
|
|
int count = valet_lot_count(lot);
|
|
|
|
if (count) {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", count, count == 1 ? "" : "s");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "active");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
if (call_id) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
|
}
|
|
switch_event_fire(&event);
|
|
}
|
|
found++;
|
|
} else {
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
if (call_id) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
|
}
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
} else {
|
|
switch_console_callback_match_t *matches = NULL;
|
|
switch_console_callback_match_node_t *m;
|
|
switch_hash_index_t *hi;
|
|
const void *var;
|
|
void *val;
|
|
const char *nvar;
|
|
|
|
switch_mutex_lock(globals.mutex);
|
|
for (hi = switch_core_hash_first(globals.hash); hi; hi = switch_core_hash_next(&hi)) {
|
|
switch_core_hash_this(hi, &var, NULL, &val);
|
|
nvar = (const char *) var;
|
|
|
|
if (!strchr(nvar, '@') || switch_stristr(domain_name, nvar)) {
|
|
switch_console_push_match(&matches, nvar);
|
|
}
|
|
}
|
|
switch_mutex_unlock(globals.mutex);
|
|
|
|
if (matches) {
|
|
valet_token_t *token;
|
|
|
|
for (m = matches->head; !found && m; m = m->next) {
|
|
lot = valet_find_lot(m->val, SWITCH_FALSE);
|
|
switch_mutex_lock(lot->mutex);
|
|
|
|
if ((token = (valet_token_t *) switch_core_hash_find(lot->hash, lot_name)) && !token->timeout) {
|
|
found++;
|
|
|
|
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", token->bridged == 0 ? "Holding" : "Active");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "confirmed");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", token->bridged == 0 ? "outbound" : "inbound");
|
|
if (call_id) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
|
}
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
|
|
switch_mutex_unlock(lot->mutex);
|
|
}
|
|
switch_console_free_matches(&matches);
|
|
}
|
|
}
|
|
|
|
|
|
if (!found && switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", VALET_PROTO);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", lot_name);
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", lot_name, domain_name);
|
|
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Empty");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
|
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", lot_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", "inbound");
|
|
if (call_id) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-id", call_id);
|
|
}
|
|
switch_event_fire(&event);
|
|
}
|
|
|
|
switch_safe_free(dup_to);
|
|
switch_safe_free(dup_lot_name);
|
|
}
|
|
|
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_valet_parking_shutdown)
|
|
{
|
|
switch_event_unbind_callback(pres_event_handler);
|
|
switch_core_hash_destroy(&globals.hash);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Macro expands to: switch_status_t mod_valet_parking_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load)
|
|
{
|
|
switch_application_interface_t *app_interface;
|
|
switch_api_interface_t *api_interface;
|
|
|
|
/* create/register custom event message type */
|
|
if (switch_event_reserve_subclass(VALET_EVENT) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", VALET_EVENT);
|
|
return SWITCH_STATUS_TERM;
|
|
}
|
|
|
|
switch_event_bind(modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL);
|
|
|
|
memset(&globals, 0, sizeof(globals));
|
|
|
|
globals.pool = pool;
|
|
switch_core_hash_init(&globals.hash);
|
|
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
|
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
|
|
|
SWITCH_ADD_APP(app_interface, "valet_park", "valet_park", "valet_park", valet_parking_function, VALET_APP_SYNTAX, SAF_NONE);
|
|
SWITCH_ADD_API(api_interface, "valet_info", "Valet Parking Info", valet_info_function, "[<lot name>]");
|
|
|
|
return SWITCH_STATUS_NOUNLOAD;
|
|
}
|
|
|
|
/* 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 noet
|
|
*/
|