Merge "res_pjsip_transport_websocket: Fix use-after-free bugs."
authorMark Michelson <mmichelson@digium.com>
Wed, 10 Jun 2015 15:38:12 +0000 (10:38 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Wed, 10 Jun 2015 15:38:12 +0000 (10:38 -0500)
15 files changed:
apps/app_meetme.c
channels/chan_iax2.c
channels/chan_sip.c
channels/chan_skinny.c
include/asterisk/pbx.h
include/asterisk/strings.h
main/manager_channels.c
main/pbx.c
main/presencestate.c
main/stasis_channels.c
main/utils.c
pbx/pbx_config.c
tests/test_gosub.c
tests/test_pbx.c
tests/test_strings.c

index e74ad75..0c339d6 100644 (file)
@@ -7539,14 +7539,13 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
        ao2_unlock(trunk);
 
        if (!ast_strlen_zero(trunk->autocontext)) {
-               struct ast_context *context;
-               context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
-               if (!context) {
+               if (!ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically find or create "
                                "context '%s' for SLA!\n", trunk->autocontext);
                        return -1;
                }
-               if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
+
+               if (ast_add_extension(trunk->autocontext, 0 /* don't replace */, "s", 1,
                        NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically create extension "
                                "for trunk '%s'!\n", trunk->name);
@@ -7715,17 +7714,16 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
        ao2_unlock(station);
 
        if (!ast_strlen_zero(station->autocontext)) {
-               struct ast_context *context;
                struct sla_trunk_ref *trunk_ref;
-               context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
-               if (!context) {
+
+               if (!ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically find or create "
                                "context '%s' for SLA!\n", station->autocontext);
                        return -1;
                }
                /* The extension for when the handset goes off-hook.
                 * exten => station1,1,SLAStation(station1) */
-               if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
+               if (ast_add_extension(station->autocontext, 0 /* don't replace */, station->name, 1,
                        NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
                        ast_log(LOG_ERROR, "Failed to automatically create extension "
                                "for trunk '%s'!\n", station->name);
@@ -7738,7 +7736,7 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                        snprintf(hint, sizeof(hint), "SLA:%s", exten);
                        /* Extension for this line button 
                         * exten => station1_line1,1,SLAStation(station1_line1) */
-                       if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
+                       if (ast_add_extension(station->autocontext, 0 /* don't replace */, exten, 1,
                                NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create extension "
                                        "for trunk '%s'!\n", station->name);
@@ -7746,7 +7744,7 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
                        }
                        /* Hint for this line button 
                         * exten => station1_line1,hint,SLA:station1_line1 */
-                       if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
+                       if (ast_add_extension(station->autocontext, 0 /* don't replace */, exten, PRIORITY_HINT,
                                NULL, NULL, hint, NULL, NULL, sla_registrar)) {
                                ast_log(LOG_ERROR, "Failed to automatically create hint "
                                        "for trunk '%s'!\n", station->name);
index dbad79d..c797b87 100644 (file)
@@ -14626,7 +14626,6 @@ static void cleanup_thread_list(void *head)
 
 static int __unload_module(void)
 {
-       struct ast_context *con;
        int x;
 
        network_change_stasis_unsubscribe();
@@ -14703,9 +14702,7 @@ static int __unload_module(void)
        sched = NULL;
        ao2_ref(peercnts, -1);
 
-       con = ast_context_find(regcontext);
-       if (con)
-               ast_context_destroy(con, "IAX2");
+       ast_context_destroy_by_name(regcontext, "IAX2");
        ast_unload_realtime("iaxpeers");
 
        ao2_ref(iax2_tech.capabilities, -1);
index 7c4c8a6..1d58a84 100644 (file)
@@ -19740,8 +19740,7 @@ static void cleanup_stale_contexts(char *new, char *old)
                        }
 
                }
-               if (stalecontext)
-                       ast_context_destroy(ast_context_find(stalecontext), "SIP");
+               ast_context_destroy_by_name(stalecontext, "SIP");
        }
 }
 
@@ -34550,7 +34549,6 @@ static int unload_module(void)
 {
        struct sip_pvt *p;
        struct sip_threadinfo *th;
-       struct ast_context *con;
        struct ao2_iterator i;
        int wait_count;
 
@@ -34730,10 +34728,7 @@ static int unload_module(void)
        close(sipsock);
        io_context_destroy(io);
        ast_sched_context_destroy(sched);
-       con = ast_context_find(used_context);
-       if (con) {
-               ast_context_destroy(con, "SIP");
-       }
+       ast_context_destroy_by_name(used_context, "SIP");
        ast_unload_realtime("sipregs");
        ast_unload_realtime("sippeers");
        ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
index 03da0e0..08629fd 100644 (file)
@@ -2198,8 +2198,7 @@ static void cleanup_stale_contexts(char *new, char *old)
                        }
 
                }
-               if (stalecontext)
-                       ast_context_destroy(ast_context_find(stalecontext), "Skinny");
+               ast_context_destroy_by_name(stalecontext, "Skinny");
        }
 }
 
@@ -8717,7 +8716,6 @@ static int unload_module(void)
        struct skinny_device *d;
        struct skinny_line *l;
        struct skinny_subchannel *sub;
-       struct ast_context *con;
        pthread_t tempthread;
 
        ast_rtp_glue_unregister(&skinny_rtp_glue);
@@ -8778,9 +8776,7 @@ static int unload_module(void)
                ast_sched_context_destroy(sched);
        }
 
-       con = ast_context_find(used_context);
-       if (con)
-               ast_context_destroy(con, "Skinny");
+       ast_context_destroy_by_name(used_context, "Skinny");
 
        ao2_ref(default_cap, -1);
        return 0;
index c09de98..f5feb93 100644 (file)
@@ -300,7 +300,21 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
 void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *registrar);
 
 /*!
- * \brief Destroy a context (matches the specified context (or ANY context if NULL)
+ * \brief Destroy a context by name
+ *
+ * \param context Name of the context to destroy
+ * \param registrar who registered it
+ *
+ * You can optionally leave out the registrar parameter.  It will find it
+ * based on the context name.
+ *
+ * \retval -1 context not found
+ * \retval 0 Success
+ */
+int ast_context_destroy_by_name(const char *context, const char *registrar);
+
+/*!
+ * \brief Destroy a context (matches the specified context or ANY context if NULL)
  *
  * \param con context to destroy
  * \param registrar who registered it
index 7af92af..d361293 100644 (file)
@@ -310,6 +310,59 @@ char *ast_unescape_semicolon(char *s);
 char *ast_unescape_c(char *s);
 
 /*!
+ * \brief Escape the 'to_escape' characters in the given string.
+ *
+ * \note The given output buffer has to have enough memory allocated to store the
+ *       original string plus any escaped values.
+ *
+ * \param dest the escaped string
+ * \param s the source string to escape
+ * \param num number of characters to be copied from the source
+ * \param to_escape an array of characters to escape
+ *
+ * \return Pointer to the destination.
+ */
+char* ast_escape(char *dest, const char *s, size_t num, const char *to_escape);
+
+/*!
+ * \brief Escape standard 'C' sequences in the given string.
+ *
+ * \note The given output buffer has to have enough memory allocated to store the
+ *       original string plus any escaped values.
+ *
+ * \param dest the escaped string
+ * \param s the source string to escape
+ * \param num number of characters to be copied from the source
+ * \param to_escape an array of characters to escape
+ *
+ * \return Pointer to the escaped string.
+ */
+char* ast_escape_c(char *dest, const char *s, size_t num);
+
+/*!
+ * \brief Escape the 'to_escape' characters in the given string.
+ *
+ * \note Caller is responsible for freeing the returned string
+ *
+ * \param s the source string to escape
+ * \param to_escape an array of characters to escape
+ *
+ * \return Pointer to the escaped string or NULL.
+ */
+char *ast_escape_alloc(const char *s, const char *to_escape);
+
+/*!
+ * \brief Escape standard 'C' sequences in the given string.
+ *
+ * \note Caller is responsible for freeing the returned string
+ *
+ * \param s the source string to escape
+ *
+ * \return Pointer to the escaped string or NULL.
+ */
+char *ast_escape_c_alloc(const char *s);
+
+/*!
   \brief Size-limited null-terminating string copy.
   \param dst The destination buffer.
   \param src The source string
index e459a73..da7eeba 100644 (file)
@@ -408,6 +408,7 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
 {
        struct ast_str *out = ast_str_create(1024);
        int res = 0;
+       char *caller_name, *connected_name;
 
        if (!out) {
                return NULL;
@@ -418,6 +419,9 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
                return NULL;
        }
 
+       caller_name = ast_escape_c_alloc(snapshot->caller_name);
+       connected_name = ast_escape_c_alloc(snapshot->connected_name);
+
        res = ast_str_set(&out, 0,
                "%sChannel: %s\r\n"
                "%sChannelState: %u\r\n"
@@ -436,9 +440,9 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
                prefix, snapshot->state,
                prefix, ast_state2str(snapshot->state),
                prefix, S_OR(snapshot->caller_number, "<unknown>"),
-               prefix, S_OR(snapshot->caller_name, "<unknown>"),
+               prefix, S_OR(caller_name, "<unknown>"),
                prefix, S_OR(snapshot->connected_number, "<unknown>"),
-               prefix, S_OR(snapshot->connected_name, "<unknown>"),
+               prefix, S_OR(connected_name, "<unknown>"),
                prefix, snapshot->language,
                prefix, snapshot->accountcode,
                prefix, snapshot->context,
@@ -448,18 +452,26 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
 
        if (!res) {
                ast_free(out);
+               ast_free(caller_name);
+               ast_free(connected_name);
                return NULL;
        }
 
        if (snapshot->manager_vars) {
                struct ast_var_t *var;
+               char *val;
                AST_LIST_TRAVERSE(snapshot->manager_vars, var, entries) {
+                       val = ast_escape_c_alloc(var->value);
                        ast_str_append(&out, 0, "%sChanVariable: %s=%s\r\n",
                                       prefix,
-                                      var->name, var->value);
+                                      var->name, S_OR(val, ""));
+                       ast_free(val);
                }
        }
 
+       ast_free(caller_name);
+       ast_free(connected_name);
+
        return out;
 }
 
@@ -556,6 +568,9 @@ static struct ast_manager_event_blob *channel_new_callerid(
        struct ast_channel_snapshot *old_snapshot,
        struct ast_channel_snapshot *new_snapshot)
 {
+       struct ast_manager_event_blob *res;
+       char *callerid;
+
        /* No NewCallerid event on cache clear or first event */
        if (!old_snapshot || !new_snapshot) {
                return NULL;
@@ -565,11 +580,19 @@ static struct ast_manager_event_blob *channel_new_callerid(
                return NULL;
        }
 
-       return ast_manager_event_blob_create(
+       if (!(callerid = ast_escape_c_alloc(
+                     ast_describe_caller_presentation(new_snapshot->caller_pres)))) {
+               return NULL;
+       }
+
+       res = ast_manager_event_blob_create(
                EVENT_FLAG_CALL, "NewCallerid",
                "CID-CallingPres: %d (%s)\r\n",
                new_snapshot->caller_pres,
-               ast_describe_caller_presentation(new_snapshot->caller_pres));
+               callerid);
+
+       ast_free(callerid);
+       return res;
 }
 
 static struct ast_manager_event_blob *channel_new_connected_line(
index 45909f5..0da0fef 100644 (file)
@@ -8739,9 +8739,9 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                ast_rdlock_contexts();
                local_contexts = &contexts;
                tmp = ast_hashtab_lookup(contexts_table, &search);
-               ast_unlock_contexts();
                if (tmp) {
                        tmp->refcount++;
+                       ast_unlock_contexts();
                        return tmp;
                }
        } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
@@ -8765,11 +8765,13 @@ struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts,
                tmp->refcount = 1;
        } else {
                ast_log(LOG_ERROR, "Danger! We failed to allocate a context for %s!\n", name);
+               if (!extcontexts) {
+                       ast_unlock_contexts();
+               }
                return NULL;
        }
 
        if (!extcontexts) {
-               ast_wrlock_contexts();
                tmp->next = *local_contexts;
                *local_contexts = tmp;
                ast_hashtab_insert_safe(contexts_table, tmp); /*put this context into the tree */
@@ -9668,18 +9670,24 @@ int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const
 
 int ast_ignore_pattern(const char *context, const char *pattern)
 {
-       struct ast_context *con = ast_context_find(context);
+       int ret = 0;
+       struct ast_context *con;
 
+       ast_rdlock_contexts();
+       con = ast_context_find(context);
        if (con) {
                struct ast_ignorepat *pat;
 
                for (pat = con->ignorepats; pat; pat = pat->next) {
-                       if (ast_extension_match(pat->pattern, pattern))
-                               return 1;
+                       if (ast_extension_match(pat->pattern, pattern)) {
+                               ret = 1;
+                               break;
+                       }
                }
        }
+       ast_unlock_contexts();
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -10887,6 +10895,22 @@ void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *context
        }
 }
 
+int ast_context_destroy_by_name(const char *context, const char *registrar)
+{
+       struct ast_context *con;
+       int ret = -1;
+
+       ast_wrlock_contexts();
+       con = ast_context_find(context);
+       if (con) {
+               ast_context_destroy(con, registrar);
+               ret = 0;
+       }
+       ast_unlock_contexts();
+
+       return ret;
+}
+
 void ast_context_destroy(struct ast_context *con, const char *registrar)
 {
        ast_wrlock_contexts();
index 399613e..3be2ebe 100644 (file)
@@ -522,14 +522,23 @@ int ast_presence_state_engine_init(void)
 static struct ast_manager_event_blob *presence_state_to_ami(struct stasis_message *msg)
 {
        struct ast_presence_state_message *presence_state = stasis_message_data(msg);
+       struct ast_manager_event_blob *res;
 
-       return ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
+       char *subtype = ast_escape_c_alloc(presence_state->subtype);
+       char *message = ast_escape_c_alloc(presence_state->message);
+
+       res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
                "Presentity: %s\r\n"
                "Status: %s\r\n"
                "Subtype: %s\r\n"
                "Message: %s\r\n",
                presence_state->provider,
                ast_presence_state2str(presence_state->state),
-               presence_state->subtype,
-               presence_state->message);
+               subtype ?: "",
+                message ?: "");
+
+       ast_free(subtype);
+       ast_free(message);
+
+       return res;
 }
index 7dc57bf..1a4a90f 100644 (file)
@@ -793,8 +793,12 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
        struct ast_channel_blob *obj = stasis_message_data(msg);
        const char *variable =
                ast_json_string_get(ast_json_object_get(obj->blob, "variable"));
-       const char *value =
-               ast_json_string_get(ast_json_object_get(obj->blob, "value"));
+       RAII_VAR(char *, value, ast_escape_c_alloc(
+                        ast_json_string_get(ast_json_object_get(obj->blob, "value"))), ast_free);
+
+       if (!value) {
+               return NULL;
+       }
 
        if (obj->snapshot) {
                channel_event_string =
index b1b7ac9..3bf2c51 100644 (file)
@@ -1618,6 +1618,114 @@ char *ast_unescape_c(char *src)
        return ret;
 }
 
+/*
+ * Standard escape sequences - Note, '\0' is not included as a valid character
+ * to escape, but instead is used here as a NULL terminator for the string.
+ */
+char escape_sequences[] = {
+       '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\'', '\"', '\?', '\0'
+};
+
+/*
+ * Standard escape sequences output map (has to maintain matching order with
+ * escape_sequences). '\0' is included here as a NULL terminator for the string.
+ */
+static char escape_sequences_map[] = {
+       'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"', '?', '\0'
+};
+
+char* ast_escape(char *dest, const char *s, size_t num, const char *to_escape)
+{
+       char *p;
+
+       if (!dest || ast_strlen_zero(s)) {
+               return dest;
+       }
+
+       if (ast_strlen_zero(to_escape)) {
+               ast_copy_string(dest, s, num);
+               return dest;
+       }
+
+       for (p = dest; *s && num--; ++s, ++p) {
+               /* If in the list of characters to escape then escape it */
+               if (strchr(to_escape, *s)) {
+                       /*
+                        * See if the character to escape is part of the standard escape
+                        * sequences. If so we'll have to use its mapped counterpart
+                        * otherwise just use the current character.
+                        */
+                       char *c = strchr(escape_sequences, *s);
+                       *p++ = '\\';
+                       *p = c ? escape_sequences_map[c - escape_sequences] : *s;
+               } else {
+                       *p = *s;
+               }
+       }
+
+       *p = '\0';
+       return dest;
+}
+
+char* ast_escape_c(char *dest, const char *s, size_t num)
+{
+       /*
+        * Note - This is an optimized version of ast_escape. When looking only
+        * for escape_sequences a couple of checks used in the generic case can
+        * be left out thus making it slightly more efficient.
+        */
+       char *p;
+
+       if (!dest || ast_strlen_zero(s)) {
+               return dest;
+       }
+
+       for (p = dest; *s && num--; ++s, ++p) {
+               /*
+                * See if the character to escape is part of the standard escape
+                * sequences. If so use its mapped counterpart.
+                */
+               char *c = strchr(escape_sequences, *s);
+               if (c) {
+                       *p++ = '\\';
+                       *p = escape_sequences_map[c - escape_sequences];
+               } else {
+                       *p = *s;
+               }
+       }
+
+       *p = '\0';
+       return dest;
+}
+
+static char *escape_alloc(const char *s, size_t *size)
+{
+       if (!s || !(*size = strlen(s))) {
+               return NULL;
+       }
+
+       /*
+        * The result string needs to be twice the size of the given
+        * string just in case every character in it needs to be escaped.
+        */
+       *size = *size * 2 + 1;
+       return ast_calloc(sizeof(char), *size);
+}
+
+char *ast_escape_alloc(const char *s, const char *to_escape)
+{
+       size_t size = 0;
+       char *dest = escape_alloc(s, &size);
+       return ast_escape(dest, s, size, to_escape);
+}
+
+char *ast_escape_c_alloc(const char *s)
+{
+       size_t size = 0;
+       char *dest = escape_alloc(s, &size);
+       return ast_escape_c(dest, s, size);
+}
+
 int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
 {
        int result;
index d85b901..5848e91 100644 (file)
@@ -133,8 +133,6 @@ static char *complete_dialplan_remove_context(struct ast_cli_args *);
 
 static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct ast_context *con;
-
        switch (cmd) {
        case CLI_INIT:
                e->command = "dialplan remove context";
@@ -150,16 +148,11 @@ static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd
                return CLI_SHOWUSAGE;
        }
 
-       con = ast_context_find(a->argv[3]);
-
-       if (!con) {
-               ast_cli(a->fd, "There is no such context as '%s'\n",
-                        a->argv[3]);
-                return CLI_SUCCESS;
+       if (ast_context_destroy_by_name(a->argv[3], NULL)) {
+               ast_cli(a->fd, "There is no such context as '%s'\n", a->argv[3]);
+               return CLI_SUCCESS;
        } else {
-               ast_context_destroy(con, registrar);
-               ast_cli(a->fd, "Removing context '%s'\n",
-                       a->argv[3]);
+               ast_cli(a->fd, "Removed context '%s'\n", a->argv[3]);
                return CLI_SUCCESS;
        }
 }
index c4b071e..e0f618c 100644 (file)
@@ -42,10 +42,10 @@ ASTERISK_REGISTER_FILE()
 
 AST_TEST_DEFINE(test_gosub)
 {
+#define CONTEXT_NAME "tests_test_gosub_virtual_context"
        int res = AST_TEST_PASS, i;
        struct ast_channel *chan;
        struct ast_str *str;
-       struct ast_context *con;
        struct testplan {
                const char *app;
                const char *args;
@@ -119,14 +119,14 @@ AST_TEST_DEFINE(test_gosub)
        }
 
        /* Create our test dialplan */
-       if (!(con = ast_context_find_or_create(NULL, NULL, "tests_test_gosub_virtual_context", "test_gosub"))) {
+       if (!ast_context_find_or_create(NULL, NULL, CONTEXT_NAME, "test_gosub")) {
                ast_test_status_update(test, "Unable to create test dialplan context");
                ast_free(str);
                ast_channel_unref(chan);
                return AST_TEST_FAIL;
        }
 
-       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "test_gosub");
+       ast_add_extension(CONTEXT_NAME, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "test_gosub");
 
        for (i = 0; i < ARRAY_LEN(testplan); i++) {
                if (testplan[i].app == NULL) {
@@ -157,8 +157,8 @@ AST_TEST_DEFINE(test_gosub)
 
        ast_free(str);
        ast_channel_unref(chan);
-       ast_context_remove_extension2(con, "s", 1, NULL, 0);
-       ast_context_destroy(con, "test_gosub");
+       ast_context_remove_extension(CONTEXT_NAME, "s", 1, NULL);
+       ast_context_destroy(NULL, "test_gosub");
 
        return res;
 }
index 388baa3..8845167 100644 (file)
@@ -198,7 +198,6 @@ AST_TEST_DEFINE(pattern_match_test)
         */
        struct {
                const char * context_string;
-               struct ast_context *context;
        } contexts[] = {
                { TEST_PATTERN, },
                { TEST_PATTERN_INCLUDE, },
@@ -267,7 +266,7 @@ AST_TEST_DEFINE(pattern_match_test)
         */
 
        for (i = 0; i < ARRAY_LEN(contexts); ++i) {
-               if (!(contexts[i].context = ast_context_find_or_create(NULL, NULL, contexts[i].context_string, registrar))) {
+               if (!ast_context_find_or_create(NULL, NULL, contexts[i].context_string, registrar)) {
                        ast_test_status_update(test, "Failed to create context %s\n", contexts[i].context_string);
                        res = AST_TEST_FAIL;
                        goto cleanup;
@@ -319,11 +318,7 @@ AST_TEST_DEFINE(pattern_match_test)
        }
 
 cleanup:
-       for (i = 0; i < ARRAY_LEN(contexts); ++i) {
-               if (contexts[i].context) {
-                       ast_context_destroy(contexts[i].context, registrar);
-               }
-       }
+       ast_context_destroy(NULL, registrar);
 
        return res;
 }
index 31fb11a..d1ff2f0 100644 (file)
@@ -455,6 +455,38 @@ AST_TEST_DEFINE(escape_semicolons_test)
        return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(escape_test)
+{
+       char buf[128];
+
+#define TEST_ESCAPE(s, to_escape, expected) \
+       !strcmp(ast_escape(buf, s, sizeof(buf) / sizeof(char), to_escape), expected)
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "escape";
+               info->category = "/main/strings/";
+               info->summary = "Test ast_escape";
+               info->description = "Test escaping values in a string";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       ast_test_validate(test, TEST_ESCAPE("null escape", NULL, "null escape"));
+       ast_test_validate(test, TEST_ESCAPE("no matching escape", "Z", "no matching escape"));
+       ast_test_validate(test, TEST_ESCAPE("escape Z", "Z", "escape \\Z"));
+       ast_test_validate(test, TEST_ESCAPE("Z", "Z", "\\Z"));
+       ast_test_validate(test, TEST_ESCAPE(";;", ";;", "\\;\\;"));
+       ast_test_validate(test, TEST_ESCAPE("escape \n", "\n", "escape \\n"));
+       ast_test_validate(test, TEST_ESCAPE("escape \n again \n", "\n", "escape \\n again \\n"));
+
+       ast_test_validate(test, !strcmp(ast_escape_c(buf, "escape \a\b\f\n\r\t\v\\\'\"\?",
+                                                    sizeof(buf) / sizeof(char)),
+                                       "escape \\a\\b\\f\\n\\r\\t\\v\\\\\\\'\\\"\\?"));
+       return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
        AST_TEST_UNREGISTER(str_test);
@@ -462,6 +494,7 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(ends_with_test);
        AST_TEST_UNREGISTER(strsep_test);
        AST_TEST_UNREGISTER(escape_semicolons_test);
+       AST_TEST_UNREGISTER(escape_test);
        return 0;
 }
 
@@ -472,6 +505,7 @@ static int load_module(void)
        AST_TEST_REGISTER(ends_with_test);
        AST_TEST_REGISTER(strsep_test);
        AST_TEST_REGISTER(escape_semicolons_test);
+       AST_TEST_REGISTER(escape_test);
        return AST_MODULE_LOAD_SUCCESS;
 }