From e8cf9c7d35c0572006c149e4a1defa647b4a606c Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Wed, 3 Dec 2014 16:03:58 -0500 Subject: [PATCH] FS-7045 #resolve #comment [mod_rayo] guarantee that dialed call can be joined when answered event is sent. --- src/mod/event_handlers/mod_rayo/mod_rayo.c | 109 +++++++++++++++------ 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 614975ee15..4a840fd610 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -141,6 +141,10 @@ struct rayo_call { switch_event_t *end_event; /** True if ringing event sent to client */ int ringing_sent; + /** true if rayo app has started */ + int rayo_app_started; + /** delayed delivery of answer event because rayo APP wasn't started yet */ + switch_event_t *answer_event; /** True if request to create this call failed */ int dial_request_failed; }; @@ -1167,6 +1171,9 @@ done: if (event) { switch_event_destroy(&event); } + if (call->answer_event) { + switch_event_destroy(&call->answer_event); + } switch_core_hash_destroy(&call->pcps); } @@ -1350,6 +1357,8 @@ static struct rayo_call *rayo_call_init(struct rayo_call *call, switch_memory_po call->dial_request_id = NULL; call->end_event = NULL; call->dial_request_failed = 0; + call->rayo_app_started = 0; + call->answer_event = NULL; switch_core_hash_init(&call->pcps); } @@ -2080,31 +2089,38 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, st /* check if joining to rayo call */ struct rayo_call *b_call = RAYO_CALL_LOCATE(call_uri); - if (!b_call) { - /* not a rayo call */ - response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is gone"); - } else if (!has_call_control(b_call, msg)) { - /* not allowed to join to this call */ - response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED); - } else if (b_call->joined) { - /* don't support multiple joined calls */ - response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported"); + if (b_call) { + if (!call->rayo_app_started) { + /* A-leg not under rayo control yet */ + response = iks_new_error_detailed(node, STANZA_ERROR_UNEXPECTED_REQUEST, "a-leg is not ready to join"); + } else if (!b_call->rayo_app_started) { + /* B-leg not under rayo control yet */ + response = iks_new_error_detailed(node, STANZA_ERROR_UNEXPECTED_REQUEST, "b-leg is not ready to join"); + } else if (!has_call_control(b_call, msg)) { + /* not allowed to join to this call */ + response = iks_new_error(node, STANZA_ERROR_NOT_ALLOWED); + } else if (b_call->joined) { + /* don't support multiple joined calls */ + response = iks_new_error_detailed(node, STANZA_ERROR_CONFLICT, "multiple joined calls not supported"); + } else { + /* bridge this call to call-uri */ + switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass); + if (switch_false(bypass)) { + switch_channel_pre_answer(switch_core_session_get_channel(session)); + } + call->pending_join_request = iks_copy(node); + if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) != SWITCH_STATUS_SUCCESS) { + iks *request = call->pending_join_request; + iks *result = iks_new_error(request, STANZA_ERROR_SERVICE_UNAVAILABLE); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(call->pending_join_request); + } + } RAYO_RELEASE(b_call); } else { - /* bridge this call to call-uri */ - switch_channel_set_variable(switch_core_session_get_channel(session), "bypass_media", bypass); - if (switch_false(bypass)) { - switch_channel_pre_answer(switch_core_session_get_channel(session)); - } - call->pending_join_request = iks_copy(node); - if (switch_ivr_uuid_bridge(rayo_call_get_uuid(call), rayo_call_get_uuid(b_call)) != SWITCH_STATUS_SUCCESS) { - iks *request = call->pending_join_request; - iks *result = iks_new_error(request, STANZA_ERROR_SERVICE_UNAVAILABLE); - call->pending_join_request = NULL; - RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); - iks_delete(call->pending_join_request); - } - RAYO_RELEASE(b_call); + /* not a rayo call */ + response = iks_new_error_detailed(node, STANZA_ERROR_SERVICE_UNAVAILABLE, "b-leg is gone"); } return response; } @@ -2187,7 +2203,10 @@ static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, s iks *node = msg->payload; iks *response = NULL; - if (call->joined_id) { + if (!call->rayo_app_started) { + /* A-leg not under rayo control yet */ + response = iks_new_error_detailed(node, STANZA_ERROR_UNEXPECTED_REQUEST, "call is not ready to join"); + } else if (call->joined_id) { /* adjust join conference params */ if (!strcmp("duplex", direction)) { if ((response = exec_conference_api(session, mixer_name, "unmute", node)) || @@ -3379,10 +3398,17 @@ static void on_call_answer_event(struct rayo_client *rclient, switch_event_t *ev { struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); if (call) { - iks *revent = iks_new_presence("answered", RAYO_NS, - switch_event_get_header(event, "variable_rayo_call_jid"), - switch_event_get_header(event, "variable_rayo_dcp_jid")); - RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + switch_mutex_lock(RAYO_ACTOR(call)->mutex); + if (call->rayo_app_started) { + iks *revent = iks_new_presence("answered", RAYO_NS, + switch_event_get_header(event, "variable_rayo_call_jid"), + switch_event_get_header(event, "variable_rayo_dcp_jid")); + RAYO_SEND_MESSAGE(call, RAYO_JID(rclient), revent); + } else if (!call->answer_event) { + /* delay sending this event until the rayo APP has started */ + switch_event_dup(&call->answer_event, event); + } + switch_mutex_unlock(RAYO_ACTOR(call)->mutex); RAYO_RELEASE(call); } } @@ -3840,6 +3866,13 @@ SWITCH_STANDARD_APP(rayo_app) clients_to_offer_count = switch_separate_string(data_dup, ',', clients_to_offer, sizeof(clients_to_offer) / sizeof(clients_to_offer[0])); } + /* It is now safe for inbound call to be fully controlled by rayo client */ + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) { + switch_mutex_lock(RAYO_ACTOR(call)->mutex); + call->rayo_app_started = 1; + switch_mutex_unlock(RAYO_ACTOR(call)->mutex); + } + /* Offer call to all (or specified) ONLINE clients */ /* TODO load balance offers so first session doesn't always get offer first? */ switch_mutex_lock(globals.clients_mutex); @@ -3880,11 +3913,31 @@ done: switch_channel_set_variable(channel, "hold_hangup_xfer_exten", "park:inline:"); switch_channel_set_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE, "-1"); /* required so that output mixing works */ switch_core_event_hook_add_read_frame(session, rayo_call_on_read_frame); + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + /* At this point, this outbound call might already be under control of a rayo client that is waiting for answer before sending + commands. The answered event might have been sent before we are ready to execute commands, so we delayed sending + those events if the rayo APP hadn't started yet. This delay would have only been a few milliseconds. + */ + switch_mutex_lock(RAYO_ACTOR(call)->mutex); + call->rayo_app_started = 1; + if (call->answer_event) { + struct rayo_client *rclient = RAYO_CLIENT(RAYO_LOCATE(rayo_call_get_dcp_jid(call))); + if (rclient) { + on_call_answer_event(rclient, call->answer_event); + switch_event_destroy(&call->answer_event); + RAYO_RELEASE(rclient); + } + } + switch_mutex_unlock(RAYO_ACTOR(call)->mutex); + + /* Outbound calls might have a nested join to another call or conference - do that now */ if (!zstr(app)) { switch_core_session_execute_application(session, app, app_args); } } + + /* Ready for remote control */ switch_ivr_park(session, NULL); } }