]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add pickup endpoint and app to dptools add pickup/keyname to forked dial, then route...
authorAnthony Minessale <anthm@freeswitch.org>
Fri, 4 May 2012 23:59:25 +0000 (18:59 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Fri, 4 May 2012 23:59:34 +0000 (18:59 -0500)
src/include/switch_types.h
src/mod/applications/mod_dptools/mod_dptools.c
src/mod/endpoints/mod_sofia/sofia_presence.c
src/switch_ivr_originate.c

index 3c1cb1cf7cc31e940ca21b44724888bfcb91264e..47336a800a2ce896b0cf6a2bcc6918ea55ac73cd 100644 (file)
@@ -1211,6 +1211,7 @@ typedef enum {
        CF_SERVICE_VIDEO,
        CF_ZRTP_HASH,
        CF_ZRTP_PASS,
+       CF_CHANNEL_SWAP,
        /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
        /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */
        CF_FLAG_MAX
index 3331226e9d8de1176b3267f6a1239c71355f14d1..4adfca6611a8193054421c10dc4554cf4c243dd2 100755 (executable)
@@ -38,7 +38,8 @@
 #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)
 {
@@ -3094,6 +3095,484 @@ SWITCH_STANDARD_APP(audio_bridge_function)
        }
 }
 
+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,
@@ -3985,6 +4464,14 @@ static char *file_string_supported_formats[SWITCH_MAX_CODECS] = { 0 };
 #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;
@@ -3993,9 +4480,16 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        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);
@@ -4019,6 +4513,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        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);
 
@@ -4193,6 +4692,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        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 */
index 8dc58d1320bdd5957df4eb6804d7d7ef092ba607..6befca3439decea0cafa580e094d2c1854aec492 100644 (file)
@@ -1761,6 +1761,20 @@ static int sofia_dialog_probe_callback(void *pArg, int argc, char **argv, char *
                remote_user = to_user;
                remote_host = local_host;
        }
+       else if (proto && !strcasecmp(proto, "pickup")) {
+               local_user = to_user;
+               local_user_param = switch_mprintf(";proto=%s", proto);
+               event_status = "hold";
+               if (skip_proto) {
+                       buf_to_free = switch_mprintf("sip:%s", to_user);
+               } else {
+                       buf_to_free = switch_mprintf("sip:pickup+%s", to_user);
+               }
+               remote_uri = buf_to_free;
+               strcpy(remote_display_buf, "pickup");
+               remote_user = to_user;
+               remote_host = local_host;
+       }
        else if (proto && !strcasecmp(proto, "conf")) {
                local_user = to_user;
                local_user_param = switch_mprintf(";proto=%s", proto);
@@ -2626,6 +2640,18 @@ static int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char *
                                                        stream.write_function(&stream, "<target uri=\"sip:park+%s\"/>\n", uuid);
                                                }
                                                stream.write_function(&stream, "</remote>\n");
+                                       } else if (!strcasecmp(proto, "pickup")) {
+                                               stream.write_function(&stream, "<local>\n<identity display=\"pickup\">sip:%s@%s;proto=pickup</identity>\n",
+                                                                                         !zstr(clean_to_user) ? clean_to_user : "unknown", host);
+                                               stream.write_function(&stream, "<target uri=\"sip:%s@%s;proto=pickup\">\n", !zstr(clean_to_user) ? clean_to_user : "unknown", host);
+                                               stream.write_function(&stream, "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n</target>\n</local>\n");
+                                               stream.write_function(&stream, "<remote>\n<identity display=\"pickup\">sip:%s</identity>\n", uuid);
+                                               if (skip_proto) {
+                                                       stream.write_function(&stream, "<target uri=\"sip:%s\"/>\n", uuid);
+                                               } else {
+                                                       stream.write_function(&stream, "<target uri=\"sip:pickup+%s\"/>\n", uuid);
+                                               }
+                                               stream.write_function(&stream, "</remote>\n");
                                        } else if (!strcasecmp(proto, "conf")) {
                                                stream.write_function(&stream, "<local>\n<identity display=\"conference\">sip:%s@%s;proto=conference</identity>\n",
                                                                                          !zstr(clean_to_user) ? clean_to_user : "unknown", host);
index 4a3067afc2ab59a87dc5c7e77e719054334c7102..c3da16f07237b27e17bc716c333fbda7f9c2f026 100644 (file)
@@ -3541,6 +3541,25 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
        if (*bleg) {
                switch_channel_t *bchan = switch_core_session_get_channel(*bleg);
 
+
+               if (switch_channel_test_flag(bchan, CF_CHANNEL_SWAP)) {
+                       const char *key = switch_channel_get_variable(bchan, "channel_swap_uuid");
+                       switch_core_session_t *swap_session, *old_session;
+                       
+                       if ((swap_session = switch_core_session_locate(key))) {
+                               switch_channel_clear_flag(bchan, CF_CHANNEL_SWAP);
+                               switch_channel_hangup(bchan, SWITCH_CAUSE_PICKED_OFF);
+
+                               switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(bchan), SWITCH_LOG_DEBUG, "Swapping %s for %s\n",
+                                                                 switch_core_session_get_name(swap_session), switch_channel_get_name(bchan));
+                               
+                               old_session = *bleg;
+                               *bleg = swap_session;
+                               bchan = switch_core_session_get_channel(*bleg);
+                               switch_channel_answer(bchan);
+                               switch_core_session_rwunlock(old_session);
+                       }
+               }
                
 
                if (session && caller_channel) {