提交 ae69c5a7 authored 作者: Chris Rienzo's avatar Chris Rienzo

FS-6407 --resolve mod_rayo: support SIP URI caller ID w/ display name

上级 05263818
...@@ -2400,6 +2400,130 @@ static iks *on_rayo_unjoin(struct rayo_actor *call, struct rayo_message *msg, vo ...@@ -2400,6 +2400,130 @@ static iks *on_rayo_unjoin(struct rayo_actor *call, struct rayo_message *msg, vo
return response; return response;
} }
/**
* @return 1 if display name is valid
*/
static int is_valid_display_name(char *display)
{
if (zstr(display)) {
return 0;
}
return 1;
}
/**
* @return 1 if SIP URI is valid
*/
static int is_valid_sip_uri(char *uri)
{
/* just some basic checks to prevent failure when passing URI as caller ID */
if (zstr(uri) || strchr(uri, '<') || strchr(uri, '>')) {
return 0;
}
return 1;
}
/**
* @return 1 if tel URI is valid
*/
static int is_valid_tel_uri(char *uri)
{
if (!zstr(uri)) {
/* alphanumeric only for now */
int i;
int len = strlen(uri);
for (i = 0; i < len; i++) {
if (!isalnum(uri[i])) {
return 0;
}
}
return 1;
}
return 0;
}
#define RAYO_URI_SCHEME_UNKNOWN 0
#define RAYO_URI_SCHEME_TEL 1
#define RAYO_URI_SCHEME_SIP 2
/**
* Parse dial "from" parameter
* @param pool to use
* @param from the parameter to parse
* @param uri the URI
* @param display the display name
* @return scheme
*/
static int parse_dial_from(switch_memory_pool_t *pool, const char *from, char **uri, char **display)
{
if (!zstr(from)) {
char *l_display = switch_core_strdup(pool, from);
char *l_uri;
*display = NULL;
*uri = NULL;
/* TODO regex would be better */
/* split display-name and URI */
l_uri = strrchr(l_display, ' ');
if (l_uri) {
*l_uri++ = '\0';
if (!zstr(l_display)) {
/* remove "" from display-name */
if (l_display[0] == '"') {
int len;
*l_display++ = '\0';
len = strlen(l_display);
if (len < 2 || l_display[len - 1] != '"') {
return RAYO_URI_SCHEME_UNKNOWN;
}
l_display[len - 1] = '\0';
}
if (!is_valid_display_name(l_display)) {
return RAYO_URI_SCHEME_UNKNOWN;
}
*display = l_display;
}
} else {
l_uri = l_display;
}
if (zstr(l_uri)) {
return RAYO_URI_SCHEME_UNKNOWN;
}
/* remove <> from URI */
if (l_uri[0] == '<') {
int len;
*l_uri++ = '\0';
len = strlen(l_uri);
if (len < 2 || l_uri[len - 1] != '>') {
return RAYO_URI_SCHEME_UNKNOWN;
}
l_uri[len - 1] = '\0';
if (zstr(l_uri)) {
return RAYO_URI_SCHEME_UNKNOWN;
}
}
*uri = l_uri;
/* figure out URI scheme and validate it */
if (!strncmp("sip:", l_uri, 4) || !strncmp("sips:", l_uri, 5)) {
/* validate SIP URI */
if (is_valid_sip_uri(l_uri)) {
return RAYO_URI_SCHEME_SIP;
}
} else if (!strncmp("tel:", l_uri, 4)) {
l_uri += 4;
*uri = l_uri;
}
if (is_valid_tel_uri(l_uri)) {
return RAYO_URI_SCHEME_TEL;
}
}
return RAYO_URI_SCHEME_UNKNOWN;
}
struct dial_thread_data { struct dial_thread_data {
switch_memory_pool_t *pool; switch_memory_pool_t *pool;
iks *node; iks *node;
...@@ -2425,10 +2549,12 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * ...@@ -2425,10 +2549,12 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
const char *dial_timeout_ms = iks_find_attrib(dial, "timeout"); const char *dial_timeout_ms = iks_find_attrib(dial, "timeout");
const char *requested_call_uri = iks_find_attrib(dial, "uri"); const char *requested_call_uri = iks_find_attrib(dial, "uri");
const char *uuid = NULL; const char *uuid = NULL;
switch_event_t *originate_vars = NULL;
struct dial_gateway *gateway = NULL; struct dial_gateway *gateway = NULL;
struct rayo_call *call = NULL; struct rayo_call *call = NULL;
switch_stream_handle_t stream = { 0 }; uint32_t dial_timeout_sec = 0;
SWITCH_STANDARD_STREAM(stream);
/* TODO dial_to needs validation */
/* Check if optional URI is valid. */ /* Check if optional URI is valid. */
if (!zstr(requested_call_uri)) { if (!zstr(requested_call_uri)) {
...@@ -2483,38 +2609,52 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * ...@@ -2483,38 +2609,52 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s has control of call\n", dcp_jid); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_INFO, "%s has control of call\n", dcp_jid);
uuid = switch_core_strdup(dtdata->pool, rayo_call_get_uuid(call)); uuid = switch_core_strdup(dtdata->pool, rayo_call_get_uuid(call));
/* set rayo channel variables so channel originate event can be identified as coming from Rayo */ /* create container for origination variables */
stream.write_function(&stream, "{origination_uuid=%s,rayo_dcp_jid=%s,rayo_call_jid=%s", if (switch_event_create_plain(&originate_vars, SWITCH_EVENT_CHANNEL_DATA) != SWITCH_STATUS_SUCCESS) {
rayo_call_get_uuid(call), dcp_jid, RAYO_JID(call)); abort();
}
/* parse optional params from dialstring and append to */ /* add dialstring vars to origination variables */
if (*dial_to == '{') { if (*dial_to == '{') {
switch_event_t *params = NULL;
dial_to_dup = switch_core_strdup(dtdata->pool, dial_to); dial_to_dup = switch_core_strdup(dtdata->pool, dial_to);
switch_event_create_brackets(dial_to_dup, '{', '}', ',', &params, (char **)&dial_to, SWITCH_FALSE); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: parsing dialstring channel variables\n");
if (params) { switch_event_create_brackets(dial_to_dup, '{', '}', ',', &originate_vars, (char **)&dial_to, SWITCH_FALSE);
switch_event_header_t *param;
for(param = params->headers; param; param = param->next) {
if (strchr(param->value, ',')) {
stream.write_function(&stream, ",%s=\\'%s\\'", param->name, param->value);
} else {
stream.write_function(&stream, ",%s=%s", param->name, param->value);
}
}
switch_event_destroy(&params);
}
} }
/* set originate channel variables */ /* set originate channel variables */
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "origination_uuid", rayo_call_get_uuid(call));
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "rayo_dcp_jid", dcp_jid);
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "rayo_call_jid", RAYO_JID(call));
if (!zstr(dial_from)) { if (!zstr(dial_from)) {
/* caller ID */ char *from_uri = NULL;
/* TODO parse caller ID name and number from URI */ char *from_display;
stream.write_function(&stream, ",origination_caller_id_number=%s,origination_caller_id_name=%s", dial_from, dial_from); int scheme = parse_dial_from(dtdata->pool, dial_from, &from_uri, &from_display);
if (scheme == RAYO_URI_SCHEME_UNKNOWN) {
response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "Bad from URI");
goto done;
} else if (scheme == RAYO_URI_SCHEME_SIP) {
/* SIP URI */
if (!zstr(from_uri)) {
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "sip_from_uri", from_uri);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: sip_from_uri=%s\n", from_uri);
}
if (!zstr(from_display)) {
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "sip_from_display", from_display);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: sip_from_display=%s\n", from_display);
}
}
if (!zstr(from_uri)) {
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "origination_caller_id_number", from_uri);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: origination_caller_id_number=%s\n", from_uri);
}
if (!zstr(from_display)) {
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, "origination_caller_id_name", from_display);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: origination_caller_id_name=%s\n", from_display);
}
} }
if (!zstr(dial_timeout_ms) && switch_is_number(dial_timeout_ms)) { if (!zstr(dial_timeout_ms) && switch_is_number(dial_timeout_ms)) {
/* timeout */ dial_timeout_sec = round((double)atoi(dial_timeout_ms) / 1000.0);
int dial_timeout_sec = round((double)atoi(dial_timeout_ms) / 1000.0);
stream.write_function(&stream, ",originate_timeout=%i", dial_timeout_sec);
} }
/* set outbound signaling headers - only works on SIP */ /* set outbound signaling headers - only works on SIP */
...@@ -2525,22 +2665,24 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * ...@@ -2525,22 +2665,24 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
const char *name = iks_find_attrib_soft(header, "name"); const char *name = iks_find_attrib_soft(header, "name");
const char *value = iks_find_attrib_soft(header, "value"); const char *value = iks_find_attrib_soft(header, "value");
if (!zstr(name) && !zstr(value)) { if (!zstr(name) && !zstr(value)) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Adding header: %s: %s\n", name, value); char *header_name = switch_core_sprintf(dtdata->pool, "%s%s", RAYO_SIP_REQUEST_HEADER, name);
stream.write_function(&stream, ",%s%s=%s", RAYO_SIP_REQUEST_HEADER, name, value); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: Adding SIP header: %s: %s\n", name, value);
switch_event_add_header_string(originate_vars, SWITCH_STACK_BOTTOM, header_name, value);
} }
} }
} }
} }
stream.write_function(&stream, "}");
/* build dialstring and dial call */ /* build dialstring and dial call */
gateway = dial_gateway_find(dial_to); gateway = dial_gateway_find(dial_to);
if (gateway) { if (gateway) {
iks *join = iks_find(dial, "join"); iks *join = iks_find(dial, "join");
const char *dial_to_stripped = dial_to + gateway->strip; const char *dial_to_stripped = dial_to + gateway->strip;
switch_stream_handle_t api_stream = { 0 }; switch_core_session_t *caller_session = NULL;
SWITCH_STANDARD_STREAM(api_stream); switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
const char *dialstring = NULL;
const char *app = NULL;
const char *app_args = NULL;
if (join) { if (join) {
/* check join args */ /* check join args */
...@@ -2567,36 +2709,51 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * ...@@ -2567,36 +2709,51 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
RAYO_UNLOCK(b_call); RAYO_UNLOCK(b_call);
goto done; goto done;
} }
stream.write_function(&stream, "%s%s &rayo(bridge %s)", gateway->dial_prefix, dial_to_stripped, rayo_call_get_uuid(b_call)); app = "bridge";
app_args = switch_core_strdup(dtdata->pool, rayo_call_get_uuid(b_call));
RAYO_UNLOCK(b_call); RAYO_UNLOCK(b_call);
} else { } else {
/* conference */ /* conference */
stream.write_function(&stream, "%s%s &rayo(conference %s@%s)", gateway->dial_prefix, dial_to_stripped, mixer_name, globals.mixer_conf_profile); app = "conference";
app_args = switch_core_sprintf(dtdata->pool, "%s@%s", mixer_name, globals.mixer_conf_profile);
} }
} else { } else {
stream.write_function(&stream, "%s%s &rayo", gateway->dial_prefix, dial_to_stripped); /* default one-legged call */
app = "rayo";
app_args = "";
} }
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "Using dialstring: %s\n", (char *)stream.data); dialstring = switch_core_sprintf(dtdata->pool, "%s%s", gateway->dial_prefix, dial_to_stripped);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_DEBUG, "dial: Using dialstring: %s\n", dialstring);
/* <iq><ref> response will be sent when originate event is received- otherwise error is returned */ /* <iq><ref> response will be sent when originate event is received- otherwise error is returned */
if (switch_api_execute("originate", stream.data, NULL, &api_stream) == SWITCH_STATUS_SUCCESS) { if (switch_ivr_originate(NULL, &caller_session, &cause, dialstring, dial_timeout_sec, NULL, NULL, NULL, NULL, originate_vars, SOF_NONE, NULL) == SWITCH_STATUS_SUCCESS && caller_session) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got originate result: %s\n", (char *)api_stream.data); /* start APP */
switch_caller_extension_t *extension = NULL;
switch_channel_t *caller_channel = switch_core_session_get_channel(caller_session);
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "dial: Call originated\n");
if ((extension = switch_caller_extension_new(caller_session, app, app_args)) == 0) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_CRIT, "Memory Error!\n");
abort();
}
switch_caller_extension_add_application(caller_session, extension, app, app_args);
switch_channel_set_caller_extension(caller_channel, extension);
switch_channel_set_state(caller_channel, CS_EXECUTE);
switch_core_session_rwunlock(caller_session);
} else {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "dial: Failed to originate call: %s\n", switch_channel_cause2str(cause));
switch_mutex_lock(RAYO_ACTOR(call)->mutex); switch_mutex_lock(RAYO_ACTOR(call)->mutex);
/* check for failure */
if (strncmp("+OK", api_stream.data, strlen("+OK"))) {
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_INFO, "Failed to originate call\n");
if (!zstr(call->dial_request_id)) { if (!zstr(call->dial_request_id)) {
call->dial_request_failed = 1; call->dial_request_failed = 1;
call->dial_request_id = NULL; call->dial_request_id = NULL;
/* map failure reason to iq error */ /* map failure reason to iq error */
if (!strncmp("-ERR DESTINATION_OUT_OF_ORDER", api_stream.data, strlen("-ERR DESTINATION_OUT_OF_ORDER"))) { switch (cause) {
case SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER:
/* out of sessions, typically */ /* out of sessions, typically */
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, "DESTINATION_OUT_OF_ORDER");
} else if (!strncmp("-ERR USER_NOT_REGISTERED", api_stream.data, strlen("-ERR USER_NOT_REGISTERED"))) { break;
case SWITCH_CAUSE_USER_NOT_REGISTERED: {
/* call session was never created, so we must fake it so that a call error is sent and /* call session was never created, so we must fake it so that a call error is sent and
not a dial error */ not a dial error */
/* send ref response to DCP immediately followed with failure */ /* send ref response to DCP immediately followed with failure */
...@@ -2617,49 +2774,47 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void * ...@@ -2617,49 +2774,47 @@ static void *SWITCH_THREAD_FUNC rayo_dial_thread(switch_thread_t *thread, void *
/* destroy call */ /* destroy call */
RAYO_DESTROY(call); RAYO_DESTROY(call);
RAYO_UNLOCK(call); RAYO_UNLOCK(call);
} else if (!strncmp("-ERR EXCHANGE_ROUTING_ERROR", api_stream.data, strlen("-ERR EXCHANGE_ROUTING_ERROR"))) { break;
}
case SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR:
/* max forwards */ /* max forwards */
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, "EXCHANGE_ROUTING_ERROR");
} else if (!strncmp("-ERR CHAN_NOT_IMPLEMENTED", api_stream.data, strlen("-ERR CHAN_NOT_IMPLEMENTED"))) { break;
case SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED:
/* unsupported endpoint type */ /* unsupported endpoint type */
response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "CHAN_NOT_IMPLEMENTED");
} else if (!strncmp("-ERR INVALID_URL", api_stream.data, strlen("-ERR INVALID_URL"))) { break;
response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, (char *)api_stream.data); case SWITCH_CAUSE_INVALID_URL:
} else if (!strncmp("-ERR INVALID_GATEWAY", api_stream.data, strlen("-ERR INVALID_GATEWAY"))) { response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "INVALID_URL");
response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, (char *)api_stream.data); break;
} else if (!strncmp("-ERR INVALID_PROFILE", api_stream.data, strlen("-ERR INVALID_PROFILE"))) { case SWITCH_CAUSE_INVALID_GATEWAY:
response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "INVALID_GATEWAY");
} else if (!strncmp("-ERR SYSTEM_SHUTDOWN", api_stream.data, strlen("-ERR SYSTEM_SHUTDOWN"))) { break;
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, (char *)api_stream.data); case SWITCH_CAUSE_INVALID_PROFILE:
} else if (!strncmp("-ERR GATEWAY_DOWN", api_stream.data, strlen("-ERR GATEWAY_DOWN"))) { response = iks_new_error_detailed(iq, STANZA_ERROR_BAD_REQUEST, "INVALID_PROFILE");
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, (char *)api_stream.data); break;
} else if (!strncmp("-ERR INVALID_NUMBER_FORMAT", api_stream.data, strlen("-ERR INVALID_NUMBER_FORMAT"))) { case SWITCH_CAUSE_SYSTEM_SHUTDOWN:
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, "SYSTEM_SHUTDOWN");
} else { break;
/* other unspecified error */ case SWITCH_CAUSE_GATEWAY_DOWN:
response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, (char *)api_stream.data); response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, "GATEWAY_DOWN");
} break;
} case SWITCH_CAUSE_INVALID_NUMBER_FORMAT:
response = iks_new_error_detailed(iq, STANZA_ERROR_RESOURCE_CONSTRAINT, "INVALID_NUMBER_FORMAT");
break;
default:
response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, switch_channel_cause2str(cause));
break;
} }
switch_mutex_unlock(RAYO_ACTOR(call)->mutex);
} else {
switch_mutex_lock(RAYO_ACTOR(call)->mutex);
if (!zstr(call->dial_request_id)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to exec originate API\n");
call->dial_request_failed = 1;
call->dial_request_id = NULL;
response = iks_new_error_detailed(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "Failed to execute originate API");
} }
switch_mutex_unlock(RAYO_ACTOR(call)->mutex); switch_mutex_unlock(RAYO_ACTOR(call)->mutex);
} }
switch_safe_free(api_stream.data);
} else { } else {
/* will only happen if misconfigured */ /* will only happen if misconfigured */
switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_CRIT, "No dial gateway found for %s!\n", dial_to); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rayo_call_get_uuid(call)), SWITCH_LOG_CRIT, "dial: No dial gateway found for %s!\n", dial_to);
call->dial_request_failed = 1; call->dial_request_failed = 1;
call->dial_request_id = NULL; call->dial_request_id = NULL;
response = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "No dial gateway found for %s!\n", dial_to); response = iks_new_error_detailed_printf(iq, STANZA_ERROR_INTERNAL_SERVER_ERROR, "dial: No dial gateway found for %s!\n", dial_to);
goto done; goto done;
} }
...@@ -2678,7 +2833,10 @@ done: ...@@ -2678,7 +2833,10 @@ done:
} }
iks_delete(iq); iks_delete(iq);
switch_safe_free(stream.data);
if (originate_vars) {
switch_event_destroy(&originate_vars);
}
{ {
switch_memory_pool_t *pool = dtdata->pool; switch_memory_pool_t *pool = dtdata->pool;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论