diff --git a/CHANGES b/CHANGES index 71166ef90f..4f5225ac1b 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,14 @@ === ============================================================================== +------------------------------------------------------------------------------ +--- Functionality changes from Asterisk 15 to Asterisk 16 -------------------- +------------------------------------------------------------------------------ + +chan_sip +------------------ + * New function SIP_HEADERS() enumerates all headers in the incoming INVITE. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14 to Asterisk 15 -------------------- ------------------------------------------------------------------------------ @@ -1539,7 +1547,7 @@ res_pjsip_endpoint_identifer_ip ------------------ * New CLI commands have been added: "pjsip show identif(y|ies)", which lists all configured PJSIP identify objects - + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 12 to Asterisk 13 -------------------- ------------------------------------------------------------------------------ diff --git a/channels/chan_sip.c b/channels/chan_sip.c index beaa3de0b1..941a1e96dc 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -380,6 +380,37 @@ Please observe that contents of the SDP (an attachment to the SIP request) can't be accessed with this function. + + SIP_HEADERS + + + + + Gets the list of SIP header names from an incoming INVITE message. + + + + If specified, only the headers matching the given prefix are returned. + + + + Returns a comma-separated list of header names (without values) from the + INVITE message that originated the current channel. Multiple headers with the + same name are included in the list only once. The returned list can be iterated + over using the functions POP() and SIP_HEADER(). + For example, ${SIP_HEADERS(Co)} might return + Contact,Content-Length,Content-Type. As a practical example, + you may use ${SIP_HEADERS(X-)} to enumerate optional extended + headers. + This function does not access headers from the incoming SIP REFER message; + see the documentation of the function SIP_HEADER for how to access them. + Please observe that contents of the SDP (an attachment to the + SIP request) can't be accessed with this function. + + + SIP_HEADER + POP + @@ -22995,6 +23026,7 @@ static int func_header_read(struct ast_channel *chan, const char *function, char { struct sip_pvt *p; const char *content = NULL; + char *mutable_data = ast_strdupa(data); AST_DECLARE_APP_ARGS(args, AST_APP_ARG(header); AST_APP_ARG(number); @@ -23018,7 +23050,7 @@ static int func_header_read(struct ast_channel *chan, const char *function, char return -1; } - AST_STANDARD_APP_ARGS(args, data); + AST_STANDARD_APP_ARGS(args, mutable_data); if (!args.number) { number = 1; } else { @@ -23054,6 +23086,91 @@ static struct ast_custom_function sip_header_function = { .read = func_header_read, }; +/*! \brief Read unique list of SIP headers (dialplan function) */ +static int func_headers_read2(struct ast_channel *chan, const char *function, char *data, struct ast_str **buf, ssize_t maxlen) +{ + int i; + struct sip_pvt *pvt; + char *mutable_data = ast_strdupa(data); + struct ast_str *token = ast_str_alloca(100); + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(pattern); + ); + + if (!chan) { + return -1; + } + + ast_channel_lock(chan); + + if (!IS_SIP_TECH(ast_channel_tech(chan))) { + ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); + ast_channel_unlock(chan); + return -1; + } + + pvt = ast_channel_tech_pvt(chan); + if (!pvt) { + ast_channel_unlock(chan); + return -1; + } + + AST_STANDARD_APP_ARGS(args, mutable_data); + if (!args.pattern || strcmp(args.pattern, "*") == 0) { + args.pattern = ""; + } + + for (i = 0; i < pvt->initreq.headers; i++) { + const char *header = REQ_OFFSET_TO_STR(&pvt->initreq, header[i]); + if (ast_begins_with(header, args.pattern)) { + int hdrlen = strcspn(header, " \t:,"); /* Comma will break our logic, and illegal per RFC. */ + const char *term = ast_skip_blanks(header + hdrlen); + if (hdrlen > 0 && *term == ':') { /* Header is malformed otherwise! */ + const char *s = NULL; + + /* Return short headers in full form always. */ + if (hdrlen == 1) { + char short_hdr[2] = { header[0], '\0' }; + s = find_full_alias(short_hdr, NULL); + } + if (s) { + /* Short header was found and expanded. */ + ast_str_set(&token, -1, "%s,", s); + } else { + /* Return the header as is, whether 1-character or not. */ + ast_str_set(&token, -1, "%.*s,", hdrlen, header); + } + + /* Has the same header been already added? */ + s = ast_str_buffer(*buf); + while ((s = strstr(s, ast_str_buffer(token))) != NULL) { + /* Found suffix, but is it the full token? */ + if (s == ast_str_buffer(*buf) || s[-1] == ',') + break; + /* Only suffix matched, go on with the search after the comma. */ + s += hdrlen + 1; + } + + /* s is null iff not broken from the loop, hence header not yet added. */ + if (s == NULL) { + ast_str_append(buf, maxlen, "%s", ast_str_buffer(token)); + } + } + } + } + + ast_str_truncate(*buf, -1); /* Trim the last comma. Safe if empty. */ + + ast_channel_unlock(chan); + return 0; +} + +static struct ast_custom_function sip_headers_function = { + .name = "SIP_HEADERS", + .read2 = func_headers_read2, +}; + + /*! \brief Dial plan function to check if domain is local */ static int func_check_sipdomain(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { @@ -35201,6 +35318,7 @@ static int load_module(void) /* Register dialplan functions */ ast_custom_function_register(&sip_header_function); + ast_custom_function_register(&sip_headers_function); ast_custom_function_register(&sippeer_function); ast_custom_function_register(&checksipdomain_function); @@ -35301,6 +35419,7 @@ static int unload_module(void) /* Unregister dial plan functions */ ast_custom_function_unregister(&sippeer_function); + ast_custom_function_unregister(&sip_headers_function); ast_custom_function_unregister(&sip_header_function); ast_custom_function_unregister(&checksipdomain_function);