/** * Start execution of call output component * @param component to start * @param session the session to output to * @param output the output request * @param iq the original request */ static iks *start_call_output(struct rayo_component *component, switch_core_session_t *session, iks *output, iks *iq) { switch_stream_handle_t stream = { 0 }; /* acknowledge command */ rayo_component_send_start(component, iq); /* build playback command */ SWITCH_STANDARD_STREAM(stream); stream.write_function(&stream, "{id=%s,session=%s,pause=%s", RAYO_JID(component), switch_core_session_get_uuid(session), OUTPUT_COMPONENT(component)->start_paused ? "true" : "false"); if (OUTPUT_COMPONENT(component)->max_time > 0) { stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time * 1000); } stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component)); if (switch_ivr_displace_session(session, stream.data, 0, "m") == SWITCH_STATUS_SUCCESS) { RAYO_UNLOCK(component); } else { if (OUTPUT_COMPONENT(component)->document) { iks_delete(OUTPUT_COMPONENT(component)->document); } if (switch_channel_get_state(switch_core_session_get_channel(session)) >= CS_HANGUP) { rayo_component_send_complete(component, COMPONENT_COMPLETE_HANGUP); component = NULL; } else { rayo_component_send_complete(component, COMPONENT_COMPLETE_ERROR); component = NULL; } } switch_safe_free(stream.data); return NULL; }
/** * Pass output component command */ static iks *forward_output_component_request(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) %s prompt\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state), iks_name(iks_first_tag(iq))); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_OUTPUT: case PCS_START_INPUT_OUTPUT: case PCS_INPUT_OUTPUT: { /* forward request to output component */ iks_insert_attrib(iq, "from", RAYO_JID(prompt)); iks_insert_attrib(iq, "to", PROMPT_COMPONENT(prompt)->output_jid); RAYO_SEND_MESSAGE_DUP(prompt, PROMPT_COMPONENT(prompt)->output_jid, iq); return NULL; } case PCS_START_INPUT_TIMERS: case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: /* ref hasn't been sent yet */ return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "too soon"); break; case PCS_START_INPUT: case PCS_STOP_OUTPUT: case PCS_DONE_STOP_OUTPUT: case PCS_INPUT: case PCS_DONE: return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "output is finished"); } return NULL; }
/** * Handle barge event */ static iks *prompt_component_handle_input_barge(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *presence = msg->payload; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input barge\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_INPUT_OUTPUT: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, got <start-of-input> from %s: %s\n", RAYO_JID(prompt), msg->from_jid, iks_string(iks_stack(presence), presence)); PROMPT_COMPONENT(prompt)->state = PCS_STOP_OUTPUT; rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output_jid); break; case PCS_STOP_OUTPUT: case PCS_INPUT: /* don't care */ break; case PCS_OUTPUT: case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: case PCS_START_INPUT: case PCS_START_INPUT_OUTPUT: case PCS_START_INPUT_TIMERS: case PCS_DONE_STOP_OUTPUT: case PCS_DONE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt)); break; } return NULL; }
/** * Handle input failure. */ static iks *prompt_component_handle_input_error(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; iks *error = iks_find(iq, "error"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input error\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_START_INPUT_TIMERS: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, <input> error: %s\n", RAYO_JID(prompt), iks_string(iks_stack(iq), iq)); PROMPT_COMPONENT(prompt)->state = PCS_DONE; /* forward IQ error to client */ iq = PROMPT_COMPONENT(prompt)->iq; iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); iks_insert_node(iq, iks_copy_within(error, iks_stack(iq))); RAYO_SEND_REPLY(prompt, RAYO_COMPONENT(prompt)->client_jid, iq); /* done */ RAYO_UNLOCK(prompt); RAYO_DESTROY(prompt); break; case PCS_START_INPUT: /* send presence error to client */ PROMPT_COMPONENT(prompt)->state = PCS_DONE; iks_delete(PROMPT_COMPONENT(prompt)->iq); rayo_component_send_complete(RAYO_COMPONENT(prompt), COMPONENT_COMPLETE_ERROR); break; case PCS_START_INPUT_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT; /* forward IQ error to client */ iq = PROMPT_COMPONENT(prompt)->iq; iks_insert_attrib(iq, "from", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); iks_insert_node(iq, iks_copy_within(error, iks_stack(iq))); PROMPT_COMPONENT(prompt)->complete = iq; rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output_jid); break; case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: case PCS_INPUT_OUTPUT: case PCS_STOP_OUTPUT: case PCS_INPUT: case PCS_OUTPUT: case PCS_DONE_STOP_OUTPUT: case PCS_DONE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start input error event\n", RAYO_JID(prompt)); break; } return NULL; }
/** * Lower volume of output component */ static iks *volume_down_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data) { iks *iq = msg->payload; switch_stream_handle_t stream = { 0 }; char *command = switch_mprintf("%s volume:-", RAYO_JID(component)); SWITCH_STANDARD_STREAM(stream); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s lowering volume\n", RAYO_JID(component)); switch_api_execute("fileman", command, NULL, &stream); switch_safe_free(stream.data); switch_safe_free(command); return iks_new_iq_result(iq); }
/** * Start execution of prompt component */ static iks *start_call_prompt_component(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; switch_memory_pool_t *pool; struct prompt_component *prompt_component = NULL; iks *prompt = iks_find(iq, "prompt"); iks *input; iks *output; iks *cmd; if (!VALIDATE_RAYO_PROMPT(prompt)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Bad <prompt> attrib\n"); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Bad <prompt> attrib value"); } output = iks_find(prompt, "output"); if (!output) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Missing <output>\n"); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing <output>"); } input = iks_find(prompt, "input"); if (!input) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Missing <input>\n"); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing <input>"); } /* create prompt component, linked to call */ switch_core_new_memory_pool(&pool); prompt_component = switch_core_alloc(pool, sizeof(*prompt_component)); rayo_component_init(RAYO_COMPONENT(prompt_component), pool, RAT_CALL_COMPONENT, "prompt", NULL, call, iks_find_attrib(iq, "from")); prompt_component->iq = iks_copy(iq); /* start output */ if (iks_find_bool_attrib(prompt, "barge-in")) { prompt_component->state = PCS_START_OUTPUT_BARGE; } else { prompt_component->state = PCS_START_OUTPUT; } cmd = iks_new("iq"); iks_insert_attrib(cmd, "from", RAYO_JID(prompt_component)); iks_insert_attrib(cmd, "to", RAYO_JID(call)); iks_insert_attrib(cmd, "id", iks_find_attrib(iq, "id")); iks_insert_attrib(cmd, "type", "set"); output = iks_copy_within(output, iks_stack(cmd)); iks_insert_node(cmd, output); RAYO_SEND_MESSAGE(prompt_component, RAYO_JID(call), cmd); return NULL; }
/** * Start execution of mixer output component */ static iks *start_mixer_output_component(struct rayo_actor *mixer, struct rayo_message *msg, void *data) { iks *iq = msg->payload; struct rayo_component *component = NULL; iks *output = iks_find(iq, "output"); iks *document = NULL; switch_stream_handle_t stream = { 0 }; /* validate output attributes */ if (!VALIDATE_RAYO_OUTPUT(output)) { return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } /* check if <document> exists */ document = iks_find(output, "document"); if (!document) { return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } component = create_output_component(mixer, RAT_MIXER_COMPONENT, output, iks_find_attrib(iq, "from")); if (!component) { return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to create output entity"); } /* build conference command */ SWITCH_STANDARD_STREAM(stream); stream.write_function(&stream, "%s play ", rayo_mixer_get_name(RAYO_MIXER(mixer)), RAYO_ID(component)); stream.write_function(&stream, "{id=%s,pause=%s", RAYO_JID(component), OUTPUT_COMPONENT(component)->start_paused ? "true" : "false"); if (OUTPUT_COMPONENT(component)->max_time_ms > 0) { stream.write_function(&stream, ",timeout=%i", OUTPUT_COMPONENT(component)->max_time_ms); } if (OUTPUT_COMPONENT(component)->start_offset_ms > 0) { stream.write_function(&stream, ",start_offset_ms=%i", OUTPUT_COMPONENT(component)->start_offset_ms); } stream.write_function(&stream, "}fileman://rayo://%s", RAYO_JID(component)); /* acknowledge command */ rayo_component_send_start(component, iq); rayo_component_api_execute_async(component, "conference", stream.data); switch_safe_free(stream.data); RAYO_RELEASE(component); return NULL; }
/** * Start input component */ static void start_input(struct prompt_component *prompt, int start_timers, int barge_event) { iks *iq = iks_new("iq"); iks *input = iks_find(PROMPT_COMPONENT(prompt)->iq, "prompt"); input = iks_find(input, "input"); iks_insert_attrib(iq, "from", RAYO_JID(prompt)); iks_insert_attrib(iq, "to", RAYO_JID(RAYO_COMPONENT(prompt)->parent)); iks_insert_attrib_printf(iq, "id", "mod_rayo-prompt-%d", RAYO_SEQ_NEXT(prompt)); iks_insert_attrib(iq, "type", "set"); input = iks_copy_within(input, iks_stack(iq)); iks_insert_attrib(input, "start-timers", start_timers ? "true" : "false"); iks_insert_attrib(input, "barge-event", barge_event ? "true" : "false"); iks_insert_node(iq, input); RAYO_SEND_MESSAGE(prompt, RAYO_JID(RAYO_COMPONENT(prompt)->parent), iq); }
/** * Handle start of output. */ static iks *prompt_component_handle_output_start(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) output start\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_START_OUTPUT: PROMPT_COMPONENT(prompt)->output_jid = switch_core_strdup(RAYO_POOL(prompt), msg->from_jid); PROMPT_COMPONENT(prompt)->state = PCS_OUTPUT; /* send ref to client */ rayo_component_send_start(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->iq); break; case PCS_START_OUTPUT_BARGE: PROMPT_COMPONENT(prompt)->output_jid = switch_core_strdup(RAYO_POOL(prompt), msg->from_jid); PROMPT_COMPONENT(prompt)->state = PCS_START_INPUT_OUTPUT; /* start input without timers and with barge events */ start_input(PROMPT_COMPONENT(prompt), 0, 1); break; case PCS_OUTPUT: case PCS_START_INPUT_OUTPUT: case PCS_START_INPUT: case PCS_START_INPUT_TIMERS: case PCS_INPUT_OUTPUT: case PCS_STOP_OUTPUT: case PCS_INPUT: case PCS_DONE_STOP_OUTPUT: case PCS_DONE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output event\n", RAYO_JID(prompt)); break; } return NULL; }
/** * Send component start reply * @param component the component * @param iq the start request */ void rayo_component_send_start(struct rayo_component *component, iks *iq) { iks *response = iks_new_iq_result(iq); iks *ref = iks_insert(response, "ref"); iks_insert_attrib(ref, "xmlns", RAYO_NS); iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(component)); RAYO_SEND_REPLY(component, iks_find_attrib(response, "to"), response); }
/** * Handle barge event */ static iks *prompt_component_handle_input_start_timers_error(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { /* this is only expected if input component is gone */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) start timers error\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); return NULL; }
/** * Handle completion event */ static iks *prompt_component_handle_input_complete(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *presence = msg->payload; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) input complete\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_INPUT_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT; presence = iks_copy(presence); iks_insert_attrib(presence, "from", RAYO_JID(prompt)); iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid); PROMPT_COMPONENT(prompt)->complete = presence; rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output_jid); break; case PCS_STOP_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT; presence = iks_copy(presence); iks_insert_attrib(presence, "from", RAYO_JID(prompt)); iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid); PROMPT_COMPONENT(prompt)->complete = presence; break; case PCS_INPUT: PROMPT_COMPONENT(prompt)->state = PCS_DONE; /* pass through */ case PCS_DONE: presence = iks_copy(presence); iks_insert_attrib(presence, "from", RAYO_JID(prompt)); iks_insert_attrib(presence, "to", RAYO_COMPONENT(prompt)->client_jid); iks_delete(PROMPT_COMPONENT(prompt)->iq); rayo_component_send_complete_event(RAYO_COMPONENT(prompt), presence); break; case PCS_OUTPUT: case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: case PCS_START_INPUT: case PCS_START_INPUT_OUTPUT: case PCS_START_INPUT_TIMERS: case PCS_DONE_STOP_OUTPUT: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt)); break; } return NULL; }
/** * Send input-timers-started event */ void rayo_component_send_input_timers_started_event(struct rayo_component *component) { iks *event = iks_new("presence"); iks *x; iks_insert_attrib(event, "from", RAYO_JID(component)); iks_insert_attrib(event, "to", component->client_jid); x = iks_insert(event, "input-timers-started"); iks_insert_attrib(x, "xmlns", RAYO_PROMPT_NS); RAYO_SEND_REPLY(component, component->client_jid, event); }
/** * Start input component timers */ static void start_input_timers(struct prompt_component *prompt) { iks *x; iks *iq = iks_new("iq"); iks_insert_attrib(iq, "from", RAYO_JID(prompt)); iks_insert_attrib(iq, "to", prompt->input_jid); iks_insert_attrib(iq, "type", "set"); iks_insert_attrib_printf(iq, "id", "mod_rayo-prompt-%d", RAYO_SEQ_NEXT(prompt)); x = iks_insert(iq, "start-timers"); iks_insert_attrib(x, "xmlns", RAYO_INPUT_NS); RAYO_SEND_MESSAGE(prompt, prompt->input_jid, iq); }
/** * Send stop to component */ static void rayo_component_send_stop(struct rayo_actor *from, const char *to) { iks *stop = iks_new("iq"); iks *x; iks_insert_attrib(stop, "from", RAYO_JID(from)); iks_insert_attrib(stop, "to", to); iks_insert_attrib(stop, "type", "set"); iks_insert_attrib_printf(stop, "id", "mod_rayo-prompt-%d", RAYO_SEQ_NEXT(from)); x = iks_insert(stop, "stop"); iks_insert_attrib(x, "xmlns", RAYO_EXT_NS); RAYO_SEND_MESSAGE(from, to, stop); }
/** * Handle start of input/output. */ static iks *prompt_component_handle_io_start(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, got <ref> from %s: %s\n", RAYO_JID(prompt), msg->from_jid, iks_string(iks_stack(iq), iq)); if (!strcmp("input", msg->from_subtype)) { return prompt_component_handle_input_start(prompt, msg, data); } else if (!strcmp("output", msg->from_subtype)) { return prompt_component_handle_output_start(prompt, msg, data); } return NULL; }
/** * Forward result */ static iks *prompt_component_handle_result(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; /* forward all results, except for internal ones... */ if (strncmp("mod_rayo-prompt", iks_find_attrib_soft(iq, "id"), 15)) { iks_insert_attrib(iq, "from", RAYO_JID(prompt)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); RAYO_SEND_REPLY_DUP(prompt, RAYO_COMPONENT(prompt)->client_jid, iq); } return NULL; }
/** * Forward CPA signal to client */ static void rayo_cpa_detector_event(const char *jid, void *user_data) { struct rayo_actor *component = RAYO_LOCATE(jid); if (component) { if (CPA_COMPONENT(component)->ready) { switch_event_t *event = (switch_event_t *)user_data; const char *signal_type = switch_event_get_header(event, "signal-type"); struct cpa_signal *cpa_signal = switch_core_hash_find(CPA_COMPONENT(component)->signals, signal_type); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Handling CPA event\n"); if (cpa_signal) { const char *value = switch_event_get_header(event, "value"); const char *duration = switch_event_get_header(event, "duration"); if (cpa_signal->terminate) { iks *complete_event; iks *signal_xml; stop_cpa_detectors(CPA_COMPONENT(component)); /* send complete event to client */ complete_event = rayo_component_create_complete_event(RAYO_COMPONENT(component), "signal", RAYO_CPA_NS); signal_xml = iks_find(complete_event, "complete"); signal_xml = iks_find(signal_xml, "signal"); iks_insert_attrib(signal_xml, "type", signal_type); if (!zstr(value)) { iks_insert_attrib(signal_xml, "value", value); } if (!zstr(duration)) { iks_insert_attrib(signal_xml, "duration", duration); } rayo_component_send_complete_event(RAYO_COMPONENT(component), complete_event); } else { /* send event to client */ iks *signal_event = iks_new_presence("signal", RAYO_CPA_NS, RAYO_JID(component), RAYO_COMPONENT(component)->client_jid); iks *signal_xml = iks_find(signal_event, "signal"); iks_insert_attrib(signal_xml, "type", signal_type); if (!zstr(value)) { iks_insert_attrib(signal_xml, "value", value); } if (!zstr(duration)) { iks_insert_attrib(signal_xml, "duration", duration); } RAYO_SEND_REPLY(component, RAYO_COMPONENT(component)->client_jid, signal_event); } } } else { switch_log_printf(SWITCH_CHANNEL_UUID_LOG(RAYO_COMPONENT(component)->parent->id), SWITCH_LOG_DEBUG, "Skipping CPA event\n"); } RAYO_UNLOCK(component); } }
/** * Forward result */ static iks *prompt_component_handle_result(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; /* forward all results, except for internal ones... */ const char *id = iks_find_attrib_soft(iq, "id"); if (strncmp("mod_rayo-prompt", id, 15)) { iks_insert_attrib(iq, "from", RAYO_JID(prompt)); iks_insert_attrib(iq, "to", RAYO_COMPONENT(prompt)->client_jid); RAYO_SEND_REPLY_DUP(prompt, RAYO_COMPONENT(prompt)->client_jid, iq); } else if (!strcmp(PROMPT_COMPONENT(prompt)->start_timers_request_id, id)) { rayo_component_send_input_timers_started_event(RAYO_COMPONENT(prompt)); } return NULL; }
/** * Stop all CPA detectors */ static void stop_cpa_detectors(struct cpa_component *cpa) { if (cpa->signals) { switch_hash_index_t *hi = NULL; for (hi = switch_core_hash_first(cpa->signals); hi; hi = switch_core_hash_next(hi)) { const void *signal_type; void *cpa_signal = NULL; switch_core_hash_this(hi, &signal_type, NULL, &cpa_signal); if (cpa_signal) { rayo_cpa_detector_stop(RAYO_COMPONENT(cpa)->parent->id, ((struct cpa_signal *)cpa_signal)->name); unsubscribe(RAYO_COMPONENT(cpa)->parent->id, ((struct cpa_signal *)cpa_signal)->name, RAYO_JID(cpa)); } } switch_core_hash_destroy(&cpa->signals); cpa->signals = NULL; } unsubscribe(RAYO_COMPONENT(cpa)->parent->id, "hangup", RAYO_JID(cpa)); }
/** * Send rayo complete */ void rayo_component_send_complete_with_metadata_string(struct rayo_component *component, const char *reason, const char *reason_namespace, const char *meta, int child_of_complete) { iks *meta_xml = NULL; iksparser *p = iks_dom_new(&meta_xml); if (iks_parse(p, meta, 0, 1) != IKS_OK) { /* unexpected ... */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "%s Failed to parse metadata for complete event: %s\n", RAYO_JID(component), meta); /* send without... */ rayo_component_send_complete(component, reason, reason_namespace); } else { rayo_component_send_complete_with_metadata(component, reason, reason_namespace, meta_xml, child_of_complete); } if (meta_xml) { iks_delete(meta_xml); } iks_parser_delete(p); }
/** * Stop execution of prompt component */ static iks *stop_call_prompt_component(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { iks *iq = msg->payload; iks *reply = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) stop prompt\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_OUTPUT: /* input hasn't started yet */ PROMPT_COMPONENT(prompt)->state = PCS_DONE_STOP_OUTPUT; PROMPT_COMPONENT(prompt)->complete = rayo_component_create_complete_event(RAYO_COMPONENT(prompt), COMPONENT_COMPLETE_STOP); rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->output_jid); break; case PCS_INPUT_OUTPUT: case PCS_INPUT: case PCS_STOP_OUTPUT: /* stopping input will trigger completion */ rayo_component_send_stop(prompt, PROMPT_COMPONENT(prompt)->input_jid); break; case PCS_START_INPUT: /* stop input as soon as it starts */ PROMPT_COMPONENT(prompt)->state = PCS_DONE; break; case PCS_DONE_STOP_OUTPUT: case PCS_DONE: /* already done */ break; case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: case PCS_START_INPUT_OUTPUT: case PCS_START_INPUT_TIMERS: /* ref hasn't been sent yet */ reply = iks_new_error(iq, STANZA_ERROR_UNEXPECTED_REQUEST); break; } if (!reply) { reply = iks_new_iq_result(iq); } return reply; }
/** * Handle completion event */ static iks *prompt_component_handle_output_complete(struct rayo_actor *prompt, struct rayo_message *msg, void *data) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s) output complete\n", RAYO_JID(prompt), prompt_component_state_to_string(PROMPT_COMPONENT(prompt)->state)); switch (PROMPT_COMPONENT(prompt)->state) { case PCS_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_START_INPUT; /* start input with timers enabled and barge events disabled */ start_input(PROMPT_COMPONENT(prompt), 1, 0); iks_delete(PROMPT_COMPONENT(prompt)->iq); break; case PCS_START_INPUT_OUTPUT: /* output finished before input started */ PROMPT_COMPONENT(prompt)->state = PCS_START_INPUT_TIMERS; break; case PCS_INPUT_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_INPUT; start_input_timers(PROMPT_COMPONENT(prompt)); break; case PCS_STOP_OUTPUT: PROMPT_COMPONENT(prompt)->state = PCS_INPUT; start_input_timers(PROMPT_COMPONENT(prompt)); break; case PCS_DONE_STOP_OUTPUT: if (PROMPT_COMPONENT(prompt)->complete) { rayo_component_send_complete_event(RAYO_COMPONENT(prompt), PROMPT_COMPONENT(prompt)->complete); } break; case PCS_INPUT: case PCS_START_OUTPUT: case PCS_START_OUTPUT_BARGE: case PCS_START_INPUT: case PCS_START_INPUT_TIMERS: case PCS_DONE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, unexpected start output error event\n", RAYO_JID(prompt)); break; } return NULL; }
/** * Seek output component */ static iks *seek_output_component(struct rayo_actor *component, struct rayo_message *msg, void *data) { iks *iq = msg->payload; iks *seek = iks_find(iq, "seek"); if (VALIDATE_RAYO_OUTPUT_SEEK(seek)) { int is_forward = !strcmp("forward", iks_find_attrib(seek, "direction")); int amount_ms = iks_find_int_attrib(seek, "amount"); char *command = switch_mprintf("%s seek:%s%i", RAYO_JID(component), is_forward ? "+" : "-", amount_ms); switch_stream_handle_t stream = { 0 }; SWITCH_STANDARD_STREAM(stream); switch_api_execute("fileman", command, NULL, &stream); switch_safe_free(stream.data); switch_safe_free(command); return iks_new_iq_result(iq); } return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); }
/** * Create component complete event * @param component the component * @param reason_str the completion reason * @param reason_namespace the completion reason namespace * @param meta metadata to add as child * @param child_of_complete if true metadata is child of complete instead of reason * @return the event */ iks *rayo_component_create_complete_event_with_metadata(struct rayo_component *component, const char *reason_str, const char *reason_namespace, iks *meta, int child_of_complete) { iks *response = iks_new("presence"); iks *complete; iks *reason; iks_insert_attrib(response, "from", RAYO_JID(component)); iks_insert_attrib(response, "to", component->client_jid); iks_insert_attrib(response, "type", "unavailable"); complete = iks_insert(response, "complete"); iks_insert_attrib(complete, "xmlns", RAYO_EXT_NS); reason = iks_insert(complete, reason_str); iks_insert_attrib(reason, "xmlns", reason_namespace); if (meta) { meta = iks_copy_within(meta, iks_stack(response)); if (child_of_complete) { iks_insert_node(complete, meta); } else { iks_insert_node(reason, meta); } } return response; }
/** * Start execution of call receivefax component * @param call the call to receive fax from * @param msg the original request * @param session_data the call's session */ static iks *start_receivefax_component(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; struct receivefax_component *receivefax_component = NULL; iks *receivefax = iks_find(iq, "receivefax"); iks *response = NULL; switch_event_t *execute_event = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); switch_memory_pool_t *pool; int file_no; /* validate attributes */ if (!VALIDATE_RAYO_RECEIVEFAX(receivefax)) { return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } /* fax is only allowed if the call is not currently joined */ if (rayo_call_is_joined(RAYO_CALL(call))) { return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "can't receive fax on a joined call"); } if (rayo_call_is_faxing(RAYO_CALL(call))) { return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "fax already in progress"); } /* create receivefax component */ switch_core_new_memory_pool(&pool); receivefax_component = switch_core_alloc(pool, sizeof(*receivefax_component)); rayo_component_init((struct rayo_component *)receivefax_component, pool, RAT_CALL_COMPONENT, "receivefax", NULL, call, iks_find_attrib(iq, "from")); file_no = rayo_actor_seq_next(call); receivefax_component->filename = switch_core_sprintf(pool, "%s%s%s-%d.tif", globals.file_prefix, SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session), file_no); if (!strncmp(receivefax_component->filename, "http://", 7) || !strncmp(receivefax_component->filename, "https://", 8)) { /* This is an HTTP URL, need to PUT after fax is received */ receivefax_component->local_filename = switch_core_sprintf(pool, "%s%s%s-%d", SWITCH_GLOBAL_dirs.temp_dir, SWITCH_PATH_SEPARATOR, switch_core_session_get_uuid(session), file_no); receivefax_component->http_put_after_receive = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s save fax to HTTP URL\n", RAYO_JID(receivefax_component)); } else { /* assume file.. */ receivefax_component->local_filename = receivefax_component->filename; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s save fax to local file\n", RAYO_JID(receivefax_component)); } /* add channel variable so that fax component can be located from fax events */ switch_channel_set_variable(channel, "rayo_fax_jid", RAYO_JID(receivefax_component)); /* clear fax result variables */ switch_channel_set_variable(channel, "fax_success", NULL); switch_channel_set_variable(channel, "fax_result_code", NULL); switch_channel_set_variable(channel, "fax_result_text", NULL); switch_channel_set_variable(channel, "fax_document_transferred_pages", NULL); switch_channel_set_variable(channel, "fax_document_total_pages", NULL); switch_channel_set_variable(channel, "fax_image_resolution", NULL); switch_channel_set_variable(channel, "fax_image_size", NULL); switch_channel_set_variable(channel, "fax_bad_rows", NULL); switch_channel_set_variable(channel, "fax_transfer_rate", NULL); switch_channel_set_variable(channel, "fax_ecm_used", NULL); switch_channel_set_variable(channel, "fax_local_station_id", NULL); switch_channel_set_variable(channel, "fax_remote_station_id", NULL); /* clear fax interrupt variable */ switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_read_frame_interrupt", NULL); rayo_call_set_faxing(RAYO_CALL(call), 1); /* execute rxfax APP */ 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", "rxfax"); switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-arg", receivefax_component->local_filename); 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(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to rxfax (queue event failed)"); if (execute_event) { switch_event_destroy(&execute_event); } rayo_call_set_faxing(RAYO_CALL(call), 0); RAYO_UNLOCK(receivefax_component); } else { /* component starting... */ rayo_component_send_start(RAYO_COMPONENT(receivefax_component), iq); } } else { response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to create rxfax event"); rayo_call_set_faxing(RAYO_CALL(call), 0); RAYO_UNLOCK(receivefax_component); } return response; }
/** * Handle fax completion event from FreeSWITCH core * @param event received from FreeSWITCH core. It will be destroyed by the core after this function returns. */ static void on_execute_complete_event(switch_event_t *event) { const char *application = switch_event_get_header(event, "Application"); if (!zstr(application) && (!strcmp(application, "rxfax") || !strcmp(application, "txfax"))) { int is_rxfax = !strcmp(application, "rxfax"); const char *uuid = switch_event_get_header(event, "Unique-ID"); const char *fax_jid = switch_event_get_header(event, "variable_rayo_fax_jid"); struct rayo_actor *component; if (!zstr(fax_jid) && (component = RAYO_LOCATE(fax_jid))) { iks *result; iks *complete; iks *fax; int have_fax_document = 1; switch_core_session_t *session; switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got result for %s\n", fax_jid); /* clean up channel */ session = switch_core_session_locate(uuid); if (session) { switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_read_frame_interrupt", NULL); switch_core_session_rwunlock(session); } /* RX only: transfer HTTP document and delete local copy */ if (is_rxfax && RECEIVEFAX_COMPONENT(component)->http_put_after_receive && switch_file_exists(RECEIVEFAX_COMPONENT(component)->local_filename, RAYO_POOL(component)) == SWITCH_STATUS_SUCCESS) { switch_stream_handle_t stream = { 0 }; SWITCH_STANDARD_STREAM(stream); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s PUT fax to %s\n", RAYO_JID(component), RECEIVEFAX_COMPONENT(component)->filename); switch_api_execute("http_put", RECEIVEFAX_COMPONENT(component)->filename, NULL, &stream); /* check if successful */ if (!zstr(stream.data) && strncmp(stream.data, "+OK", 3)) { /* PUT failed */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s PUT fax to %s failed: %s\n", RAYO_JID(component), RECEIVEFAX_COMPONENT(component)->filename, (char *)stream.data); have_fax_document = 0; } switch_safe_free(stream.data) switch_file_remove(RECEIVEFAX_COMPONENT(component)->local_filename, RAYO_POOL(component)); } /* successful fax? */ if (have_fax_document && switch_true(switch_event_get_header(event, "variable_fax_success"))) { result = rayo_component_create_complete_event(RAYO_COMPONENT(component), FAX_FINISH); } else if (have_fax_document && FAX_COMPONENT(component)->stop) { result = rayo_component_create_complete_event(RAYO_COMPONENT(component), COMPONENT_COMPLETE_STOP); } else { result = rayo_component_create_complete_event(RAYO_COMPONENT(component), COMPONENT_COMPLETE_ERROR); } complete = iks_find(result, "complete"); /* RX only: add fax document information */ if (is_rxfax && have_fax_document) { const char *pages = switch_event_get_header(event, "variable_fax_document_transferred_pages"); if (!zstr(pages) && switch_is_number(pages) && atoi(pages) > 0) { const char *resolution = switch_event_get_header(event, "variable_fax_file_image_resolution"); const char *size = switch_event_get_header(event, "variable_fax_image_size"); fax = iks_insert(complete, "fax"); iks_insert_attrib(fax, "xmlns", RAYO_FAX_COMPLETE_NS); if (RECEIVEFAX_COMPONENT(component)->http_put_after_receive) { iks_insert_attrib(fax, "url", RECEIVEFAX_COMPONENT(component)->filename); } else { /* convert absolute path to file:// URI */ iks_insert_attrib_printf(fax, "url", "file://%s", RECEIVEFAX_COMPONENT(component)->filename); } if (!zstr(resolution)) { iks_insert_attrib(fax, "resolution", resolution); } if (!zstr(size)) { iks_insert_attrib(fax, "size", size); } iks_insert_attrib(fax, "pages", pages); } } /* add metadata from event */ insert_fax_metadata(event, "fax_success", complete); insert_fax_metadata(event, "fax_result_code", complete); insert_fax_metadata(event, "fax_result_text", complete); insert_fax_metadata(event, "fax_document_transferred_pages", complete); insert_fax_metadata(event, "fax_document_total_pages", complete); insert_fax_metadata(event, "fax_image_resolution", complete); insert_fax_metadata(event, "fax_image_size", complete); insert_fax_metadata(event, "fax_bad_rows", complete); insert_fax_metadata(event, "fax_transfer_rate", complete); insert_fax_metadata(event, "fax_ecm_used", complete); insert_fax_metadata(event, "fax_local_station_id", complete); insert_fax_metadata(event, "fax_remote_station_id", complete); /* flag faxing as done */ rayo_call_set_faxing(RAYO_CALL(RAYO_COMPONENT(component)->parent), 0); rayo_component_send_complete_event(RAYO_COMPONENT(component), result); RAYO_UNLOCK(component); } } }
/** * Start execution of call sendfax component * @param call the call to send fax to * @param msg the original request * @param session_data the call's session */ static iks *start_sendfax_component(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; struct fax_component *sendfax_component = NULL; iks *sendfax = iks_find(iq, "sendfax"); iks *response = NULL; switch_event_t *execute_event = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); switch_memory_pool_t *pool; iks *document; const char *fax_document; const char *fax_header; const char *fax_identity; const char *pages; /* validate attributes */ if (!VALIDATE_RAYO_SENDFAX(sendfax)) { return iks_new_error(iq, STANZA_ERROR_BAD_REQUEST); } /* fax is only allowed if the call is not currently joined */ if (rayo_call_is_joined(RAYO_CALL(call))) { return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "can't send fax on a joined call"); } if (rayo_call_is_faxing(RAYO_CALL(call))) { return iks_new_error_detailed(iq, STANZA_ERROR_UNEXPECTED_REQUEST, "fax already in progress"); } /* get fax document */ document = iks_find(sendfax, "document"); if (!document) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "missing document"); } fax_document = iks_find_attrib_soft(document, "url"); if (zstr(fax_document)) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "missing document url"); } /* is valid URL type? */ if (!strncasecmp(fax_document, "http://", 7) || !strncasecmp(fax_document, "https://", 8)) { switch_stream_handle_t stream = { 0 }; SWITCH_STANDARD_STREAM(stream); /* need to fetch document from server... */ switch_api_execute("http_get", fax_document, session, &stream); if (!zstr(stream.data) && !strncmp(fax_document, SWITCH_PATH_SEPARATOR, strlen(SWITCH_PATH_SEPARATOR))) { fax_document = switch_core_session_strdup(session, stream.data); } else { switch_safe_free(stream.data); return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to fetch document"); } switch_safe_free(stream.data); } else if (!strncasecmp(fax_document, "file://", 7)) { fax_document = fax_document + 7; if (zstr(fax_document)) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid file:// url"); } } else if (strncasecmp(fax_document, SWITCH_PATH_SEPARATOR, strlen(SWITCH_PATH_SEPARATOR))) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "unsupported url type"); } /* does document exist? */ if (switch_file_exists(fax_document, pool) != SWITCH_STATUS_SUCCESS) { return iks_new_error_detailed_printf(iq, STANZA_ERROR_BAD_REQUEST, "file not found: %s", fax_document); } /* get fax identity and header */ fax_identity = iks_find_attrib_soft(document, "identity"); if (!zstr(fax_identity)) { switch_channel_set_variable(channel, "fax_ident", fax_identity); } else { switch_channel_set_variable(channel, "fax_ident", NULL); } fax_header = iks_find_attrib_soft(document, "header"); if (!zstr(fax_header)) { switch_channel_set_variable(channel, "fax_header", fax_header); } else { switch_channel_set_variable(channel, "fax_header", NULL); } /* get pages to send */ pages = iks_find_attrib_soft(document, "pages"); if (!zstr(pages)) { if (switch_regex_match(pages, "[1-9][0-9]*(-[1-9][0-9]*)?") == SWITCH_STATUS_FALSE) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid pages value"); } else { int start = 0; int end = 0; char *pages_dup = switch_core_session_strdup(session, pages); char *sep = strchr(pages_dup, '-'); if (sep) { *sep = '\0'; sep++; end = atoi(sep); } start = atoi(pages_dup); if (end && end < start) { return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "invalid pages value"); } switch_channel_set_variable(channel, "fax_start_page", pages_dup); switch_channel_set_variable(channel, "fax_end_page", sep); } } else { switch_channel_set_variable(channel, "fax_start_page", NULL); switch_channel_set_variable(channel, "fax_end_page", NULL); } /* create sendfax component */ switch_core_new_memory_pool(&pool); sendfax_component = switch_core_alloc(pool, sizeof(*sendfax_component)); rayo_component_init((struct rayo_component *)sendfax_component, pool, RAT_CALL_COMPONENT, "sendfax", NULL, call, iks_find_attrib(iq, "from")); /* add channel variable so that fax component can be located from fax events */ switch_channel_set_variable(channel, "rayo_fax_jid", RAYO_JID(sendfax_component)); /* clear fax result variables */ switch_channel_set_variable(channel, "fax_success", NULL); switch_channel_set_variable(channel, "fax_result_code", NULL); switch_channel_set_variable(channel, "fax_result_text", NULL); switch_channel_set_variable(channel, "fax_document_transferred_pages", NULL); switch_channel_set_variable(channel, "fax_document_total_pages", NULL); switch_channel_set_variable(channel, "fax_image_resolution", NULL); switch_channel_set_variable(channel, "fax_image_size", NULL); switch_channel_set_variable(channel, "fax_bad_rows", NULL); switch_channel_set_variable(channel, "fax_transfer_rate", NULL); switch_channel_set_variable(channel, "fax_ecm_used", NULL); switch_channel_set_variable(channel, "fax_local_station_id", NULL); switch_channel_set_variable(channel, "fax_remote_station_id", NULL); /* clear fax interrupt variable */ switch_channel_set_variable(switch_core_session_get_channel(session), "rayo_read_frame_interrupt", NULL); rayo_call_set_faxing(RAYO_CALL(call), 1); /* execute txfax APP */ 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", "txfax"); switch_event_add_header_string(execute_event, SWITCH_STACK_BOTTOM, "execute-app-arg", fax_document); 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(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to txfax (queue event failed)"); if (execute_event) { switch_event_destroy(&execute_event); } rayo_call_set_faxing(RAYO_CALL(call), 0); RAYO_UNLOCK(sendfax_component); } else { /* component starting... */ rayo_component_send_start(RAYO_COMPONENT(sendfax_component), iq); } } else { response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "failed to create txfax event"); rayo_call_set_faxing(RAYO_CALL(call), 0); RAYO_UNLOCK(sendfax_component); } return response; }
/** * Start CPA */ iks *rayo_cpa_component_start(struct rayo_actor *call, struct rayo_message *msg, void *session_data) { iks *iq = msg->payload; switch_core_session_t *session = (switch_core_session_t *)session_data; iks *input = iks_find(iq, "input"); switch_memory_pool_t *pool = NULL; struct cpa_component *component = NULL; int have_grammar = 0; iks *grammar = NULL; /* create CPA component */ switch_core_new_memory_pool(&pool); component = switch_core_alloc(pool, sizeof(*component)); component = CPA_COMPONENT(rayo_component_init((struct rayo_component *)component, pool, RAT_CALL_COMPONENT, "cpa", NULL, call, iks_find_attrib(iq, "from"))); if (!component) { switch_core_destroy_memory_pool(&pool); return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to create CPA entity"); } switch_core_hash_init(&component->signals); /* start CPA detectors */ for (grammar = iks_find(input, "grammar"); grammar; grammar = iks_next_tag(grammar)) { if (!strcmp("grammar", iks_name(grammar))) { const char *error_str = ""; const char *url = iks_find_attrib_soft(grammar, "url"); char *url_dup; char *url_params; if (zstr(url)) { stop_cpa_detectors(component); RAYO_UNLOCK(component); RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Missing grammar URL"); } have_grammar = 1; url_dup = strdup(url); if ((url_params = strchr(url_dup, '?'))) { *url_params = '\0'; url_params++; } if (switch_core_hash_find(component->signals, url)) { free(url_dup); stop_cpa_detectors(component); RAYO_UNLOCK(component); RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Duplicate URL"); } /* start detector */ /* TODO return better reasons... */ if (rayo_cpa_detector_start(switch_core_session_get_uuid(session), url_dup, &error_str)) { struct cpa_signal *cpa_signal = switch_core_alloc(pool, sizeof(*cpa_signal)); cpa_signal->terminate = !zstr(url_params) && strstr(url_params, "terminate=true"); cpa_signal->name = switch_core_strdup(pool, url_dup); switch_core_hash_insert(component->signals, cpa_signal->name, cpa_signal); subscribe(switch_core_session_get_uuid(session), cpa_signal->name, RAYO_JID(component)); } else { free(url_dup); stop_cpa_detectors(component); RAYO_UNLOCK(component); RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, error_str); } free(url_dup); } } if (!have_grammar) { stop_cpa_detectors(component); RAYO_UNLOCK(component); RAYO_DESTROY(component); return iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "No grammar defined"); } /* acknowledge command */ rayo_component_send_start(RAYO_COMPONENT(component), iq); /* TODO hangup race condition */ subscribe(switch_core_session_get_uuid(session), "hangup", RAYO_JID(component)); /* ready to forward detector events */ component->ready = 1; return NULL; }