SIP diversion: Fix REDIRECTING(reason) value inconsistencies.
[asterisk/asterisk.git] / res / res_pjsip_diversion.c
index a4ac157..f1a6ddf 100644 (file)
 
 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)
@@ -117,7 +139,9 @@ static void set_redirecting_value(char **dst, const pj_str_t *src)
 {
        ast_free(*dst);
        *dst = ast_malloc(pj_strlen(src) + 1);
-       ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+       if (*dst) {
+               ast_copy_pj_str(*dst, src, pj_strlen(src) + 1);
+       }
 }
 
 static void set_redirecting_id(pjsip_name_addr *name_addr, struct ast_party_id *data,
@@ -157,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,
@@ -248,6 +290,10 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
        pjsip_name_addr *name_addr;
        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;
@@ -269,10 +315,24 @@ 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;
+       old_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &diversion_name, NULL);
+       if (old_hdr) {
+               pj_list_erase(old_hdr);
+       }
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
 }
 
@@ -343,8 +403,8 @@ static int unload_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Add Diversion Header Support",
-               .support_level = AST_MODULE_SUPPORT_CORE,
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_APP_DEPEND,
-              );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+);