diff --git a/CHANGES b/CHANGES index 91e170ffbb20c7519239106cb0ab30670c7e38a2..78a464f44430a75810c636b5e42fa327030ab1c4 100644 --- a/CHANGES +++ b/CHANGES @@ -232,6 +232,36 @@ app_confbridge conference state and made the locked column a yes/no value instead of a locked/unlocked value. +REDIRECTING(reason) +------------------ + * The REDIRECTING(reason) value is now treated consistently between + chan_sip and chan_pjsip. + + Both channel drivers match incoming reason values with values documented + by REDIRECTING(reason) and values documented by RFC5806 regardless of + whether they are quoted or not. RFC5806 values are mapped to the + equivalent REDIRECTING(reason) documented value and is set in + REDIRECTING(reason). e.g., an incoming RFC5806 'unconditional' value or a + quoted string version ('"unconditional"') is converted to + REDIRECTING(reason)'s 'cfu' value. The user's dialplan only needs to deal + with 'cfu' instead of any of the aliases. + + The incoming 480 response reason text supported by chan_sip checks for + known reason values and if not matched then puts quotes around the reason + string and assigns that to REDIRECTING(reason). + + Both channel drivers send outgoing known REDIRECTING(reason) values as the + unquoted RFC5806 equivalent. User custom values are either sent as is or + with added quotes if SIP doesn't allow a character within the value as + part of a RFC3261 Section 25.1 token. Note that there are still + limitations on what characters can be put in a custom user value. e.g., + embedding quotes in the middle of the reason string is just going to cause + you grief. + + * Setting a REDIRECTING(reason) value now recognizes RFC5806 aliases. + e.g., Setting REDIRECTING(reason) to 'unconditional' is converted to the + 'cfu' value. + res_pjproject ------------------ * This module is the successor of res_pjsip_log_forwarder. As well as diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 0b0d114820cd7b59ab920a4b50e4d107b1dfcacb..2d5843d91abbe04f9146e37c0deec5f3af0a2013 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -710,7 +710,7 @@ static const struct cfsip_methods { */ static const struct sip_reasons { enum AST_REDIRECTING_REASON code; - char * const text; + const char *text; } sip_reason_table[] = { { AST_REDIRECTING_REASON_UNKNOWN, "unknown" }, { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" }, @@ -723,8 +723,8 @@ static const struct sip_reasons { { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" }, { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" }, { AST_REDIRECTING_REASON_AWAY, "away" }, - { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"}, - { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"}, + { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" }, /* Non-standard */ + { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" }, /* Non-standard */ }; @@ -2375,52 +2375,54 @@ static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue) return errorvalue; } -static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text) +/*! + * \internal + * \brief Determine if the given string is a SIP token. + * \since 13.8.0 + * + * \param str String to determine if is a SIP token. + * + * \note A token is defined by RFC3261 Section 25.1 + * + * \return Non-zero if the string is a SIP token. + */ +static int sip_is_token(const char *str) { - enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN; - int i; + int is_token; + + if (ast_strlen_zero(str)) { + /* An empty string is not a token. */ + return 0; + } - for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) { - if (!strcasecmp(text, sip_reason_table[i].text)) { - ast = sip_reason_table[i].code; + is_token = 1; + do { + if (!isalnum(*str) + && !strchr("-.!%*_+`'~", *str)) { + /* The character is not allowed in a token. */ + is_token = 0; break; } - } + } while (*++str); - return ast; + return is_token; } -static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup) +static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason) { - int code = reason->code; + int idx; + int code; - /* If there's a specific string set, then we just - * use it. - */ + /* use specific string if given */ if (!ast_strlen_zero(reason->str)) { - /* If we care about whether this can be found in - * the table, then we need to check about that. - */ - if (table_lookup) { - /* If the string is literally "unknown" then don't bother with the lookup - * because it can lead to a false negative. - */ - if (!strcasecmp(reason->str, "unknown") || - sip_reason_str_to_code(reason->str) != AST_REDIRECTING_REASON_UNKNOWN) { - *table_lookup = TRUE; - } else { - *table_lookup = FALSE; - } - } return reason->str; } - if (table_lookup) { - *table_lookup = TRUE; - } - - if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) { - return sip_reason_table[code].text; + code = reason->code; + for (idx = 0; idx < ARRAY_LEN(sip_reason_table); ++idx) { + if (code == sip_reason_table[idx].code) { + return sip_reason_table[idx].text; + } } return "unknown"; @@ -14219,7 +14221,7 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt) { struct ast_party_id diverting_from; const char *reason; - int found_in_table; + const char *quote_str; char header_text[256]; char encoded_number[SIPBUFSIZE/2]; @@ -14244,17 +14246,18 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt) ast_copy_string(encoded_number, diverting_from.number.str, sizeof(encoded_number)); } - reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason, &found_in_table); + reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason); + + /* Reason is either already quoted or it is a token to not need quotes added. */ + quote_str = *reason == '\"' || sip_is_token(reason) ? "" : "\""; /* We at least have a number to place in the Diversion header, which is enough */ if (!diverting_from.name.valid || ast_strlen_zero(diverting_from.name.str)) { snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s%s%s", - encoded_number, - ast_sockaddr_stringify_host_remote(&pvt->ourip), - found_in_table ? "" : "\"", - reason, - found_in_table ? "" : "\""); + encoded_number, + ast_sockaddr_stringify_host_remote(&pvt->ourip), + quote_str, reason, quote_str); } else { char escaped_name[SIPBUFSIZE/2]; if (sip_cfg.pedanticsipchecking) { @@ -14263,12 +14266,10 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt) ast_copy_string(escaped_name, diverting_from.name.str, sizeof(escaped_name)); } snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s%s%s", - escaped_name, - encoded_number, - ast_sockaddr_stringify_host_remote(&pvt->ourip), - found_in_table ? "" : "\"", - reason, - found_in_table ? "" : "\""); + escaped_name, + encoded_number, + ast_sockaddr_stringify_host_remote(&pvt->ourip), + quote_str, reason, quote_str); } add_header(req, "Diversion", header_text); @@ -17740,6 +17741,9 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c char *params, *reason_param = NULL; struct sip_request *req; + ast_assert(reason_code != NULL); + ast_assert(reason_str != NULL); + req = oreq ? oreq : &p->initreq; ast_copy_string(tmp, sip_get_header(req, "Diversion"), sizeof(tmp)); @@ -17773,16 +17777,6 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c if ((end = strchr(reason_param, ';'))) { *end = '\0'; } - /* Remove enclosing double-quotes */ - if (*reason_param == '"') - reason_param = ast_strip_quoted(reason_param, "\"", "\""); - if (!ast_strlen_zero(reason_param)) { - sip_set_redirstr(p, reason_param); - if (p->owner) { - pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause); - pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param); - } - } } } @@ -17814,12 +17808,27 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c } if (!ast_strlen_zero(reason_param)) { - if (reason_code) { - *reason_code = sip_reason_str_to_code(reason_param); + *reason_str = ast_strdup(reason_param); + + /* Remove any enclosing double-quotes */ + if (*reason_param == '"') { + reason_param = ast_strip_quoted(reason_param, "\"", "\""); } - if (reason_str) { - *reason_str = ast_strdup(reason_param); + *reason_code = ast_redirecting_reason_parse(reason_param); + if (*reason_code < 0) { + *reason_code = AST_REDIRECTING_REASON_UNKNOWN; + } else { + ast_free(*reason_str); + *reason_str = ast_strdup(""); + } + + if (!ast_strlen_zero(reason_param)) { + sip_set_redirstr(p, reason_param); + if (p->owner) { + pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause); + pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param); + } } } @@ -22698,10 +22707,11 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request redirecting->to.name.str = redirecting_to_name; } redirecting->reason.code = reason; + ast_free(redirecting->reason.str); + redirecting->reason.str = reason_str; if (reason_str) { - ast_debug(3, "Got redirecting reason %s\n", reason_str); - ast_free(redirecting->reason.str); - redirecting->reason.str = reason_str; + ast_debug(3, "Got redirecting reason %s\n", ast_strlen_zero(reason_str) + ? sip_reason_code_to_str(&redirecting->reason) : reason_str); } } @@ -23502,14 +23512,22 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest if (p->owner && !req->ignore) { struct ast_party_redirecting redirecting; struct ast_set_party_redirecting update_redirecting; + char *quoted_rest = ast_alloca(strlen(rest) + 3); + ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(p->owner)); memset(&update_redirecting, 0, sizeof(update_redirecting)); - redirecting.reason.code = sip_reason_str_to_code(rest); - redirecting.reason.str = ast_strdup(rest); + redirecting.reason.code = ast_redirecting_reason_parse(rest); + if (redirecting.reason.code < 0) { + sprintf(quoted_rest, "\"%s\"", rest);/* Safe */ + + redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN; + redirecting.reason.str = quoted_rest; + } else { + redirecting.reason.str = ""; + } ast_channel_queue_redirecting_update(p->owner, &redirecting, &update_redirecting); - ast_party_redirecting_free(&redirecting); ast_queue_control(p->owner, AST_CONTROL_BUSY); } diff --git a/main/callerid.c b/main/callerid.c index 69fe6ff70629f835fa3b71ec7f0e8fa8416ff15c..d2b3218922897c08aaa270ec73fbdf13bd074d3f 100644 --- a/main/callerid.c +++ b/main/callerid.c @@ -1209,7 +1209,16 @@ static const struct ast_value_translation redirecting_reason_types[] = { { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out_of_order", "Called DTE Out-Of-Order" }, { AST_REDIRECTING_REASON_AWAY, "away", "Callee is Away" }, { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte", "Call Forwarding By The Called DTE" }, - { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm", "Call is being redirected to user's voicemail"}, + { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm", "Call is being redirected to user's voicemail" }, + + /* Convenience SIP aliases. Alias descriptions are not used. */ + { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" }, + { AST_REDIRECTING_REASON_NO_ANSWER, "no-answer" }, + { AST_REDIRECTING_REASON_UNCONDITIONAL, "unconditional" }, + { AST_REDIRECTING_REASON_TIME_OF_DAY, "time-of-day" }, + { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" }, + { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" }, + { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" }, /* *INDENT-ON* */ }; @@ -1232,7 +1241,7 @@ const char *ast_redirecting_reason_describe(int data) for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) { if (redirecting_reason_types[index].value == data) { - return redirecting_reason_types[index].description; + return redirecting_reason_types[index].description ?: "Redirecting reason alias-bug"; } } diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c index ea2c7cd13351fcfc0d78646a0758f7e2bc99a207..f1a6ddf77e5bb9456cd5deb933b7e863ae0e01ca 100644 --- a/res/res_pjsip_diversion.c +++ b/res/res_pjsip_diversion.c @@ -37,6 +37,39 @@ static const pj_str_t diversion_name = { "Diversion", 9 }; +/*! + * \internal + * \brief Determine if the given string is a SIP token. + * \since 13.8.0 + * + * \param str String to determine if is a SIP token. + * + * \note A token is defined by RFC3261 Section 25.1 + * + * \return Non-zero if the string is a SIP token. + */ +static int sip_is_token(const char *str) +{ + int is_token; + + if (ast_strlen_zero(str)) { + /* An empty string is not a token. */ + return 0; + } + + is_token = 1; + do { + if (!isalnum(*str) + && !strchr("-.!%*_+`'~", *str)) { + /* The character is not allowed in a token. */ + is_token = 0; + break; + } + } while (*++str); + + return is_token; +} + /*! \brief Diversion header reasons * * The core defines a bunch of constants used to define @@ -46,7 +79,7 @@ static const pj_str_t diversion_name = { "Diversion", 9 }; */ static const struct reasons { enum AST_REDIRECTING_REASON code; - char *const text; + const char *text; } reason_table[] = { { AST_REDIRECTING_REASON_UNKNOWN, "unknown" }, { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" }, @@ -59,39 +92,28 @@ static const struct reasons { { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" }, { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" }, { AST_REDIRECTING_REASON_AWAY, "away" }, - { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"}, - { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"}, + { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" }, /* Non-standard */ + { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" }, /* Non-standard */ }; static const char *reason_code_to_str(const struct ast_party_redirecting_reason *reason) { - int code = reason->code; + int idx; + int code; /* use specific string if given */ if (!ast_strlen_zero(reason->str)) { return reason->str; } - if (code >= 0 && code < ARRAY_LEN(reason_table)) { - return reason_table[code].text; - } - - return "unknown"; -} - -static enum AST_REDIRECTING_REASON reason_str_to_code(const char *text) -{ - enum AST_REDIRECTING_REASON code = AST_REDIRECTING_REASON_UNKNOWN; - int i; - - for (i = 0; i < ARRAY_LEN(reason_table); ++i) { - if (!strcasecmp(text, reason_table[i].text)) { - code = reason_table[i].code; - break; + code = reason->code; + for (idx = 0; idx < ARRAY_LEN(reason_table); ++idx) { + if (code == reason_table[idx].code) { + return reason_table[idx].text; } } - return code; + return "unknown"; } static pjsip_fromto_hdr *get_diversion_header(pjsip_rx_data *rdata) @@ -159,13 +181,31 @@ static void set_redirecting_reason(pjsip_fromto_hdr *hdr, { static const pj_str_t reason_name = { "reason", 6 }; pjsip_param *reason = pjsip_param_find(&hdr->other_param, &reason_name); + char *reason_str; if (!reason) { return; } set_redirecting_value(&data->str, &reason->value); - data->code = reason_str_to_code(data->str); + if (!data->str) { + /* Oops, allocation failure */ + return; + } + reason_str = ast_strdupa(data->str); + + /* Remove any enclosing double-quotes */ + if (*reason_str == '"') { + reason_str = ast_strip_quoted(reason_str, "\"", "\""); + } + + data->code = ast_redirecting_reason_parse(reason_str); + if (data->code < 0) { + data->code = AST_REDIRECTING_REASON_UNKNOWN; + } else { + ast_free(data->str); + data->str = ast_strdup(""); + } } static void set_redirecting(struct ast_sip_session *session, @@ -251,6 +291,9 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect pjsip_sip_uri *uri; pjsip_param *param; pjsip_fromto_hdr *old_hdr; + const char *reason_str; + const char *quote_str; + char *reason_buf; struct ast_party_id *id = &data->from; pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri; @@ -272,7 +315,17 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); param->name = pj_str("reason"); - param->value = pj_str((char*)reason_code_to_str(&data->reason)); + + reason_str = reason_code_to_str(&data->reason); + + /* Reason is either already quoted or it is a token to not need quotes added. */ + quote_str = *reason_str == '\"' || sip_is_token(reason_str) ? "" : "\""; + + reason_buf = pj_pool_alloc(tdata->pool, strlen(reason_str) + 3); + sprintf(reason_buf, "%s%s%s", quote_str, reason_str, quote_str);/* Safe */ + + param->value = pj_str(reason_buf); + pj_list_insert_before(&hdr->other_param, param); hdr->uri = (pjsip_uri *) name_addr;