#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load);
-SWITCH_MODULE_DEFINITION(mod_dptools, mod_dptools_load, NULL, NULL);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_dptools_shutdown);
+SWITCH_MODULE_DEFINITION(mod_dptools, mod_dptools_load, mod_dptools_shutdown, NULL);
SWITCH_STANDARD_DIALPLAN(inline_dialplan_hunt)
{
}
}
+static struct {
+ switch_memory_pool_t *pool;
+ switch_hash_t *pickup_hash;
+ switch_mutex_t *pickup_mutex;
+} globals;
+
+/* pickup channel */
+
+
+
+typedef struct pickup_node_s {
+ char *key;
+ char *uuid;
+ struct pickup_node_s *next;
+} pickup_node_t;
+
+
+#define PICKUP_PROTO "pickup"
+static int EC = 0;
+
+static int pickup_count(const char *key_name)
+{
+ int count = 0;
+ pickup_node_t *head, *np;
+
+ switch_mutex_lock(globals.pickup_mutex);
+ if ((head = switch_core_hash_find(globals.pickup_hash, key_name))) {
+ for (np = head; np; np = np->next) count++;
+ }
+ switch_mutex_unlock(globals.pickup_mutex);
+
+ return count;
+
+}
+
+static void pickup_send_presence(const char *key_name)
+{
+
+ char *domain_name, *dup_key_name = NULL, *dup_domain_name = NULL, *dup_id = NULL;
+ switch_event_t *event;
+ int count;
+
+
+ dup_key_name = strdup(key_name);
+ key_name = dup_key_name;
+
+ if ((domain_name = strchr(dup_key_name, '@'))) {
+ *domain_name++ = '\0';
+ }
+
+ if (zstr(domain_name)) {
+ dup_domain_name = switch_core_get_variable_dup("domain");
+ domain_name = dup_domain_name;
+ }
+
+ if (zstr(domain_name)) {
+ domain_name = "cluecon.com";
+ }
+
+ dup_id = switch_mprintf("%s@%s", key_name, domain_name);
+
+ count = pickup_count(dup_id);
+
+ 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", PICKUP_PROTO);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", dup_id);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", dup_id);
+
+
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d call%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", key_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, "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", PICKUP_PROTO);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", dup_id);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", dup_id);
+
+
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Idle");
+ 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", dup_id);
+ 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, "call-direction", "inbound");
+ switch_event_fire(&event);
+ }
+ }
+
+ switch_safe_free(dup_domain_name);
+ switch_safe_free(dup_key_name);
+ switch_safe_free(dup_id);
+
+}
+
+static void pickup_pres_event_handler(switch_event_t *event)
+{
+ char *to = switch_event_get_header(event, "to");
+ char *dup_to = NULL, *key_name, *dup_key_name = NULL, *domain_name, *dup_domain_name = NULL;
+ int count = 0;
+
+ if (!to || strncasecmp(to, "pickup+", 7) || !strchr(to, '@')) {
+ return;
+ }
+
+ if (!(dup_to = strdup(to))) {
+ return;
+ }
+
+ key_name = dup_to + 7;
+
+ if ((domain_name = strchr(key_name, '@'))) {
+ *domain_name++ = '\0';
+ } else {
+ dup_domain_name = switch_core_get_variable_dup("domain");
+ domain_name = dup_domain_name;
+ }
+
+ if (zstr(domain_name)) {
+ switch_safe_free(dup_to);
+ return;
+ }
+
+ dup_key_name = switch_mprintf("%q@%q", key_name, domain_name);
+ count = pickup_count(dup_key_name);
+
+ switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN);
+
+ if (count) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", key_name);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", key_name, domain_name);
+
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d call%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", key_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, "call-direction", "inbound");
+ } else {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", PICKUP_PROTO);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", key_name);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", key_name, domain_name);
+
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Idle");
+ 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", key_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, "call-direction", "inbound");
+
+ }
+
+ switch_event_fire(&event);
+ switch_safe_free(dup_to);
+ switch_safe_free(dup_key_name);
+ switch_safe_free(dup_domain_name);
+}
+
+
+
+static void pickup_add_session(switch_core_session_t *session, const char *key)
+{
+ pickup_node_t *head, *node, *np;
+ char *dup_key = NULL;
+
+ if (!strchr(key, '@')) {
+ dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain"));
+ key = dup_key;
+ }
+
+ node = malloc(sizeof(*node));
+ node->key = strdup(key);
+ node->uuid = strdup(switch_core_session_get_uuid(session));
+ node->next = NULL;
+
+ switch_mutex_lock(globals.pickup_mutex);
+ head = switch_core_hash_find(globals.pickup_hash, key);
+
+ if (head) {
+ for (np = head; np && np->next; np = np->next);
+ np->next = node;
+ } else {
+ head = node;
+ switch_core_hash_insert(globals.pickup_hash, key, head);
+ }
+
+ switch_mutex_unlock(globals.pickup_mutex);
+
+ pickup_send_presence(key);
+
+ switch_safe_free(dup_key);
+}
+
+static char *pickup_pop_uuid(const char *key, const char *uuid)
+{
+ pickup_node_t *node = NULL, *head;
+ char *r = NULL;
+ char *dup_key = NULL;
+
+ if (!strchr(key, '@')) {
+ dup_key = switch_mprintf("%s@%s", key, switch_core_get_variable("domain"));
+ key = dup_key;
+ }
+
+ switch_mutex_lock(globals.pickup_mutex);
+
+ if ((head = switch_core_hash_find(globals.pickup_hash, key))) {
+
+ switch_core_hash_delete(globals.pickup_hash, key);
+
+ if (uuid) {
+ pickup_node_t *np, *lp = NULL;
+
+ for(np = head; np; np = np->next) {
+ if (!strcmp(np->uuid, uuid)) {
+ if (lp) {
+ lp->next = np->next;
+ } else {
+ head = np->next;
+ }
+
+ node = np;
+ break;
+ }
+
+ lp = np;
+ }
+
+ } else {
+ node = head;
+ head = head->next;
+ }
+
+
+ if (head) {
+ switch_core_hash_insert(globals.pickup_hash, key, head);
+ }
+ }
+
+ if (node) {
+ r = node->uuid;
+ free(node->key);
+ free(node);
+ }
+
+ switch_mutex_unlock(globals.pickup_mutex);
+
+ if (r) pickup_send_presence(key);
+
+ switch_safe_free(dup_key);
+
+ return r;
+}
+
+
+typedef struct pickup_pvt_s {
+ char *key;
+ switch_event_t *vars;
+} pickup_pvt_t;
+
+switch_endpoint_interface_t *pickup_endpoint_interface;
+static switch_call_cause_t pickup_outgoing_channel(switch_core_session_t *session,
+ switch_event_t *var_event,
+ switch_caller_profile_t *outbound_profile,
+ switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
+ switch_call_cause_t *cancel_cause);
+switch_io_routines_t pickup_io_routines = {
+ /*.outgoing_channel */ pickup_outgoing_channel
+};
+
+static switch_status_t pickup_event_handler(switch_core_session_t *session)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_channel_state_t state = switch_channel_get_running_state(channel);
+ pickup_pvt_t *tech_pvt = switch_core_session_get_private(session);
+
+ switch(state) {
+ case CS_DESTROY:
+ if (tech_pvt->vars) {
+ switch_event_destroy(&tech_pvt->vars);
+ }
+ break;
+ case CS_REPORTING:
+ return SWITCH_STATUS_FALSE;
+ case CS_HANGUP:
+ {
+
+ if (switch_channel_test_flag(channel, CF_CHANNEL_SWAP)) {
+ const char *key = switch_channel_get_variable(channel, "channel_swap_uuid");
+ switch_core_session_t *swap_session;
+
+ if ((swap_session = switch_core_session_locate(key))) {
+ switch_channel_t *swap_channel = switch_core_session_get_channel(swap_session);
+ switch_channel_hangup(swap_channel, SWITCH_CAUSE_PICKED_OFF);
+ switch_core_session_rwunlock(swap_session);
+ }
+ switch_channel_clear_flag(channel, CF_CHANNEL_SWAP);
+ }
+
+ pickup_pop_uuid(tech_pvt->key, switch_core_session_get_uuid(session));
+ }
+ break;
+ default:
+ break;
+ }
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_state_handler_table_t pickup_event_handlers = {
+ /*.on_init */ pickup_event_handler,
+ /*.on_routing */ pickup_event_handler,
+ /*.on_execute */ pickup_event_handler,
+ /*.on_hangup */ pickup_event_handler,
+ /*.on_exchange_media */ pickup_event_handler,
+ /*.on_soft_execute */ pickup_event_handler,
+ /*.on_consume_media */ pickup_event_handler,
+ /*.on_hibernate */ pickup_event_handler,
+ /*.on_reset */ pickup_event_handler,
+ /*.on_park */ pickup_event_handler,
+ /*.on_reporting */ pickup_event_handler,
+ /*.on_destroy */ pickup_event_handler
+};
+
+static switch_call_cause_t pickup_outgoing_channel(switch_core_session_t *session,
+ switch_event_t *var_event,
+ switch_caller_profile_t *outbound_profile,
+ switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
+ switch_call_cause_t *cancel_cause)
+{
+ char *pickup;
+ switch_call_cause_t cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
+ switch_core_session_t *nsession;
+ switch_channel_t *nchannel;
+ char *name;
+ pickup_pvt_t *tech_pvt;
+ switch_caller_profile_t *caller_profile;
+
+ if (zstr(outbound_profile->destination_number)) {
+ goto done;
+ }
+
+ pickup = outbound_profile->destination_number;
+
+
+ if (!(nsession = switch_core_session_request(pickup_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n");
+ goto error;
+ }
+
+ tech_pvt = switch_core_session_alloc(nsession, sizeof(*tech_pvt));
+ tech_pvt->key = switch_core_session_strdup(nsession, pickup);
+ switch_event_dup(&tech_pvt->vars, var_event);
+
+ switch_core_session_set_private(nsession, tech_pvt);
+
+ nchannel = switch_core_session_get_channel(nsession);
+ caller_profile = switch_caller_profile_clone(nsession, outbound_profile);
+ switch_channel_set_caller_profile(nchannel, caller_profile);
+
+ switch_channel_set_state(nchannel, CS_ROUTING);
+
+
+
+ *new_session = nsession;
+ cause = SWITCH_CAUSE_SUCCESS;
+ name = switch_core_session_sprintf(nsession, "pickup/%s", pickup);
+ switch_channel_set_name(nchannel, name);
+ switch_channel_set_variable(nchannel, "process_cdr", "false");
+ pickup_add_session(nsession, pickup);
+
+ goto done;
+
+ error:
+
+ if (nsession) {
+ switch_core_session_destroy(&nsession);
+ }
+
+ if (pool) {
+ *pool = NULL;
+ }
+
+ done:
+
+
+ return cause;
+}
+
+#define PICKUP_SYNTAX "[<key>]"
+SWITCH_STANDARD_APP(pickup_function)
+{
+ char *uuid = NULL;
+ switch_core_session_t *pickup_session;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+ if (zstr(data)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing data. Usage: pickup %s\n", PICKUP_SYNTAX);
+ return;
+ }
+
+ if ((uuid = pickup_pop_uuid((char *)data, NULL))) {
+ if ((pickup_session = switch_core_session_locate(uuid))) {
+ switch_channel_t *pickup_channel = switch_core_session_get_channel(pickup_session);
+ switch_caller_profile_t *pickup_caller_profile = switch_channel_get_caller_profile(pickup_channel),
+ *caller_profile = switch_channel_get_caller_profile(channel);
+ const char *name, *num;
+ switch_event_t *event;
+ switch_event_header_t *hp;
+ pickup_pvt_t *tech_pvt = switch_core_session_get_private(pickup_session);
+
+ for(hp = tech_pvt->vars->headers; hp; hp = hp->next) {
+ switch_channel_set_variable(channel, hp->name, hp->value);
+ }
+
+
+ switch_channel_set_flag(pickup_channel, CF_CHANNEL_SWAP);
+ switch_channel_set_variable(pickup_channel, "channel_swap_uuid", switch_core_session_get_uuid(session));
+
+ name = caller_profile->caller_id_name;
+ num = caller_profile->caller_id_number;
+
+ caller_profile->caller_id_name = switch_core_strdup(caller_profile->pool, pickup_caller_profile->caller_id_name);
+ caller_profile->caller_id_number = switch_core_strdup(caller_profile->pool, pickup_caller_profile->caller_id_number);
+
+ caller_profile->callee_id_name = name;
+ caller_profile->callee_id_number = num;
+
+ if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) {
+ const char *uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "RECV");
+
+ if (!uuid) {
+ uuid = switch_channel_get_variable(channel, "originate_signal_bond");
+ }
+
+
+ if (uuid) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridged-To", uuid);
+ }
+ switch_channel_event_set_data(channel, event);
+ switch_event_fire(&event);
+ }
+
+
+ switch_channel_set_state(channel, CS_HIBERNATE);
+
+ switch_channel_mark_answered(pickup_channel);
+ switch_core_session_rwunlock(pickup_session);
+ }
+ free(uuid);
+ }
+}
+
+
+
+
+
/* fake chan_error */
switch_endpoint_interface_t *error_endpoint_interface;
static switch_call_cause_t error_outgoing_channel(switch_core_session_t *session,
#define LOG_LONG_DESC "Logs a channel variable for the channel calling the application."
#define TRANSFER_LONG_DESC "Immediately transfer the calling channel to a new extension"
#define SLEEP_LONG_DESC "Pause the channel for a given number of milliseconds, consuming the audio for that period of time."
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_dptools_shutdown)
+{
+ switch_event_unbind_callback(pickup_pres_event_handler);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
{
switch_api_interface_t *api_interface;
switch_chat_interface_t *chat_interface;
switch_file_interface_t *file_interface;
+ globals.pool = pool;
+ switch_core_hash_init(&globals.pickup_hash, NULL);
+ switch_mutex_init(&globals.pickup_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_event_bind(modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, pickup_pres_event_handler, NULL);
+
+
file_string_supported_formats[0] = "file_string";
file_interface = (switch_file_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
user_endpoint_interface->interface_name = "user";
user_endpoint_interface->io_routines = &user_io_routines;
+ pickup_endpoint_interface = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
+ pickup_endpoint_interface->interface_name = "pickup";
+ pickup_endpoint_interface->io_routines = &pickup_io_routines;
+ pickup_endpoint_interface->state_handler = &pickup_event_handlers;
+
SWITCH_ADD_CHAT(chat_interface, "event", event_chat_send);
SWITCH_ADD_CHAT(chat_interface, "api", api_chat_send);
SWITCH_ADD_APP(app_interface, "limit_execute", "Limit", LIMITEXECUTE_DESC, limit_execute_function, LIMITEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "limit_hash_execute", "Limit", LIMITHASHEXECUTE_DESC, limit_hash_execute_function, LIMITHASHEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_APP(app_interface, "pickup", "Pickup", "Pickup a call", pickup_function, PICKUP_SYNTAX, SAF_SUPPORT_NOMEDIA);
+
+
SWITCH_ADD_DIALPLAN(dp_interface, "inline", inline_dialplan_hunt);
/* indicate that the module should continue to be loaded */