diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 92257aa2f6..df1ce007c9 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -941,10 +941,11 @@ static void rayo_call_cleanup(struct rayo_actor *actor) /* lost the race: pending join failed... send IQ result to client now. */ if (call->pending_join_request) { - iks *result = iks_new_error_detailed(call->pending_join_request, STANZA_ERROR_ITEM_NOT_FOUND, "call ended"); - RAYO_SEND_REPLY(call, iks_find_attrib_soft(call->pending_join_request, "from"), result); - iks_delete(call->pending_join_request); + iks *request = call->pending_join_request; + iks *result = iks_new_error_detailed(request, STANZA_ERROR_ITEM_NOT_FOUND, "call ended"); call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(call->pending_join_request); } iks_delete(revent); @@ -1765,9 +1766,11 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, st } 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) { - response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to bridge call"); - iks_delete(call->pending_join_request); + iks *request = call->pending_join_request; + iks *result = iks_new_error_detailed(request, STANZA_ERROR_ITEM_NOT_FOUND, "failed to bridge call"); call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(call->pending_join_request); } RAYO_UNLOCK(b_call); } @@ -1776,14 +1779,66 @@ static iks *join_call(struct rayo_call *call, switch_core_session_t *session, st /** * Execute command on session's conference + * @param session to execute conference API on + * @param conf_name of conference + * @param command to send to conference + * @param node IQ request + * @return response on failure */ -static void exec_conference_api(switch_core_session_t *session, const char *conf_name, const char *command) +static iks *exec_conference_api(switch_core_session_t *session, const char *conf_name, const char *command, iks *node) { + iks *response = NULL; switch_stream_handle_t stream = { 0 }; const char *conf_member_id = switch_channel_get_variable(switch_core_session_get_channel(session), "conference_member_id"); SWITCH_STANDARD_STREAM(stream); switch_api_execute("conference", switch_core_session_sprintf(session, "%s %s %s", conf_name, command, conf_member_id), NULL, &stream); + if (!zstr(stream.data) && strncmp("OK", stream.data, 2)) { + response = iks_new_error_detailed_printf(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "%s", stream.data); + } switch_safe_free(stream.data); + return response; +} + +/** + * Execute conference app on session + * @param session to execute conference API on + * @param command to send to conference (conference name, member flags, etc) + * @param node IQ request + * @return response on failure + */ +static iks *exec_conference_app(switch_core_session_t *session, const char *command, iks *node) +{ + iks *response = NULL; + switch_event_t *execute_event = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + + /* conference requires local media on channel */ + if (!switch_channel_media_ready(channel) && switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + /* shit */ + response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to start media"); + return response; + } + + /* send execute conference event to session */ + if (switch_event_create(&execute_event, SWITCH_EVENT_COMMAND) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-name", "conference"); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-arg", command); + //switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "event_uuid", uuid); + switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "event-lock", "true"); + if (!switch_channel_test_flag(channel, CF_PROXY_MODE)) { + switch_channel_set_flag(channel, CF_BLOCK_BROADCAST_UNTIL_MEDIA); + } + + if (switch_core_session_queue_private_event(session, &execute_event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to join mixer (queue event failed)"); + if (execute_event) { + switch_event_destroy(&execute_event); + } + return response; + } + } + return response; } /** @@ -1803,14 +1858,20 @@ static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, s if (call->joined_id) { /* adjust join conference params */ if (!strcmp("duplex", direction)) { - exec_conference_api(session, mixer_name, "unmute"); - exec_conference_api(session, mixer_name, "undeaf"); + if ((response = exec_conference_api(session, mixer_name, "unmute", node)) || + (response = exec_conference_api(session, mixer_name, "undeaf", node))) { + return response; + } } else if (!strcmp("recv", direction)) { - exec_conference_api(session, mixer_name, "mute"); - exec_conference_api(session, mixer_name, "undeaf"); + if ((response = exec_conference_api(session, mixer_name, "mute", node)) || + (response = exec_conference_api(session, mixer_name, "undeaf", node))) { + return response; + } } else { - exec_conference_api(session, mixer_name, "unmute"); - exec_conference_api(session, mixer_name, "deaf"); + if ((response = exec_conference_api(session, mixer_name, "unmute", node)) || + (response = exec_conference_api(session, mixer_name, "deaf", node))) { + return response; + } } response = iks_new_iq_result(node); } else { @@ -1821,10 +1882,12 @@ static iks *join_mixer(struct rayo_call *call, switch_core_session_t *session, s } else if (!strcmp("recv", direction)) { conf_args = switch_core_session_sprintf(session, "%s+flags{mute}", conf_args); } - if (switch_core_session_execute_application_async(session, "conference", conf_args) == SWITCH_STATUS_SUCCESS) { - response = iks_new_iq_result(node); - } else { - response = iks_new_error_detailed(node, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed execute conference app"); + + call->pending_join_request = iks_copy(node); + response = exec_conference_app(session, conf_args, node); + if (response) { + iks_delete(call->pending_join_request); + call->pending_join_request = NULL; } } return response; @@ -1951,11 +2014,12 @@ static iks *unjoin_mixer(struct rayo_call *call, switch_core_session_t *session, goto done; } - /* ack command */ - response = iks_new_iq_result(node); - /* kick the member */ - exec_conference_api(session, mixer_name, "hup"); + response = exec_conference_api(session, mixer_name, "hup", node); + if (!response) { + /* ack command */ + response = iks_new_iq_result(node); + } done: @@ -2538,6 +2602,15 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * call->joined = JOINED_MIXER; call->joined_id = switch_core_strdup(RAYO_POOL(call), rayo_mixer_get_name(mixer)); + /* send IQ result to client now. */ + if (call->pending_join_request) { + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); + call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); + } + /* send mixer joined event to member DCP */ add_member_event = iks_new_presence("joined", RAYO_NS, RAYO_JID(call), call->dcp_jid); x = iks_find(add_member_event, "joined"); @@ -2705,14 +2778,13 @@ static void on_call_bridge_event(struct rayo_client *rclient, switch_event_t *ev call->joined_id = switch_core_strdup(RAYO_POOL(call), b_uuid); /* send IQ result to client now. */ - switch_mutex_lock(RAYO_ACTOR(call)->mutex); if (call->pending_join_request) { - iks *result = iks_new_iq_result(call->pending_join_request); - RAYO_SEND_REPLY(call, iks_find_attrib_soft(call->pending_join_request, "from"), result); - iks_delete(call->pending_join_request); + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); } - switch_mutex_unlock(RAYO_ACTOR(call)->mutex); /* send A-leg event */ revent = iks_new_presence("joined", RAYO_NS, @@ -2760,14 +2832,13 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t * call->joined_id = NULL; /* send IQ result to client now. */ - switch_mutex_lock(RAYO_ACTOR(call)->mutex); if (call->pending_join_request) { - iks *result = iks_new_iq_result(call->pending_join_request); - RAYO_SEND_REPLY(call, iks_find_attrib_soft(call->pending_join_request, "from"), result); - iks_delete(call->pending_join_request); + iks *request = call->pending_join_request; + iks *result = iks_new_iq_result(request); call->pending_join_request = NULL; + RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); + iks_delete(request); } - switch_mutex_unlock(RAYO_ACTOR(call)->mutex); /* send A-leg event */ revent = iks_new_presence("unjoined", RAYO_NS, @@ -2793,6 +2864,36 @@ static void on_call_unbridge_event(struct rayo_client *rclient, switch_event_t * } } +/** + * Handle call execute application event + * @param rclient the Rayo client + * @param event the execute event + */ +static void on_call_execute_event(struct rayo_client *rclient, switch_event_t *event) +{ + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); + if (call) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_ID(call)), SWITCH_LOG_DEBUG, "Application %s execute\n", switch_event_get_header(event, "Application")); + RAYO_UNLOCK(call); + } +} + +/** + * Handle call execute application complete event + * @param rclient the Rayo client + * @param event the execute complete event + */ +static void on_call_execute_complete_event(struct rayo_client *rclient, switch_event_t *event) +{ + struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(switch_event_get_header(event, "Unique-ID")); + if (call) { + const char *app = switch_event_get_header(event, "Application"); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_ID(call)), SWITCH_LOG_DEBUG, "Application %s execute complete: %s \n", + app, + switch_event_get_header(event, "Application-Response")); + RAYO_UNLOCK(call); + } +} /** * Handle events to deliver to client connection @@ -2819,6 +2920,12 @@ static void rayo_client_handle_event(struct rayo_client *rclient, switch_event_t case SWITCH_EVENT_CHANNEL_UNBRIDGE: on_call_unbridge_event(rclient, event); break; + case SWITCH_EVENT_CHANNEL_EXECUTE: + on_call_execute_event(rclient, event); + break; + case SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE: + on_call_execute_complete_event(rclient, event); + break; default: /* don't care */ break; @@ -2944,7 +3051,9 @@ static switch_status_t rayo_call_on_read_frame(switch_core_session_t *session, s switch_time_t idle_start = call->idle_start_time; int idle_duration_ms = (now - idle_start) / 1000; /* detect idle session (rayo-client has stopped controlling call) and terminate call */ - if (!rayo_call_is_joined(call) && idle_duration_ms > globals.max_idle_ms) { + if (rayo_call_is_joined(call)) { + call->idle_start_time = now; + } else if (idle_duration_ms > globals.max_idle_ms) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Ending abandoned call. idle_duration_ms = %i ms\n", idle_duration_ms); switch_channel_hangup(channel, RAYO_CAUSE_HANGUP); } @@ -3891,6 +4000,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_rayo_load) switch_event_bind(modname, SWITCH_EVENT_CHANNEL_ANSWER, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_BRIDGE, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_UNBRIDGE, NULL, route_call_event, NULL); + switch_event_bind(modname, SWITCH_EVENT_CHANNEL_EXECUTE, NULL, route_call_event, NULL); + switch_event_bind(modname, SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE, NULL, route_call_event, NULL); switch_event_bind(modname, SWITCH_EVENT_CHANNEL_DESTROY, NULL, on_call_end_event, NULL);