Allow for redirecting reasons to be set to arbitrary strings.
authorMark Michelson <mmichelson@digium.com>
Tue, 25 Sep 2012 19:29:14 +0000 (19:29 +0000)
committerMark Michelson <mmichelson@digium.com>
Tue, 25 Sep 2012 19:29:14 +0000 (19:29 +0000)
This allows for the REDIRECTING dialplan function to be
used to set the reason to any string.

The SIP channel driver has been modified to set the redirecting
reason string to the value received in a Diversion header. In
addition, SIP 480 response reason text will set the redirecting
reason as well.

(closes issue AST-942)
reported by Malcolm Davenport

(closes issue AST-943)
reported by Malcolm Davenport

Review: https://reviewboard.asterisk.org/r/2101

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@373701 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_misdn.c
channels/chan_sip.c
channels/sig_pri.c
funcs/func_callerid.c
include/asterisk/callerid.h
include/asterisk/channel.h
main/callerid.c
main/channel.c

diff --git a/CHANGES b/CHANGES
index da591e7..e2fea7b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -30,6 +30,13 @@ Queue
    Reports 'InUse' for no logged in agents or no free agents.
    Reports 'Idle' when an agent is free.
 
+Core
+------------------
+ * Redirecting reasons can now be set to arbitrary strings. This means
+   that the REDIRECTING dialplan function can be used to set the redirecting
+   reason to any string. It also allows for custom strings to be read as the
+   redirecting reason from SIP Diversion headers.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 10 to Asterisk 11 --------------------
 ------------------------------------------------------------------------------
index 3578bc5..2f644a8 100644 (file)
@@ -6383,7 +6383,7 @@ static void misdn_copy_redirecting_from_ast(struct misdn_bchannel *bc, struct as
                bc->redirecting.to.number_plan = NUMPLAN_UNKNOWN;
        }
 
-       bc->redirecting.reason = ast_to_misdn_reason(ast_channel_redirecting(ast)->reason);
+       bc->redirecting.reason = ast_to_misdn_reason(ast_channel_redirecting(ast)->reason.code);
        bc->redirecting.count = ast_channel_redirecting(ast)->count;
 }
 
@@ -6427,7 +6427,7 @@ static void misdn_copy_redirecting_to_ast(struct ast_channel *ast, const struct
                | misdn_to_ast_screen(redirect->to.screening);
        redirecting.to.tag = tag;
 
-       redirecting.reason = misdn_to_ast_reason(redirect->reason);
+       redirecting.reason.code = misdn_to_ast_reason(redirect->reason);
        redirecting.count = redirect->count;
 
        ast_channel_set_redirecting(ast, &redirecting, &update_redirecting);
index e41ccc1..615f576 100644 (file)
@@ -1576,7 +1576,7 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
 static int set_address_from_contact(struct sip_pvt *pvt);
 static void check_via(struct sip_pvt *p, struct sip_request *req);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason, char **reason_str);
 static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
 static int transmit_state_notify(struct sip_pvt *p, struct state_notify_data *data, int full, int timeout);
 static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
@@ -2379,8 +2379,35 @@ static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
        return ast;
 }
 
-static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code)
+static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup)
 {
+       int code = reason->code;
+
+       /* If there's a specific string set, then we just
+        * use it.
+        */
+       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;
        }
@@ -13528,7 +13555,9 @@ 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;
        char header_text[256];
+       char encoded_number[SIPBUFSIZE/2];
 
        /* We skip this entirely if the configuration doesn't allow diversion headers */
        if (!sip_cfg.send_diversion) {
@@ -13545,17 +13574,37 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
                return;
        }
 
-       reason = sip_reason_code_to_str(ast_channel_redirecting(pvt->owner)->reason);
+       if (sip_cfg.pedanticsipchecking) {
+               ast_uri_encode(diverting_from.number.str, encoded_number, sizeof(encoded_number), ast_uri_sip_user);
+       } else {
+               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);
 
        /* 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", diverting_from.number.str,
-                               ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
+               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 ? "" : "\"");
        } else {
-               snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s",
-                               diverting_from.name.str, diverting_from.number.str,
-                               ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
+               char escaped_name[SIPBUFSIZE/2];
+               if (sip_cfg.pedanticsipchecking) {
+                       ast_escape_quoted(diverting_from.name.str, escaped_name, sizeof(escaped_name));
+               } else {
+                       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 ? "" : "\"");
        }
 
        add_header(req, "Diversion", header_text);
@@ -16839,8 +16888,19 @@ static int get_rpid(struct sip_pvt *p, struct sip_request *oreq)
        return 1;
 }
 
-/*! \brief Get referring dnis */
-static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason)
+/*! \brief Get referring dnis
+ *
+ * \param p dialog information
+ * \param oreq The request to retrieve RDNIS from
+ * \param[out] name The name of the party redirecting the call.
+ * \param[out] number The number of the party redirecting the call.
+ * \param[out] reason_code The numerical code corresponding to the reason for the redirection.
+ * \param[out] reason_str A string describing the reason for redirection. Will never be zero-length
+ *
+ * \retval -1 Could not retrieve RDNIS information
+ * \retval 0 RDNIS successfully retrieved
+ */
+static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason_code, char **reason_str)
 {
        char tmp[256], *exten, *rexten, *rdomain, *rname = NULL;
        char *params, *reason_param = NULL;
@@ -16897,9 +16957,9 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
        if (p->owner)
                pbx_builtin_setvar_helper(p->owner, "__SIPRDNISDOMAIN", rdomain);
 
-       if (sip_debug_test_pvt(p))
+       if (sip_debug_test_pvt(p)) {
                ast_verbose("RDNIS for this call is %s (reason %s)\n", exten, S_OR(reason_param, ""));
-
+       }
        /*ast_string_field_set(p, rdnis, rexten);*/
 
        if (*tmp == '\"') {
@@ -16919,8 +16979,14 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
                *name = ast_strdup(rname);
        }
 
-       if (reason && !ast_strlen_zero(reason_param)) {
-               *reason = sip_reason_str_to_code(reason_param);
+       if (!ast_strlen_zero(reason_param)) {
+               if (reason_code) {
+                       *reason_code = sip_reason_str_to_code(reason_param);
+               }
+
+               if (reason_str) {
+                       *reason_str = ast_strdup(reason_param);
+               }
        }
 
        return 0;
@@ -21622,11 +21688,12 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request
        char *redirecting_from_number = NULL;
        char *redirecting_to_name = NULL;
        char *redirecting_to_number = NULL;
+       char *reason_str = NULL;
        int reason = AST_REDIRECTING_REASON_UNCONDITIONAL;
        int is_response = req->method == SIP_RESPONSE;
        int res = 0;
 
-       res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason);
+       res = get_rdnis(p, req, &redirecting_from_name, &redirecting_from_number, &reason, &reason_str);
        if (res == -1) {
                if (is_response) {
                        get_name_and_number(sip_get_header(req, "TO"), &redirecting_from_name, &redirecting_from_number);
@@ -21679,7 +21746,12 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request
                ast_free(redirecting->to.name.str);
                redirecting->to.name.str = redirecting_to_name;
        }
-       redirecting->reason = reason;
+       redirecting->reason.code = reason;
+       if (reason_str) {
+               ast_debug(3, "Got redirecting reason %s\n", reason_str);
+               ast_free(redirecting->reason.str);
+               redirecting->reason.str = reason_str;
+       }
 }
 
 /*! \brief Parse 302 Moved temporalily response
@@ -22450,6 +22522,28 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                }
                break;
 
+       case 480: /* Temporarily unavailable. */
+               /* RFC 3261 encourages setting the reason phrase to something indicative
+                * of why the endpoint is not available. We will make this readable via the
+                * redirecting reason.
+                */
+               xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+               append_history(p, "TempUnavailable", "Endpoint is temporarily unavailable.");
+               if (p->owner && !req->ignore) {
+                       struct ast_party_redirecting redirecting;
+                       struct ast_set_party_redirecting update_redirecting;
+                       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);
+
+                       ast_channel_queue_redirecting_update(p->owner, &redirecting, &update_redirecting);
+                       ast_party_redirecting_free(&redirecting);
+
+                       ast_queue_control(p->owner, AST_CONTROL_BUSY);
+               }
+               break;
        case 487: /* Cancelled transaction */
                /* We have sent CANCEL on an outbound INVITE
                        This transaction is already scheduled to be killed by sip_hangup().
@@ -23320,7 +23414,16 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                                handle_response_invite(p, resp, rest, req, seqno);
                        }
                        break;
-
+               case 480:
+                       if (sipmethod == SIP_INVITE) {
+                               handle_response_invite(p, resp, rest, req, seqno);
+                       } else if (sipmethod == SIP_SUBSCRIBE) {
+                               handle_response_subscribe(p, resp, rest, req, seqno);
+                       } else if (owner) {
+                               /* No specific handler. Default to congestion */
+                               ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
+                       }
+                       break;
                case 481: /* Call leg does not exist */
                        if (sipmethod == SIP_INVITE) {
                                handle_response_invite(p, resp, rest, req, seqno);
@@ -23409,7 +23512,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                                        }
                                        break;
                                case 482: /* Loop Detected */
-                               case 480: /* Temporarily Unavailable */
                                case 404: /* Not Found */
                                case 410: /* Gone */
                                case 400: /* Bad Request */
index bf73ced..93a77ef 100644 (file)
@@ -916,8 +916,8 @@ static void sig_pri_redirecting_update(struct sig_pri_chan *pvt, struct ast_chan
        sig_pri_party_id_from_ast(&pri_redirecting.to, &redirecting_to);
        sig_pri_party_id_from_ast(&pri_redirecting.orig_called, &redirecting_orig);
        pri_redirecting.count = ast_redirecting->count;
-       pri_redirecting.orig_reason = ast_to_pri_reason(ast_redirecting->orig_reason);
-       pri_redirecting.reason = ast_to_pri_reason(ast_redirecting->reason);
+       pri_redirecting.orig_reason = ast_to_pri_reason(ast_redirecting->orig_reason.code);
+       pri_redirecting.reason = ast_to_pri_reason(ast_redirecting->reason.code);
 
        pri_redirecting_update(pvt->pri->pri, pvt->call, &pri_redirecting);
 }
@@ -2177,8 +2177,8 @@ static void sig_pri_redirecting_convert(struct ast_party_redirecting *ast_redire
        sig_pri_party_id_convert(&ast_redirecting->from, &pri_redirecting->from, pri);
        sig_pri_party_id_convert(&ast_redirecting->to, &pri_redirecting->to, pri);
        ast_redirecting->count = pri_redirecting->count;
-       ast_redirecting->reason = pri_to_ast_reason(pri_redirecting->reason);
-       ast_redirecting->orig_reason = pri_to_ast_reason(pri_redirecting->orig_reason);
+       ast_redirecting->reason.code = pri_to_ast_reason(pri_redirecting->reason);
+       ast_redirecting->orig_reason.code = pri_to_ast_reason(pri_redirecting->orig_reason);
 }
 
 /*!
index 7895753..30026af 100644 (file)
@@ -1493,7 +1493,7 @@ static int redirecting_read(struct ast_channel *chan, const char *cmd, char *dat
        if (!strcasecmp("orig", member.argv[0])) {
                if (member.argc == 2 && !strcasecmp("reason", member.argv[1])) {
                        ast_copy_string(buf,
-                               ast_redirecting_reason_name(ast_redirecting->orig_reason), len);
+                               ast_redirecting_reason_name(&ast_redirecting->orig_reason), len);
                } else {
                        status = party_id_read(buf, len, member.argc - 1, member.argv + 1,
                                &ast_redirecting->orig);
@@ -1537,7 +1537,7 @@ static int redirecting_read(struct ast_channel *chan, const char *cmd, char *dat
                        ast_named_caller_presentation(
                                ast_party_id_presentation(&ast_redirecting->from)), len);
        } else if (member.argc == 1 && !strcasecmp("reason", member.argv[0])) {
-               ast_copy_string(buf, ast_redirecting_reason_name(ast_redirecting->reason), len);
+               ast_copy_string(buf, ast_redirecting_reason_name(&ast_redirecting->reason), len);
        } else if (member.argc == 1 && !strcasecmp("count", member.argv[0])) {
                snprintf(buf, len, "%d", ast_redirecting->count);
        } else if (1 < member.argc && !strcasecmp("priv", member.argv[0])) {
@@ -1659,10 +1659,16 @@ static int redirecting_write(struct ast_channel *chan, const char *cmd, char *da
                        }
 
                        if (reason < 0) {
-                               ast_log(LOG_ERROR,
-                                       "Unknown redirecting orig reason '%s', value unchanged\n", val);
+                       /* The argument passed into the function does not correspond to a pre-defined
+                        * reason, so we can just set the reason string to what was given and set the
+                        * code to be unknown
+                        */
+                               redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                               redirecting.orig_reason.str = val;
+                               set_it(chan, &redirecting, NULL);
                        } else {
-                               redirecting.orig_reason = reason;
+                               redirecting.orig_reason.code = reason;
+                               redirecting.orig_reason.str = "";
                                set_it(chan, &redirecting, NULL);
                        }
                } else {
@@ -1742,9 +1748,16 @@ static int redirecting_write(struct ast_channel *chan, const char *cmd, char *da
                }
 
                if (reason < 0) {
-                       ast_log(LOG_ERROR, "Unknown redirecting reason '%s', value unchanged\n", val);
+                       /* The argument passed into the function does not correspond to a pre-defined
+                        * reason, so we can just set the reason string to what was given and set the
+                        * code to be unknown
+                        */
+                       redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                       redirecting.reason.str = val;
+                       set_it(chan, &redirecting, NULL);
                } else {
-                       redirecting.reason = reason;
+                       redirecting.reason.code = reason;
+                       redirecting.reason.str = "";
                        set_it(chan, &redirecting, NULL);
                }
        } else if (member.argc == 1 && !strcasecmp("count", member.argv[0])) {
index 7c4905e..4f32dbf 100644 (file)
@@ -424,15 +424,17 @@ int ast_redirecting_reason_parse(const char *data);
  */
 const char *ast_redirecting_reason_describe(int data);
 
+struct ast_party_redirecting_reason;
+
 /*!
  * \since 1.8
  * \brief Convert redirecting reason value to text code
  *
- * \param data Q931_REDIRECTING_REASON from callerid.h
+ * \param data ast_party_redirecting_reason structure from channel.h
  *
  * \return string for config file
  */
-const char *ast_redirecting_reason_name(int data);
+const char *ast_redirecting_reason_name(const struct ast_party_redirecting_reason *data);
 
 /*!
  * \brief Connected line update source code
index b60c8ad..fed032a 100644 (file)
@@ -450,6 +450,21 @@ struct ast_set_party_connected_line {
 };
 
 /*!
+ * \brief Redirecting reason information
+ */
+struct ast_party_redirecting_reason {
+       /*! \brief a string value for the redirecting reason
+        *
+        * Useful for cases where an endpoint has specified a redirecting reason
+        * that does not correspond to an enum AST_REDIRECTING_REASON
+        */
+       char *str;
+
+       /*! \brief enum AST_REDIRECTING_REASON value for redirection */
+       int code;
+};
+
+/*!
  * \since 1.8
  * \brief Redirecting Line information.
  * RDNIS (Redirecting Directory Number Information Service)
@@ -477,14 +492,14 @@ struct ast_party_redirecting {
        /*! \brief Call is redirecting to a new party (Sent to the caller)  - private representation */
        struct ast_party_id priv_to;
 
-       /*! \brief Number of times the call was redirected */
-       int count;
+       /*! \brief Reason for the redirection */
+       struct ast_party_redirecting_reason reason;
 
-       /*! \brief enum AST_REDIRECTING_REASON value for redirection */
-       int reason;
+       /*! \brief Reason for the redirection by the original party */
+       struct ast_party_redirecting_reason orig_reason;
 
-       /*! \brief enum AST_REDIRECTING_REASON value for redirection by original party */
-       int orig_reason;
+       /*! \brief Number of times the call was redirected */
+       int count;
 };
 
 /*!
@@ -3229,6 +3244,68 @@ void ast_party_connected_line_collect_caller(struct ast_party_connected_line *co
 void ast_party_connected_line_free(struct ast_party_connected_line *doomed);
 
 /*!
+ * \brief Initialize the given redirecting reason structure
+ *
+ * \param init Redirecting reason structure to initialize
+ *
+ * \return Nothing
+ */
+void ast_party_redirecting_reason_init(struct ast_party_redirecting_reason *init);
+
+/*!
+ * \brief Copy the source redirecting reason information to the destination redirecting reason.
+ *
+ * \param dest Destination redirecting reason
+ * \param src Source redirecting reason
+ *
+ * \return Nothing
+ */
+void ast_party_redirecting_reason_copy(struct ast_party_redirecting_reason *dest,
+               const struct ast_party_redirecting_reason *src);
+
+/*!
+ * \brief Initialize the given redirecting reason structure using the given guide
+ * for a set update operation.
+ *
+ * \details
+ * The initialization is needed to allow a set operation to know if a
+ * value needs to be updated.  Simple integers need the guide's original
+ * value in case the set operation is not trying to set a new value.
+ * String values are simply set to NULL pointers if they are not going
+ * to be updated.
+ *
+ * \param init Redirecting reason structure to initialize.
+ * \param guide Source redirecting reason to use as a guide in initializing.
+ *
+ * \return Nothing
+ */
+void ast_party_redirecting_reason_set_init(struct ast_party_redirecting_reason *init,
+               const struct ast_party_redirecting_reason *guide);
+
+/*!
+ * \brief Set the redirecting reason information based on another redirecting reason source
+ *
+ * This is similar to ast_party_redirecting_reason_copy, except that NULL values for
+ * strings in the src parameter indicate not to update the corresponding dest values.
+ *
+ * \param dest The redirecting reason one wishes to update
+ * \param src The new redirecting reason values to update the dest
+ *
+ * \return Nothing
+ */
+void ast_party_redirecting_reason_set(struct ast_party_redirecting_reason *dest,
+               const struct ast_party_redirecting_reason *src);
+
+/*!
+ * \brief Destroy the redirecting reason contents
+ *
+ * \param doomed The redirecting reason to destroy.
+ *
+ * \return Nothing
+ */
+void ast_party_redirecting_reason_free(struct ast_party_redirecting_reason *doomed);
+
+/*!
  * \brief Initialize the given redirecting structure.
  * \since 1.8
  *
index d9f7418..db5e795 100644 (file)
@@ -1237,12 +1237,17 @@ const char *ast_redirecting_reason_describe(int data)
        return "not-known";
 }
 
-const char *ast_redirecting_reason_name(int data)
+const char *ast_redirecting_reason_name(const struct ast_party_redirecting_reason *data)
 {
        int index;
 
+       if (!ast_strlen_zero(data->str)) {
+               /* Use this string if it has been set. Otherwise, use the table. */
+               return data->str;
+       }
+
        for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) {
-               if (redirecting_reason_types[index].value == data) {
+               if (redirecting_reason_types[index].value == data->code) {
                        return redirecting_reason_types[index].name;
                }
        }
index dbc4703..751cbdd 100644 (file)
@@ -2235,6 +2235,49 @@ void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
        ast_party_id_free(&doomed->priv);
 }
 
+void ast_party_redirecting_reason_init(struct ast_party_redirecting_reason *init)
+{
+       init->str = NULL;
+       init->code = AST_REDIRECTING_REASON_UNKNOWN;
+}
+
+void ast_party_redirecting_reason_copy(struct ast_party_redirecting_reason *dest, const struct ast_party_redirecting_reason *src)
+{
+       if (dest == src) {
+               return;
+       }
+
+       ast_free(dest->str);
+       dest->str = ast_strdup(src->str);
+       dest->code = src->code;
+}
+
+void ast_party_redirecting_reason_set_init(struct ast_party_redirecting_reason *init, const struct ast_party_redirecting_reason *guide)
+{
+       init->str = NULL;
+       init->code = guide->code;
+}
+
+void ast_party_redirecting_reason_set(struct ast_party_redirecting_reason *dest, const struct ast_party_redirecting_reason *src)
+{
+       if (dest == src) {
+               return;
+       }
+
+       if (src->str && src->str != dest->str) {
+               ast_free(dest->str);
+               dest->str = ast_strdup(src->str);
+       }
+
+       dest->code = src->code;
+}
+
+void ast_party_redirecting_reason_free(struct ast_party_redirecting_reason *doomed)
+{
+       ast_free(doomed->str);
+}
+
+
 void ast_party_redirecting_init(struct ast_party_redirecting *init)
 {
        ast_party_id_init(&init->orig);
@@ -2243,9 +2286,9 @@ void ast_party_redirecting_init(struct ast_party_redirecting *init)
        ast_party_id_init(&init->priv_orig);
        ast_party_id_init(&init->priv_from);
        ast_party_id_init(&init->priv_to);
+       ast_party_redirecting_reason_init(&init->reason);
+       ast_party_redirecting_reason_init(&init->orig_reason);
        init->count = 0;
-       init->reason = AST_REDIRECTING_REASON_UNKNOWN;
-       init->orig_reason = AST_REDIRECTING_REASON_UNKNOWN;
 }
 
 void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
@@ -2261,9 +2304,9 @@ void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct
        ast_party_id_copy(&dest->priv_orig, &src->priv_orig);
        ast_party_id_copy(&dest->priv_from, &src->priv_from);
        ast_party_id_copy(&dest->priv_to, &src->priv_to);
+       ast_party_redirecting_reason_copy(&dest->reason, &src->reason);
+       ast_party_redirecting_reason_copy(&dest->orig_reason, &src->orig_reason);
        dest->count = src->count;
-       dest->reason = src->reason;
-       dest->orig_reason = src->orig_reason;
 }
 
 void ast_party_redirecting_set_init(struct ast_party_redirecting *init, const struct ast_party_redirecting *guide)
@@ -2274,9 +2317,9 @@ void ast_party_redirecting_set_init(struct ast_party_redirecting *init, const st
        ast_party_id_set_init(&init->priv_orig, &guide->priv_orig);
        ast_party_id_set_init(&init->priv_from, &guide->priv_from);
        ast_party_id_set_init(&init->priv_to, &guide->priv_to);
+       ast_party_redirecting_reason_set_init(&init->reason, &guide->reason);
+       ast_party_redirecting_reason_set_init(&init->orig_reason, &guide->orig_reason);
        init->count = guide->count;
-       init->reason = guide->reason;
-       init->orig_reason = guide->orig_reason;
 }
 
 void ast_party_redirecting_set(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src, const struct ast_set_party_redirecting *update)
@@ -2287,9 +2330,9 @@ void ast_party_redirecting_set(struct ast_party_redirecting *dest, const struct
        ast_party_id_set(&dest->priv_orig, &src->priv_orig, update ? &update->priv_orig : NULL);
        ast_party_id_set(&dest->priv_from, &src->priv_from, update ? &update->priv_from : NULL);
        ast_party_id_set(&dest->priv_to, &src->priv_to, update ? &update->priv_to : NULL);
+       ast_party_redirecting_reason_set(&dest->reason, &src->reason);
+       ast_party_redirecting_reason_set(&dest->orig_reason, &src->orig_reason);
        dest->count = src->count;
-       dest->reason = src->reason;
-       dest->orig_reason = src->orig_reason;
 }
 
 void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
@@ -2300,6 +2343,8 @@ void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
        ast_party_id_free(&doomed->priv_orig);
        ast_party_id_free(&doomed->priv_from);
        ast_party_id_free(&doomed->priv_to);
+       ast_party_redirecting_reason_free(&doomed->reason);
+       ast_party_redirecting_reason_free(&doomed->orig_reason);
 }
 
 /*! \brief Free a channel structure */
@@ -9616,7 +9661,7 @@ enum {
        AST_REDIRECTING_TO_NAME,
        AST_REDIRECTING_TO_NUMBER_PLAN,
        AST_REDIRECTING_TO_ID_PRESENTATION,/* Combined number and name presentation. */
-       AST_REDIRECTING_REASON,
+       AST_REDIRECTING_REASON_CODE,
        AST_REDIRECTING_COUNT,
        AST_REDIRECTING_FROM_SUBADDRESS,
        AST_REDIRECTING_FROM_SUBADDRESS_TYPE,
@@ -9656,7 +9701,7 @@ enum {
        AST_REDIRECTING_ORIG_SUBADDRESS_ODD_EVEN,
        AST_REDIRECTING_ORIG_SUBADDRESS_VALID,
        AST_REDIRECTING_ORIG_TAG,
-       AST_REDIRECTING_ORIG_REASON,
+       AST_REDIRECTING_ORIG_REASON_CODE,
        AST_REDIRECTING_PRIV_TO_NUMBER,
        AST_REDIRECTING_PRIV_TO_NUMBER_PLAN,
        AST_REDIRECTING_PRIV_TO_NUMBER_VALID,
@@ -9696,8 +9741,48 @@ enum {
        AST_REDIRECTING_PRIV_ORIG_SUBADDRESS_ODD_EVEN,
        AST_REDIRECTING_PRIV_ORIG_SUBADDRESS_VALID,
        AST_REDIRECTING_PRIV_ORIG_TAG,
+       AST_REDIRECTING_REASON_STR,
+       AST_REDIRECTING_ORIG_REASON_STR,
 };
 
+struct ast_party_redirecting_reason_ies {
+       int code;
+       int str;
+};
+
+static int redirecting_reason_build_data(unsigned char *data, size_t datalen,
+               const struct ast_party_redirecting_reason *reason, const char *label,
+               const struct ast_party_redirecting_reason_ies *ies)
+{
+       size_t length;
+       size_t pos = 0;
+       int32_t value;
+
+       if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
+               ast_log(LOG_WARNING, "No space left for %s code\n", label);
+               return -1;
+       }
+       data[pos++] = ies->code;
+       data[pos++] = sizeof(value);
+       value = htonl(reason->code);
+       memcpy(data + pos, &value, sizeof(value));
+       pos += sizeof(value);
+
+       if (reason->str) {
+               length = strlen(reason->str);
+               if (datalen < pos + sizeof(data[0] * 2) + length) {
+                       ast_log(LOG_WARNING, "No space left for %s string\n", label);
+                       return -1;
+               }
+               data[pos++] = ies->str;
+               data[pos++] = length;
+               memcpy(data + pos, reason->str, length);
+               pos += length;
+       }
+
+       return pos;
+}
+
 int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct ast_party_redirecting *redirecting, const struct ast_set_party_redirecting *update)
 {
        int32_t value;
@@ -9818,6 +9903,15 @@ int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct
                .tag = AST_REDIRECTING_PRIV_TO_TAG,
                .combined_presentation = 0,/* Not sent. */
        };
+       static const struct ast_party_redirecting_reason_ies reason_ies = {
+               .code = AST_REDIRECTING_REASON_CODE,
+               .str = AST_REDIRECTING_REASON_STR,
+       };
+
+       static const struct ast_party_redirecting_reason_ies orig_reason_ies = {
+               .code = AST_REDIRECTING_ORIG_REASON_CODE,
+               .str = AST_REDIRECTING_ORIG_REASON_STR,
+       };
 
        /* Redirecting frame version */
        if (datalen < pos + (sizeof(data[0]) * 2) + 1) {
@@ -9871,26 +9965,20 @@ int ast_redirecting_build_data(unsigned char *data, size_t datalen, const struct
        pos += res;
 
        /* Redirecting reason */
-       if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
-               ast_log(LOG_WARNING, "No space left for redirecting reason\n");
+       res = redirecting_reason_build_data(data + pos, datalen - pos, &redirecting->reason,
+                       "redirecting-reason", &reason_ies);
+       if (res < 0) {
                return -1;
        }
-       data[pos++] = AST_REDIRECTING_REASON;
-       data[pos++] = sizeof(value);
-       value = htonl(redirecting->reason);
-       memcpy(data + pos, &value, sizeof(value));
-       pos += sizeof(value);
+       pos += res;
 
        /* Redirecting original reason */
-       if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
-               ast_log(LOG_WARNING, "No space left for redirecting original reason\n");
+       res = redirecting_reason_build_data(data + pos, datalen - pos, &redirecting->orig_reason,
+                       "redirecting-orig-reason", &orig_reason_ies);
+       if (res < 0) {
                return -1;
        }
-       data[pos++] = AST_REDIRECTING_ORIG_REASON;
-       data[pos++] = sizeof(value);
-       value = htonl(redirecting->orig_reason);
-       memcpy(data + pos, &value, sizeof(value));
-       pos += sizeof(value);
+       pos += res;
 
        /* Redirecting count */
        if (datalen < pos + (sizeof(data[0]) * 2) + sizeof(value)) {
@@ -10614,25 +10702,43 @@ int ast_redirecting_parse_data(const unsigned char *data, size_t datalen, struct
                                redirecting->priv_to.tag[ie_len] = 0;
                        }
                        break;
-/* Redirecting reason */
-               case AST_REDIRECTING_REASON:
+/* Redirecting reason code */
+               case AST_REDIRECTING_REASON_CODE:
                        if (ie_len != sizeof(value)) {
                                ast_log(LOG_WARNING, "Invalid redirecting reason (%u)\n",
                                        (unsigned) ie_len);
                                break;
                        }
                        memcpy(&value, data + pos, sizeof(value));
-                       redirecting->reason = ntohl(value);
+                       redirecting->reason.code = ntohl(value);
                        break;
-/* Redirecting orig-reason */
-               case AST_REDIRECTING_ORIG_REASON:
+/* Redirecting reason string */
+               case AST_REDIRECTING_REASON_STR:
+                       ast_free(redirecting->reason.str);
+                       redirecting->reason.str = ast_malloc(ie_len + 1);
+                       if (redirecting->reason.str) {
+                               memcpy(redirecting->reason.str, data + pos, ie_len);
+                               redirecting->reason.str[ie_len] = 0;
+                       }
+                       break;
+/* Redirecting orig-reason code */
+               case AST_REDIRECTING_ORIG_REASON_CODE:
                        if (ie_len != sizeof(value)) {
                                ast_log(LOG_WARNING, "Invalid redirecting original reason (%u)\n",
                                        (unsigned) ie_len);
                                break;
                        }
                        memcpy(&value, data + pos, sizeof(value));
-                       redirecting->orig_reason = ntohl(value);
+                       redirecting->orig_reason.code = ntohl(value);
+                       break;
+/* Redirecting orig-reason string */
+               case AST_REDIRECTING_ORIG_REASON_STR:
+                       ast_free(redirecting->orig_reason.str);
+                       redirecting->orig_reason.str = ast_malloc(ie_len + 1);
+                       if (redirecting->orig_reason.str) {
+                               memcpy(redirecting->orig_reason.str, data + pos, ie_len);
+                               redirecting->orig_reason.str[ie_len] = 0;
+                       }
                        break;
 /* Redirecting count */
                case AST_REDIRECTING_COUNT: