diff options
-rw-r--r-- | .gitreview | 1 | ||||
-rw-r--r-- | addons/ooh323c/src/ooCalls.c | 2 | ||||
-rw-r--r-- | configs/samples/features.conf.sample | 4 | ||||
-rw-r--r-- | include/asterisk/cli.h | 26 | ||||
-rw-r--r-- | main/asterisk.c | 258 | ||||
-rw-r--r-- | main/cli.c | 78 | ||||
-rw-r--r-- | res/ari/resource_bridges.c | 13 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 49 | ||||
-rw-r--r-- | tests/test_substitution.c | 19 | ||||
-rw-r--r-- | third-party/pjproject/patches/0040-183_without_to_tag.patch | 17 |
10 files changed, 266 insertions, 201 deletions
diff --git a/.gitreview b/.gitreview index f9ef0502a..32bad967c 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,4 @@ host=gerrit.asterisk.org port=29418 project=asterisk.git +defaultbranch=15 diff --git a/addons/ooh323c/src/ooCalls.c b/addons/ooh323c/src/ooCalls.c index 26dc63eab..c920e6a23 100644 --- a/addons/ooh323c/src/ooCalls.c +++ b/addons/ooh323c/src/ooCalls.c @@ -29,6 +29,7 @@ #include "ooGkClient.h" #include "ooh323ep.h" #include "ooCalls.h" +#include "ooCmdChannel.h" /** Global endpoint structure */ extern OOH323EndPoint gH323ep; @@ -173,6 +174,7 @@ OOH323CallData* ooCreateCall(char* type, char*callToken) call->msdRetries = 0; call->pFastStartRes = NULL; call->usrData = NULL; + ooCreateCallCmdConnection(call); OOTRACEINFO3("Created a new call (%s, %s)\n", call->callType, call->callToken); /* Add new call to calllist */ diff --git a/configs/samples/features.conf.sample b/configs/samples/features.conf.sample index 223d69358..5806e4c04 100644 --- a/configs/samples/features.conf.sample +++ b/configs/samples/features.conf.sample @@ -72,8 +72,8 @@ ; means run the application on the opposite channel from the one that ; has activated the feature. ; ActivatedBy -> ActivatedBy is no longer honored. The feature is activated by which -; channel DYNAMIC_FEATURES includes the feature is on. Use predial -; to set different values of DYNAMIC_FEATURES on the channels. +; channel DYNAMIC_FEATURES includes the feature is on. Use a pre-dial +; handler to set different values for DYNAMIC_FEATURES on the channels. ; Historic values are: "caller", "callee", and "both". ; Application -> This is the application to execute. ; AppArguments -> These are the arguments to be passed into the application. If you need diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h index 3ed88eb61..c75fc295d 100644 --- a/include/asterisk/cli.h +++ b/include/asterisk/cli.h @@ -287,6 +287,9 @@ int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len); * Useful for readline, that's about it * \retval 0 on success * \retval -1 on failure + * + * Only call this function to proxy the CLI generator to + * another. */ char *ast_cli_generator(const char *, const char *, int); @@ -302,6 +305,9 @@ int ast_cli_generatornummatches(const char *, const char *); * Subsequent entries are all possible values, followed by a NULL. * All strings and the array itself are malloc'ed and must be freed * by the caller. + * + * \warning This function cannot be called recursively so it will always + * fail if called from a CLI_GENERATE callback. */ char **ast_cli_completion_matches(const char *, const char *); @@ -323,10 +329,30 @@ char **ast_cli_completion_matches(const char *, const char *); * by the caller. * * \note The vector is sorted and does not contain any duplicates. + * + * \warning This function cannot be called recursively so it will always + * fail if called from a CLI_GENERATE callback. */ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word); /*! + * \brief Add a result to a request for completion options. + * + * \param value A completion option text. + * + * \retval 0 Success + * \retval -1 Failure + * + * This is an alternative to returning individual values from CLI_GENERATE. Instead + * of repeatedly being asked for the next match and having to start over, you can + * call this function repeatedly from your own stateful loop. When all matches have + * been added you can return NULL from the CLI_GENERATE function. + * + * \note This function always eventually results in calling ast_free on \a value. + */ +int ast_cli_completion_add(char *value); + +/*! * \brief Command completion for the list of active channels. * * This can be called from a CLI command completion function that wants to diff --git a/main/asterisk.c b/main/asterisk.c index c4ed9093f..2c86d1ebc 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -3009,121 +3009,70 @@ static char *cli_prompt(EditLine *editline) return ast_str_buffer(prompt); } -static void destroy_match_list(char **match_list, int matches) +static struct ast_vector_string *ast_el_strtoarr(char *buf) { - if (match_list) { - int idx; + char *retstr; + struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec)); - for (idx = 0; idx < matches; ++idx) { - ast_free(match_list[idx]); - } - ast_free(match_list); + if (!vec) { + return NULL; } -} - -static char **ast_el_strtoarr(char *buf) -{ - char *retstr; - char **match_list = NULL; - char **new_list; - size_t match_list_len = 1; - int matches = 0; while ((retstr = strsep(&buf, " "))) { if (!strcmp(retstr, AST_CLI_COMPLETE_EOF)) { break; } - if (matches + 1 >= match_list_len) { - match_list_len <<= 1; - new_list = ast_realloc(match_list, match_list_len * sizeof(char *)); - if (!new_list) { - destroy_match_list(match_list, matches); - return NULL; - } - match_list = new_list; + + /* Older daemons sent duplicates. */ + if (AST_VECTOR_GET_CMP(vec, retstr, strcasecmp)) { + continue; } retstr = ast_strdup(retstr); - if (!retstr) { - destroy_match_list(match_list, matches); - return NULL; + /* Older daemons sent unsorted. */ + if (!retstr || AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) { + ast_free(retstr); + goto vector_cleanup; } - match_list[matches++] = retstr; } - if (!match_list) { - return NULL; + if (!AST_VECTOR_SIZE(vec)) { + goto vector_cleanup; } - if (matches >= match_list_len) { - new_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)); - if (!new_list) { - destroy_match_list(match_list, matches); - return NULL; - } - match_list = new_list; - } + return vec; - match_list[matches] = NULL; +vector_cleanup: + AST_VECTOR_CALLBACK_VOID(vec, ast_free); + AST_VECTOR_PTR_FREE(vec); - return match_list; -} - -static int ast_el_sort_compare(const void *i1, const void *i2) -{ - char *s1, *s2; - - s1 = ((char **)i1)[0]; - s2 = ((char **)i2)[0]; - - return strcasecmp(s1, s2); + return NULL; } -static int ast_cli_display_match_list(char **matches, int len, int max) +static void ast_cli_display_match_list(struct ast_vector_string *matches, int max) { - int i, idx, limit, count; - int screenwidth = 0; - int numoutput = 0, numoutputline = 0; - - screenwidth = ast_get_termcols(STDOUT_FILENO); - + int idx = 1; /* find out how many entries can be put on one line, with two spaces between strings */ - limit = screenwidth / (max + 2); - if (limit == 0) - limit = 1; - - /* how many lines of output */ - count = len / limit; - if (count * limit < len) - count++; + int limit = ast_get_termcols(STDOUT_FILENO) / (max + 2); - idx = 1; - - qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare); - - for (; count > 0; count--) { - numoutputline = 0; - for (i = 0; i < limit && matches[idx]; i++, idx++) { + if (limit == 0) { + limit = 1; + } - /* Don't print dupes */ - if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) { - i--; - ast_free(matches[idx]); - matches[idx] = NULL; - continue; - } + for (;;) { + int numoutputline; - numoutput++; + for (numoutputline = 0; numoutputline < limit && idx < AST_VECTOR_SIZE(matches); idx++) { numoutputline++; - fprintf(stdout, "%-*s ", max, matches[idx]); - ast_free(matches[idx]); - matches[idx] = NULL; + fprintf(stdout, "%-*s ", max, AST_VECTOR_GET(matches, idx)); + } + + if (!numoutputline) { + break; } - if (numoutputline > 0) - fprintf(stdout, "\n"); - } - return numoutput; + fprintf(stdout, "\n"); + } } @@ -3131,10 +3080,9 @@ static char *cli_complete(EditLine *editline, int ch) { int len = 0; char *ptr; - int nummatches = 0; - char **matches; + struct ast_vector_string *matches; int retval = CC_ERROR; - char buf[2048], savechr; + char savechr; int res; LineInfo *lf = (LineInfo *)el_line(editline); @@ -3155,96 +3103,90 @@ static char *cli_complete(EditLine *editline, int ch) len = lf->cursor - ptr; if (ast_opt_remote) { - snprintf(buf, sizeof(buf), "_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr); - fdsend(ast_consock, buf); - if ((res = read(ast_consock, buf, sizeof(buf) - 1)) < 0) { - return (char*)(CC_ERROR); +#define CMD_MATCHESARRAY "_COMMAND MATCHESARRAY \"%s\" \"%s\"" + char *mbuf; + char *new_mbuf; + int mlen = 0, maxmbuf = 2048; + + /* Start with a 2048 byte buffer */ + mbuf = ast_malloc(maxmbuf); + + /* This will run snprintf twice at most. */ + while (mbuf && (mlen = snprintf(mbuf, maxmbuf, CMD_MATCHESARRAY, lf->buffer, ptr)) > maxmbuf) { + /* Return value does not include space for NULL terminator. */ + maxmbuf = mlen + 1; + ast_free(mbuf); + mbuf = ast_malloc(maxmbuf); } - buf[res] = '\0'; - nummatches = atoi(buf); - - if (nummatches > 0) { - char *mbuf; - char *new_mbuf; - int mlen = 0, maxmbuf = 2048; - - /* Start with a 2048 byte buffer */ - if (!(mbuf = ast_malloc(maxmbuf))) { - *((char *) lf->cursor) = savechr; - return (char *)(CC_ERROR); - } - snprintf(buf, sizeof(buf), "_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); - fdsend(ast_consock, buf); - res = 0; - mbuf[0] = '\0'; - while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) { - if (mlen + 1024 > maxmbuf) { - /* Every step increment buffer 1024 bytes */ - maxmbuf += 1024; - new_mbuf = ast_realloc(mbuf, maxmbuf); - if (!new_mbuf) { - ast_free(mbuf); - *((char *) lf->cursor) = savechr; - return (char *)(CC_ERROR); - } - mbuf = new_mbuf; + + if (!mbuf) { + *((char *) lf->cursor) = savechr; + + return (char *)(CC_ERROR); + } + + fdsend(ast_consock, mbuf); + res = 0; + mlen = 0; + mbuf[0] = '\0'; + + while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) { + if (mlen + 1024 > maxmbuf) { + /* Expand buffer to the next 1024 byte increment. */ + maxmbuf = mlen + 1024; + new_mbuf = ast_realloc(mbuf, maxmbuf); + if (!new_mbuf) { + ast_free(mbuf); + *((char *) lf->cursor) = savechr; + + return (char *)(CC_ERROR); } - /* Only read 1024 bytes at a time */ - res = read(ast_consock, mbuf + mlen, 1024); - if (res > 0) - mlen += res; + mbuf = new_mbuf; } - mbuf[mlen] = '\0'; + /* Only read 1024 bytes at a time */ + res = read(ast_consock, mbuf + mlen, 1024); + if (res > 0) { + mlen += res; + } + } + mbuf[mlen] = '\0'; - matches = ast_el_strtoarr(mbuf); - ast_free(mbuf); - } else - matches = (char **) NULL; + matches = ast_el_strtoarr(mbuf); + ast_free(mbuf); } else { - char **p, *oldbuf=NULL; - nummatches = 0; - matches = ast_cli_completion_matches((char *)lf->buffer,ptr); - for (p = matches; p && *p; p++) { - if (!oldbuf || strcmp(*p,oldbuf)) - nummatches++; - oldbuf = *p; - } + matches = ast_cli_completion_vector((char *)lf->buffer, ptr); } if (matches) { int i; - int matches_num, maxlen, match_len; + int maxlen, match_len; + const char *best_match = AST_VECTOR_GET(matches, 0); - if (matches[0][0] != '\0') { + if (!ast_strlen_zero(best_match)) { el_deletestr(editline, (int) len); - el_insertstr(editline, matches[0]); + el_insertstr(editline, best_match); retval = CC_REFRESH; } - if (nummatches == 1) { + if (AST_VECTOR_SIZE(matches) == 2) { /* Found an exact match */ el_insertstr(editline, " "); retval = CC_REFRESH; } else { /* Must be more than one match */ - for (i = 1, maxlen = 0; matches[i]; i++) { - match_len = strlen(matches[i]); - if (match_len > maxlen) + for (i = 1, maxlen = 0; i < AST_VECTOR_SIZE(matches); i++) { + match_len = strlen(AST_VECTOR_GET(matches, i)); + if (match_len > maxlen) { maxlen = match_len; + } } - matches_num = i - 1; - if (matches_num >1) { - fprintf(stdout, "\n"); - ast_cli_display_match_list(matches, nummatches, maxlen); - retval = CC_REDISPLAY; - } else { - el_insertstr(editline," "); - retval = CC_REFRESH; - } + + fprintf(stdout, "\n"); + ast_cli_display_match_list(matches, maxlen); + retval = CC_REDISPLAY; } - for (i = 0; matches[i]; i++) - ast_free(matches[i]); - ast_free(matches); + AST_VECTOR_CALLBACK_VOID(matches, ast_free); + AST_VECTOR_PTR_FREE(matches); } *((char *) lf->cursor) = savechr; diff --git a/main/cli.c b/main/cli.c index eae14adc4..0f023b29f 100644 --- a/main/cli.c +++ b/main/cli.c @@ -2475,18 +2475,17 @@ static char *parse_args(const char *s, int *argc, const char *argv[], int max, i /*! \brief Return the number of unique matches for the generator */ int ast_cli_generatornummatches(const char *text, const char *word) { - int matches = 0, i = 0; - char *buf = NULL, *oldbuf = NULL; - - while ((buf = ast_cli_generator(text, word, i++))) { - if (!oldbuf || strcmp(buf,oldbuf)) - matches++; - if (oldbuf) - ast_free(oldbuf); - oldbuf = buf; - } - if (oldbuf) - ast_free(oldbuf); + int matches; + struct ast_vector_string *vec = ast_cli_completion_vector(text, word); + + if (!vec) { + return 0; + } + + matches = AST_VECTOR_SIZE(vec) - 1; + AST_VECTOR_CALLBACK_VOID(vec, ast_free); + AST_VECTOR_PTR_FREE(vec); + return matches; } @@ -2513,6 +2512,44 @@ char **ast_cli_completion_matches(const char *text, const char *word) return match_list; } +AST_THREADSTORAGE_RAW(completion_storage); + +/*! + * \internal + * \brief Add a value to the vector. + * + * \param vec Vector to add \a value to. Must be from threadstorage. + * \param value The value to add. + * + * \retval 0 Success + * \retval -1 Failure + */ +static int cli_completion_vector_add(struct ast_vector_string *vec, char *value) +{ + if (!value) { + return 0; + } + + if (!vec || AST_VECTOR_ADD_SORTED(vec, value, strcasecmp)) { + if (vec) { + ast_threadstorage_set_ptr(&completion_storage, NULL); + + AST_VECTOR_CALLBACK_VOID(vec, ast_free); + AST_VECTOR_FREE(vec); + } + ast_free(value); + + return -1; + } + + return 0; +} + +int ast_cli_completion_add(char *value) +{ + return cli_completion_vector_add(ast_threadstorage_get_ptr(&completion_storage), value); +} + struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word) { char *retstr, *prevstr; @@ -2520,13 +2557,23 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char size_t which = 0; struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec)); + /* Recursion into this function is a coding error. */ + ast_assert(!ast_threadstorage_get_ptr(&completion_storage)); + if (!vec) { return NULL; } + if (ast_threadstorage_set_ptr(&completion_storage, vec)) { + ast_log(LOG_ERROR, "Failed to initialize threadstorage for completion.\n"); + ast_free(vec); + + return NULL; + } + while ((retstr = ast_cli_generator(text, word, which)) != NULL) { - if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) { - ast_free(retstr); + if (cli_completion_vector_add(vec, retstr)) { + ast_threadstorage_set_ptr(&completion_storage, NULL); goto vector_cleanup; } @@ -2534,6 +2581,8 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char ++which; } + ast_threadstorage_set_ptr(&completion_storage, NULL); + if (!AST_VECTOR_SIZE(vec)) { AST_VECTOR_PTR_FREE(vec); @@ -2572,6 +2621,7 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal); if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) { ast_free(retstr); + goto vector_cleanup; } diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c index e08df7d8b..019cdea22 100644 --- a/res/ari/resource_bridges.c +++ b/res/ari/resource_bridges.c @@ -962,13 +962,12 @@ void ast_ari_bridges_create_with_id(struct ast_variable *headers, if (bridge) { /* update */ - if (!ast_strlen_zero(args->name)) { - if (!strcmp(args->name, bridge->name)) { - ast_ari_response_error( - response, 500, "Internal Error", - "Changing bridge name is not implemented"); - return; - } + if (!ast_strlen_zero(args->name) + && strcmp(args->name, bridge->name)) { + ast_ari_response_error( + response, 500, "Internal Error", + "Changing bridge name is not implemented"); + return; } if (!ast_strlen_zero(args->type)) { ast_ari_response_error( diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 839753e41..273061124 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -113,6 +113,20 @@ #define ZFONE_PROFILE_ID 0x505a #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4 +/*! + * \brief Calculate the min learning duration in ms. + * + * \details + * The min supported packet size represents 10 ms and we need to account + * for some jitter and fast clocks while learning. Some messed up devices + * have very bad jitter for a small packet sample size. Jitter can also + * be introduced by the network itself. + * + * So we'll allow packets to come in every 9ms on average for fast clocking + * with the last one coming in 5ms early for jitter. + */ +#define CALC_LEARNING_MIN_DURATION(count) (((count) - 1) * 9 - 5) +#define DEFAULT_LEARNING_MIN_DURATION CALC_LEARNING_MIN_DURATION(DEFAULT_LEARNING_MIN_SEQUENTIAL) #define SRTP_MASTER_KEY_LEN 16 #define SRTP_MASTER_SALT_LEN 14 @@ -151,6 +165,7 @@ static int nochecksums; #endif static int strictrtp = DEFAULT_STRICT_RTP; /*!< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */ static int learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; /*!< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */ +static int learning_min_duration = DEFAULT_LEARNING_MIN_DURATION; /*!< Lowest acceptable timeout between the first and the last sequential RTP frame. */ #ifdef HAVE_PJPROJECT static int icesupport = DEFAULT_ICESUPPORT; static struct sockaddr_in stunaddr; @@ -231,7 +246,7 @@ static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate); struct rtp_learning_info { struct ast_sockaddr proposed_address; /*!< Proposed remote address for strict RTP */ struct timeval start; /*!< The time learning mode was started */ - struct timeval received; /*!< The time of the last received packet */ + struct timeval received; /*!< The time of the first received packet */ int max_seq; /*!< The highest sequence number received */ int packets; /*!< The number of remaining packets before the source is accepted */ }; @@ -3065,25 +3080,28 @@ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq) */ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq) { - /* - * During the learning mode the minimum amount of media we'll accept is - * 10ms so give a reasonable 5ms buffer just in case we get it sporadically. - */ - if (!ast_tvzero(info->received) && ast_tvdiff_ms(ast_tvnow(), info->received) < 5) { - /* - * Reject a flood of packets as acceptable for learning. - * Reset the needed packets. - */ - info->packets = learning_min_sequential - 1; - } else if (seq == (uint16_t) (info->max_seq + 1)) { + if (seq == (uint16_t) (info->max_seq + 1)) { /* packet is in sequence */ info->packets--; } else { /* Sequence discontinuity; reset */ info->packets = learning_min_sequential - 1; + info->received = ast_tvnow(); + } + + /* + * Protect against packet floods by checking that we + * received the packet sequence in at least the minimum + * allowed time. + */ + if (ast_tvzero(info->received)) { + info->received = ast_tvnow(); + } else if (!info->packets && (ast_tvdiff_ms(ast_tvnow(), info->received) < learning_min_duration )) { + /* Packet flood; reset */ + info->packets = learning_min_sequential - 1; + info->received = ast_tvnow(); } info->max_seq = seq; - info->received = ast_tvnow(); return info->packets; } @@ -7153,6 +7171,7 @@ static int rtp_reload(int reload) dtmftimeout = DEFAULT_DTMF_TIMEOUT; strictrtp = DEFAULT_STRICT_RTP; learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; + learning_min_duration = DEFAULT_LEARNING_MIN_DURATION; /** This resource is not "reloaded" so much as unloaded and loaded again. * In the case of the TURN related variables, the memory referenced by a @@ -7217,10 +7236,12 @@ static int rtp_reload(int reload) strictrtp = ast_true(s); } if ((s = ast_variable_retrieve(cfg, "general", "probation"))) { - if ((sscanf(s, "%d", &learning_min_sequential) <= 0) || learning_min_sequential <= 0) { + if ((sscanf(s, "%d", &learning_min_sequential) != 1) || learning_min_sequential <= 1) { ast_log(LOG_WARNING, "Value for 'probation' could not be read, using default of '%d' instead\n", DEFAULT_LEARNING_MIN_SEQUENTIAL); + learning_min_sequential = DEFAULT_LEARNING_MIN_SEQUENTIAL; } + learning_min_duration = CALC_LEARNING_MIN_DURATION(learning_min_sequential); } #ifdef HAVE_PJPROJECT if ((s = ast_variable_retrieve(cfg, "general", "icesupport"))) { diff --git a/tests/test_substitution.c b/tests/test_substitution.c index 3a1dc1fba..8b9a164aa 100644 --- a/tests/test_substitution.c +++ b/tests/test_substitution.c @@ -43,6 +43,7 @@ #include "asterisk/stringfields.h" #include "asterisk/threadstorage.h" #include "asterisk/test.h" +#include "asterisk/vector.h" static enum ast_test_result_state test_chan_integer(struct ast_test *test, struct ast_channel *c, int *ifield, const char *expression) @@ -225,6 +226,7 @@ AST_TEST_DEFINE(test_substitution) struct ast_channel *c; int i; enum ast_test_result_state res = AST_TEST_PASS; + struct ast_vector_string *funcs; switch (cmd) { case TEST_INIT: @@ -302,11 +304,12 @@ AST_TEST_DEFINE(test_substitution) #undef TEST /* For testing dialplan functions */ - for (i = 0; ; i++) { - char *cmd = ast_cli_generator("core show function", "", i); - if (cmd == NULL) { - break; - } + funcs = ast_cli_completion_vector("core show function", ""); + + /* Skip "best match" element 0 */ + for (i = 1; funcs && i < AST_VECTOR_SIZE(funcs); i++) { + char *cmd = AST_VECTOR_GET(funcs, i); + if (strcmp(cmd, "CHANNEL") && strcmp(cmd, "CALLERID") && strncmp(cmd, "CURL", 4) && strncmp(cmd, "AES", 3) && strncmp(cmd, "BASE64", 6) && strcmp(cmd, "CDR") && strcmp(cmd, "ENV") && strcmp(cmd, "GLOBAL") && @@ -321,10 +324,14 @@ AST_TEST_DEFINE(test_substitution) } } } - ast_free(cmd); } + if (funcs) { + AST_VECTOR_CALLBACK_VOID(funcs, ast_free); + AST_VECTOR_PTR_FREE(funcs); + } ast_hangup(c); + return res; } diff --git a/third-party/pjproject/patches/0040-183_without_to_tag.patch b/third-party/pjproject/patches/0040-183_without_to_tag.patch new file mode 100644 index 000000000..e8692fe1b --- /dev/null +++ b/third-party/pjproject/patches/0040-183_without_to_tag.patch @@ -0,0 +1,17 @@ +diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c +index c9686a0..fc52a63 100644 +--- a/pjsip/src/pjsip-ua/sip_inv.c ++++ b/pjsip/src/pjsip-ua/sip_inv.c +@@ -4156,9 +4156,10 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) + status = pjsip_inv_send_msg(inv, cancel); + } + +- if (dlg->remote.info->tag.slen) { ++ if (tsx->status_code != 100) { + +- inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); ++ if (dlg->remote.info->tag.slen) ++ inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); + + inv_check_sdp_in_incoming_msg(inv, tsx, + e->body.tsx_state.src.rdata); |