diff --git a/doc/CHANGES-staging/pjsip_read_headers.txt b/doc/CHANGES-staging/pjsip_read_headers.txt new file mode 100644 index 0000000000..4dc641cdae --- /dev/null +++ b/doc/CHANGES-staging/pjsip_read_headers.txt @@ -0,0 +1,5 @@ +Subject: chan_pjsip + +Add function PJSIP_HEADERS() to get list of headers by pattern in the same way as SIP_HEADERS() do. + +Add ability to read header by pattern using PJSIP_HEADER(). diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c index 798a1cde6d..4b12079860 100644 --- a/res/res_pjsip_header_funcs.c +++ b/res/res_pjsip_header_funcs.c @@ -46,7 +46,10 @@ Returns instance number - of header name. + of header name. A * + may be appended to name to iterate over all + headers beginning with name. + Adds a new header name to this session. @@ -88,6 +91,9 @@ ; Set 'via2' to the value of the 2nd 'Via' header. exten => 1,1,Set(via2=${PJSIP_HEADER(read,Via,2)}) ; + ; Set 'xhdr' to the value of the 1sx X-header. + exten => 1,1,Set(xhdr=${PJSIP_HEADER(read,X-*,1)}) + ; ; Add an 'X-Myheader' header with the value of 'myvalue'. exten => 1,1,Set(PJSIP_HEADER(add,X-MyHeader)=myvalue) ; @@ -139,6 +145,29 @@ + + + Gets the list of SIP header names from an 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. Multiple headers with the same name are included in the list only once. + + For example, ${PJSIP_HEADERS(Co)} might return + Contact,Content-Length,Content-Type. As a practical example, + you may use ${PJSIP_HEADERS(X-)} to enumerate optional extended + headers. + + + PJSIP_HEADER + + + ***/ /*! \brief Linked list for accumulating headers */ @@ -240,6 +269,92 @@ static pjsip_hdr *find_header(struct hdr_list *list, const char *header_name, return hdr; } +/*! + * \internal + * \brief Implements PJSIP_HEADERS by searching for the requested header prefix. + * + * Retrieve the header_datastore. + * Search for the all matching headers. + * Validate the pjsip_hdr found. + * Parse pjsip_hdr into a name and copy to the buffer. + * Return the value. + */ +static int read_headers(void *obj) +{ + struct header_data *data = obj; + size_t len = strlen(data->header_name); + pjsip_hdr *hdr = NULL; + char *pj_hdr_string; + int pj_hdr_string_len; + char *p; + char *pos; + size_t plen, wlen = 0; + struct hdr_list_entry *le; + struct hdr_list *list; + + RAII_VAR(struct ast_datastore *, datastore, + ast_sip_session_get_datastore(data->channel->session, header_datastore.type), + ao2_cleanup); + + if (!datastore || !datastore->data) { + ast_debug(1, "There was no datastore from which to read headers.\n"); + return -1; + } + + list = datastore->data; + pj_hdr_string = ast_alloca(data->len); + AST_LIST_TRAVERSE(list, le, nextptr) { + if (pj_strnicmp2(&le->hdr->name, data->header_name, len) == 0) { + /* Found matched header, append to buf */ + hdr = le->hdr; + + pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len - 1); + if (pj_hdr_string_len == -1) { + ast_log(AST_LOG_ERROR, + "Not enought buffer space in pjsip_hdr_print_on\n"); + return -1; + } + pj_hdr_string[pj_hdr_string_len] = '\0'; + p = strchr(pj_hdr_string, ':'); + if (!p) { + ast_log(AST_LOG_WARNING, + "A malformed header was returned from pjsip_hdr_print_on\n"); + continue; + } + + pj_hdr_string[p - pj_hdr_string] = '\0'; + p = ast_strip(pj_hdr_string); + plen = strlen(p); + if (wlen + plen + 1 > data->len) { + ast_log(AST_LOG_ERROR, + "Buffer isn't big enough to hold header value. %zu > %zu\n", plen + 1, + data->len); + return -1; + } + pos = strstr(data->buf, p); + if (pos && pos[1] == ',') { + if (pos == data->buf) { + continue; + } else if (pos[-1] == ',') { + continue; + } + } + ast_copy_string(data->buf + wlen, p, data->len - wlen); + wlen += plen; + ast_copy_string(data->buf + wlen, ",", data->len - wlen); + wlen++; + } + } + + if (wlen == 0) { + ast_debug(1, "There was no header named %s.\n", data->header_name); + return -1; + } else { + data->buf[wlen-1] = '\0'; + } + return 0; +} + /*! * \internal @@ -254,11 +369,15 @@ static pjsip_hdr *find_header(struct hdr_list *list, const char *header_name, static int read_header(void *obj) { struct header_data *data = obj; + size_t len = strlen(data->header_name); pjsip_hdr *hdr = NULL; char *pj_hdr_string; - size_t pj_hdr_string_len; + int pj_hdr_string_len; char *p; size_t plen; + struct hdr_list_entry *le; + struct hdr_list *list; + int i = 1; RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(data->channel->session, header_datastore.type), ao2_cleanup); @@ -268,8 +387,20 @@ static int read_header(void *obj) return -1; } - hdr = find_header((struct hdr_list *) datastore->data, data->header_name, - data->header_number); + list = datastore->data; + AST_LIST_TRAVERSE(list, le, nextptr) { + if (data->header_name[len - 1] == '*') { + if (pj_strnicmp2(&le->hdr->name, data->header_name, len - 1) == 0 && i++ == data->header_number) { + hdr = le->hdr; + break; + } + } else { + if (pj_stricmp2(&le->hdr->name, data->header_name) == 0 && i++ == data->header_number) { + hdr = le->hdr; + break; + } + } + } if (!hdr) { ast_debug(1, "There was no header named %s.\n", data->header_name); @@ -277,7 +408,13 @@ static int read_header(void *obj) } pj_hdr_string = ast_alloca(data->len); - pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len); + pj_hdr_string_len = pjsip_hdr_print_on(hdr, pj_hdr_string, data->len - 1); + if (pj_hdr_string_len == -1) { + ast_log(AST_LOG_ERROR, + "Not enought buffer space in pjsip_hdr_print_on\n"); + return -1; + } + pj_hdr_string[pj_hdr_string_len] = '\0'; p = strchr(pj_hdr_string, ':'); @@ -434,13 +571,44 @@ static int remove_header(void *obj) return 0; } +/*! + * \brief Read list of unique SIP headers + */ +static int func_read_headers(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) +{ + struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL; + struct header_data header_data; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(header_pattern); + ); + AST_STANDARD_APP_ARGS(args, data); + + if (!chan || strncmp(ast_channel_name(chan), "PJSIP/", 6)) { + ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n"); + return -1; + } + + if (ast_strlen_zero(args.header_pattern)) { + ast_log(AST_LOG_ERROR, "This function requires a pattern.\n"); + return -1; + } + + header_data.channel = channel; + header_data.header_name = args.header_pattern; + header_data.header_value = NULL; + header_data.buf = buf; + header_data.len = len; + + return ast_sip_push_task_wait_serializer(channel->session->serializer, read_headers, &header_data); + +} + /*! * \brief Implements function 'read' callback. * * Valid actions are 'read' and 'remove'. */ -static int func_read_header(struct ast_channel *chan, const char *function, char *data, - char *buf, size_t len) +static int func_read_header(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) { struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL; struct header_data header_data; @@ -480,8 +648,7 @@ static int func_read_header(struct ast_channel *chan, const char *function, char header_data.len = len; if (!strcasecmp(args.action, "read")) { - return ast_sip_push_task_wait_serializer(channel->session->serializer, - read_header, &header_data); + return ast_sip_push_task_wait_serializer(channel->session->serializer, read_header, &header_data); } else if (!strcasecmp(args.action, "remove")) { return ast_sip_push_task_wait_serializer(channel->session->serializer, remove_header, &header_data); @@ -561,6 +728,11 @@ static struct ast_custom_function pjsip_header_function = { .write = func_write_header, }; +static struct ast_custom_function pjsip_headers_function = { + .name = "PJSIP_HEADERS", + .read = func_read_headers +}; + /*! * \internal * \brief Session supplement callback for outgoing INVITE requests @@ -604,6 +776,7 @@ static int load_module(void) { ast_sip_session_register_supplement(&header_funcs_supplement); ast_custom_function_register(&pjsip_header_function); + ast_custom_function_register(&pjsip_headers_function); return AST_MODULE_LOAD_SUCCESS; } @@ -611,6 +784,7 @@ static int load_module(void) static int unload_module(void) { ast_custom_function_unregister(&pjsip_header_function); + ast_custom_function_unregister(&pjsip_headers_function); ast_sip_session_unregister_supplement(&header_funcs_supplement); return 0; }