From bbae925fe602bbbc813db659c7814aacc8d6a542 Mon Sep 17 00:00:00 2001 From: Bogdan-Andrei Iancu Date: Fri, 15 Dec 2017 17:53:42 +0200 Subject: [PATCH] Added new set of functions for list headers. Functions for operating on headers having in body CSV lists of tokens (like the Supported, Require, Content-Dispsition headers) : * list_hdr_has_option(hdr_name,option) * list_hdr_add_option(hdr_name,option) * list_hdr_remove_option(hdr_name,option) --- lump_struct.h | 2 +- modules/sipmsgops/README | 106 +++++ modules/sipmsgops/doc/sipmsgops_admin.xml | 150 +++++++ modules/sipmsgops/list_hdr.c | 468 ++++++++++++++++++++++ modules/sipmsgops/list_hdr.h | 33 ++ modules/sipmsgops/sipmsgops.c | 73 +++- 6 files changed, 830 insertions(+), 2 deletions(-) create mode 100644 modules/sipmsgops/list_hdr.c create mode 100644 modules/sipmsgops/list_hdr.h diff --git a/lump_struct.h b/lump_struct.h index bc668537824..9ab848ad38a 100644 --- a/lump_struct.h +++ b/lump_struct.h @@ -72,7 +72,7 @@ enum lump_conditions { COND_FALSE, /*!< always false */ */ enum lump_flag { LUMPFLAG_NONE=0, LUMPFLAG_SHMEM=2 , LUMPFLAG_BRANCH=4, LUMPFLAG_COND_TRUE=8, - LUMPFLAG_CODEC=16}; + LUMPFLAG_CODEC=16, LUMP_FLAG_LISTHDR=32}; /*! \brief diff --git a/modules/sipmsgops/README b/modules/sipmsgops/README index bd9b3e9c5aa..372b8260d5e 100644 --- a/modules/sipmsgops/README +++ b/modules/sipmsgops/README @@ -68,6 +68,9 @@ Razvan Crainea 1.3.26. change_reply_status(code, reason) 1.3.27. stream_exists(regexp) 1.3.28. stream_delete(regexp) + 1.3.29. list_hdr_has_option(hdr_name,option) + 1.3.30. list_hdr_add_option(hdr_name,option) + 1.3.31. list_hdr_remove_option(hdr_name,option) 1.4. Known Limitations @@ -103,6 +106,9 @@ Razvan Crainea 1.28. change_reply_status usage 1.29. stream_exists usage 1.30. stream_delete usage + 1.31. list_hdr_has_option usage + 1.32. list_hdr_add_option usage + 1.33. list_hdr_remove_option usage Chapter 1. Admin Guide @@ -782,6 +788,106 @@ stream_exists("image"); stream_delete("video"); ... +1.3.29. list_hdr_has_option(hdr_name,option) + + Checks and returns true if the given option/token is listed in + the body of the given header. The header must have its body + formated as a CSV list of tokens/option (like the Supported, + Require, Content-Dispsition headers) body format + + Meaning of the parameters is as follows: + * hdr_name - the name of the header to be checked. Note that + all instances of that header will be checked (if the header + has multiple instances in the SIP message). Any kind of + header name is supported - RFC3261 standard, RFC extensions + or custom names. + * opt - the option/tolen to be searched for. + + The function returns true if the options was found listed in + one of the header instances. If no header was found, if the + option was not found or if there was a parsing or runtime + error, false will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + Example 1.31. list_hdr_has_option usage +... +# check if 100rel is advertised +if (list_hdr_has_option("Supported","100rel")) + xlog("100rel option founded\n"); +... + +1.3.30. list_hdr_add_option(hdr_name,option) + + Add a new option/token at the end of the list in the body of + the given header. The header must have its body formated as a + CSV list of tokens/option (like the Supported, Require, + Content-Dispsition headers) body format + + Multiple add / remove operations can be performed over the same + header. + + Meaning of the parameters is as follows: + * hdr_name - the name of the header where the option has to + be added. If multiple instances of that header are present + in the SIP message, the add will be performed on the first + instance. Any kind of header name is supported - RFC3261 + standard, RFC extensions or custom names. + * opt - the option/token to be added to the CSV list. Note + there is not verification for duplicated (if the newly + added option is not already present in the header). + + The function returns true if the options was successfully added + to the listed of the given header. If no header was found or if + there was a parsing or runtime error, false will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + Example 1.32. list_hdr_add_option usage +... +# add 100rel for advertising +if (!list_hdr_has_option("Supported","100rel")) + list_hdr_add_option("Supported","100rel") + +1.3.31. list_hdr_remove_option(hdr_name,option) + + Removes an option/token from the list inside the body of the + given header. The header must have its body formated as a CSV + list of tokens/option (like the Supported, Require, + Content-Dispsition headers) body format + + Multiple add / remove operations can be performed over the same + header. + + Meaning of the parameters is as follows: + * hdr_name - the name of the header where the option has to + be removed from. If the option is duplicated in the same + header, only the last one will be removed. If multiple + instances of that header are present in the SIP message, + the remove will be performed on all instance instance. Any + kind of header name is supported - RFC3261 standard, RFC + extensions or custom names. + * opt - the option/token to be removed from the CSV list. + Note that if this the only option in the header, the whole + header will be removed. + + The function returns true if the options was successfully + removed from at least one heaer instance. If no header was + found or if the token was not found or if there was a parsing + or runtime error, false will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + Example 1.33. list_hdr_remove_option usage +... +# add 100rel for advertising +if (list_hdr_has_option("Supported","100rel")) + list_hdr_remove_option("Supported","100rel"); +list_hdr_add_option("Supported","optionX"); + 1.4. Known Limitations Search functions are applied to the current message so diff --git a/modules/sipmsgops/doc/sipmsgops_admin.xml b/modules/sipmsgops/doc/sipmsgops_admin.xml index 7f9c22b684f..46670f69e69 100644 --- a/modules/sipmsgops/doc/sipmsgops_admin.xml +++ b/modules/sipmsgops/doc/sipmsgops_admin.xml @@ -1162,6 +1162,156 @@ stream_delete("video"); +
+ + <function moreinfo="none">list_hdr_has_option(hdr_name,option)</function> + + + Checks and returns true if the given option/token is listed in the + body of the given header. The header must have its body formated as a + CSV list of tokens/option (like the Supported, Require, + Content-Dispsition headers) + body format + + Meaning of the parameters is as follows: + + + hdr_name - the name of the header to be + checked. Note that all instances of that header will be checked (if the + header has multiple instances in the SIP message). Any kind of header + name is supported - RFC3261 standard, RFC extensions or custom names. + + + + opt - the option/tolen to be searched for. + + + + + + The function returns true if the options was found listed in one of the + header instances. If no header was found, if the option was not found + or if there was a parsing or runtime error, false will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + + <function>list_hdr_has_option</function> usage + +... +# check if 100rel is advertised +if (list_hdr_has_option("Supported","100rel")) + xlog("100rel option founded\n"); +... + + +
+ +
+ + <function moreinfo="none">list_hdr_add_option(hdr_name,option)</function> + + + Add a new option/token at the end of the list in the body of the given + header. The header must have its body formated as a + CSV list of tokens/option (like the Supported, Require, + Content-Dispsition headers) + body format + + + Multiple add / remove operations can be performed over the same header. + + Meaning of the parameters is as follows: + + + hdr_name - the name of the header where the + option has to be added. If multiple instances of that header are + present in the SIP message, the add will be performed on the first + instance. Any kind of header name is supported - RFC3261 standard, + RFC extensions or custom names. + + + + opt - the option/token to be added to the + CSV list. Note there is not verification for duplicated (if the newly + added option is not already present in the header). + + + + + + The function returns true if the options was successfully added to + the listed of the given header. If no header was found or if there was + a parsing or runtime error, false will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + + <function>list_hdr_add_option</function> usage + +... +# add 100rel for advertising +if (!list_hdr_has_option("Supported","100rel")) + list_hdr_add_option("Supported","100rel") + + +
+ +
+ + <function moreinfo="none">list_hdr_remove_option(hdr_name,option)</function> + + + Removes an option/token from the list inside the body of the given + header. The header must have its body formated as a + CSV list of tokens/option (like the Supported, Require, + Content-Dispsition headers) + body format + + + Multiple add / remove operations can be performed over the same header. + + Meaning of the parameters is as follows: + + + hdr_name - the name of the header where the + option has to be removed from. If the option is duplicated in the same + header, only the last one will be removed. If multiple instances of + that header are present in the SIP message, the remove will be + performed on all instance instance. Any kind of header name is + supported - RFC3261 standard, RFC extensions or custom names. + + + + opt - the option/token to be removed from + the CSV list. Note that if this the only option in the header, the + whole header will be removed. + + + + + + The function returns true if the options was successfully removed from + at least one heaer instance. If no header was found or if the + token was not found or if there was a parsing or runtime error, false + will be returned. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + FAILURE_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + + <function>list_hdr_remove_option</function> usage + +... +# add 100rel for advertising +if (list_hdr_has_option("Supported","100rel")) + list_hdr_remove_option("Supported","100rel"); +list_hdr_add_option("Supported","optionX"); + + +
diff --git a/modules/sipmsgops/list_hdr.c b/modules/sipmsgops/list_hdr.c new file mode 100644 index 00000000000..aadbd7af13f --- /dev/null +++ b/modules/sipmsgops/list_hdr.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2017 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "../../dprint.h" +#include "../../mod_fix.h" +#include "../../data_lump.h" +#include "../../mem/mem.h" +#include "../../parser/msg_parser.h" +#include "../../parser/parse_list_hdr.h" + + +static struct hdr_field * _get_first_header(struct sip_msg *msg, + gparam_t *gp_hdr) +{ + struct hdr_field *hdr; + str sval; + + /* be sure all SIP headers are parsed in the message */ + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR("failed to parse all the SIP headers\n"); + return NULL; + } + + if (gp_hdr->type == GPARAM_TYPE_INT) { + + /* header given by ID*/ + for (hdr=msg->headers; hdr; hdr=hdr->next) + if (gp_hdr->v.ival==hdr->type) + return hdr; + + } else { + + /* header given by string/variable */ + if (fixup_get_svalue(msg, gp_hdr, &sval) != 0) { + LM_ERR("failed to get the string value from variable\n"); + return NULL; + } + for (hdr=msg->headers; hdr; hdr=hdr->next) + if (hdr->type == HDR_OTHER_T && + hdr->name.len == sval.len && + strncasecmp(hdr->name.s, sval.s, hdr->name.len)==0) + return hdr; + + } + + /* header not found */ + return NULL; +} + + +static inline struct hdr_field *_get_next_hdr(struct sip_msg *msg, + struct hdr_field *hdr_start) +{ + struct hdr_field *hdr; + + if (hdr_start->type==HDR_OTHER_T) { + + /* unknown hdr type, so search by hdr name */ + for (hdr=hdr_start->next; hdr; hdr=hdr->next) + if (hdr->type == HDR_OTHER_T && + hdr->name.len == hdr_start->name.len && + strncasecmp(hdr->name.s, hdr_start->name.s, hdr->name.len)==0) + return hdr; + + } else { + + /* known hdr type, follow the "sibling" link */ + return hdr_start->sibling; + + } + + return NULL; +} + + +/* Checks if the option "val" is present in a CSV body-like header. + * It searches through all instances of the header. + * Input: + * - gp_hdr - the hdr name (ID or string/spec) + * - val - the option value (string/spec) + * Returns true on the first occurace of the option in the header instances. + */ +int list_hdr_has_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val) +{ + struct hdr_field *hdr; + struct list_hdr *lh, *lh_it; + + hdr = _get_first_header( msg, gp_hdr); + if (hdr==NULL) + /* header not found*/ + return -1; + + do { + + /* parse the body of the header */ + if (parse_list_hdr( hdr->body.s, hdr->body.len, &lh)!=0) { + LM_ERR("failed to parse body <%.*s> as CSV for hdr <%.*s>\n", + hdr->body.len, hdr->body.s, hdr->name.len, hdr->name.s); + return -1; + } + + /* search the value in the list */ + for( lh_it=lh ; lh_it ; lh_it=lh_it->next ) { + LM_DBG("testing option <%.*s>/%d against <%.*s>/%d\n", + lh_it->token.len, lh_it->token.s, lh_it->token.len, + val->len, val->s, val->len); + if (lh_it->token.len==val->len && + strncasecmp(lh_it->token.s, val->s ,val->len)==0 ) { + /* found */ + free_list_hdr(lh); + return 1; + } + } + + free_list_hdr(lh); + lh = NULL; + + /* not in this header, try the next hdr if any */ + hdr = _get_next_hdr(msg, hdr); + } while (hdr!=NULL); + + /* value not found in any header instaces */ + return -1; +} + + +/* Searchs in the existing list of lumps the lump chain that deletes the + * whole header and adds it again (as a whole) - this works a a full hdr + * header replacement, covering hdr name and CRLF. + */ +static inline struct lump *_get_lump_by_hdr(struct sip_msg *msg, + struct hdr_field *hdr) +{ + struct lump *l; + + for ( l=msg->add_rm; l ; l=l->next) + if (l->op==LUMP_DEL && l->u.offset==(hdr->name.s-msg->buf) + && (l->flags&LUMP_FLAG_LISTHDR) + && l->len==hdr->len && hdr->type==l->type + && l->after && l->after->op==LUMP_SKIP + && l->after->after && l->after->after->op==LUMP_ADD) { + /* we are on the right spot, just get the last ADD lump on after */ + l = l->after->after; + while(l->after) l = l->after ; + return l; + } + + return NULL; +} + + +/* Creates the necessary lumps in order to push the new header value. + * The whole change/update value process (for the header) is based on a + * anchoring lump tree that deletes the header and adds the future new values. + * A chain of "DEL-> SKIP->" is fix and the the new values will be chained + * on the "after" SKIP branch + * This works as a multi-time full hdr header replacement (covering hdr name + * and CRLF) + */ +static inline struct lump* _push_changes_into_lumps(struct sip_msg *msg, + struct lump *l, struct hdr_field *hdr, str *body) +{ + struct lump *l1; + + /* is this the first change ? */ + if (l==NULL) { + + /* first change, so build on the static anchoring lump tree (where + * all the future changes will be attached ) */ + l = del_lump(msg, hdr->name.s-msg->buf, hdr->len, hdr->type); + if (l==NULL) { + LM_ERR("failed to insert del lump\n"); + return NULL; + } + l->flags |= LUMP_FLAG_LISTHDR; + + l = insert_skip_lump_after( l ); + if (l==NULL) { + LM_ERR("failed to insert new skip lump after del\n"); + return NULL; + } + + } + + /* add the new value to the existing anchoring lump tree */ + + l1 = insert_new_lump_after( l, body->s, body->len, hdr->type); + if (l1==NULL) { + LM_ERR("failed to insert new lump after skip\n"); + return NULL; + } + + return l1; +} + + +/* Adds a new option "val" to a CSV body-like header. + * If the header does not exist, a new one will be added. + * There is no check if the option already exists. + * Input: + * - gp_hdr - the hdr name (ID or string/spec) + * - val - the option value (string/spec) + * Returns true upon succesfully insertion. + */ +int list_hdr_add_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val) +{ + struct hdr_field *hdr; + struct list_hdr *lh; + struct lump *l; + str body, old_hdr, new_hdr; + char *p; + + hdr = _get_first_header( msg, gp_hdr); + if (hdr==NULL) { + /* header not found*/ + // TODO - adding a completly new header ?? + return -1; + } + + /* search for the lump corresponding to this hdr */ + l = _get_lump_by_hdr( msg, hdr); + if (l) { + /* header already modified, used the lump data as value */ + old_hdr.s = l->u.value; + old_hdr.len = l->len; + } else { + /* first modification, use the original buffer as value */ + old_hdr.s = hdr->name.s; + old_hdr.len = hdr->len; + } + + if (old_hdr.len==0) { + + /* all original options were removed (current hdr status is to be + * entirly removed) -> rebuild it again */ + new_hdr.len = (hdr->len - hdr->body.len) + val->len; + new_hdr.s = (char*)pkg_malloc( new_hdr.len ); + if (new_hdr.s==NULL) { + LM_ERR("failed to allocate buffer for new body lump (needed %d)\n", + new_hdr.len); + return -1; + } + memcpy( new_hdr.s, hdr->name.s, hdr->body.s-hdr->name.s ); + p = new_hdr.s + (hdr->body.s-hdr->name.s); + memcpy( p, val->s, val->len); + p += val->len; + memcpy( p, hdr->body.s+hdr->body.len , + (hdr->name.s+hdr->len)-(hdr->body.s+hdr->body.len) ); + + LM_DBG("resulting new buffer is <%.*s>\n", new_hdr.len, new_hdr.s ); + + /* swap the buffers into the lump */ + pkg_free(l->u.value); + l->u.value = new_hdr.s; + l->len = new_hdr.len; + + } else { + + LM_DBG("adding new option <%.*s> to found buffer <%.*s>, hdr <%.*s>\n", + val->len, val->s, + old_hdr.len, old_hdr.s, + hdr->name.len, hdr->name.s); + /* get the body of the old body, based on hdr template : + * - body starts at the same offset in the hdr buffer + * - body ends with the same offset (to the end) in the hdr buffer */ + body.s = old_hdr.s + (hdr->body.s - hdr->name.s); + body.len = hdr->body.len + (old_hdr.len - hdr->len); + + /* parse the body, to be sure it is valid */ + if (parse_list_hdr( body.s, body.len, &lh)<0) { + LM_ERR("failed to parse body <%.*s> as CSV for hdr <%.*s>\n", + body.len, body.s, hdr->name.len, hdr->name.s); + return -1; + } + // TODO - check for duplicates ?? + + /* add the new value at the end of the old body */ + new_hdr.len = old_hdr.len + 1 /*','*/ + val->len; + new_hdr.s = (char*)pkg_malloc( new_hdr.len ); + if (new_hdr.s==NULL) { + LM_ERR("failed to allocate buffer for new body lump (needed %d)\n", + new_hdr.len); + return -1; + } + /* the lh list has the last option as first in the list, so we can + * simply add the new option (as index) right after the first option + * in list */ + memcpy( new_hdr.s, old_hdr.s, (lh->token.s+lh->token.len)-old_hdr.s ); + p = new_hdr.s + ((lh->token.s+lh->token.len)-old_hdr.s); + *(p++) = ','; + memcpy( p, val->s, val->len); + p += val->len; + memcpy( p, lh->token.s+lh->token.len, + (old_hdr.s+old_hdr.len)-(lh->token.s+lh->token.len) ); + free_list_hdr(lh); + + LM_DBG("resulting new buffer is <%.*s>\n", new_hdr.len, new_hdr.s ); + + /* add the new lumps for this change */ + if (_push_changes_into_lumps( msg, l, hdr, &new_hdr)==NULL) { + LM_ERR("failed to insert lump with new changes\n"); + pkg_free(new_hdr.s); + return -1; + } + } + + /* success */ + return 1; +} + + +int list_hdr_remove_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val) +{ + struct hdr_field *hdr; + struct list_hdr *lh, *lh_it, *lh_prev; + struct lump *l; + str body, old_hdr, new_hdr; + char *p; + int removed = 0; + + hdr = _get_first_header( msg, gp_hdr); + if (hdr==NULL) + /* header not found*/ + return -1; + + do { + + /* search for the lump corresponding to this hdr */ + l = _get_lump_by_hdr( msg, hdr); + if (l) { + /* header already modified, used the lump data as value */ + old_hdr.s = l->u.value; + old_hdr.len = l->len; + } else { + /* first modification, use the original buffer as value */ + old_hdr.s = hdr->name.s; + old_hdr.len = hdr->len; + } + + if (old_hdr.len==0) { + + /* all original options were removed (current hdr status is to be + * entirly removed) so, just skip it */ + + } else { + + LM_DBG("removing option <%.*s> from found buffer <%.*s>," + " hdr <%.*s>\n", val->len, val->s, + old_hdr.len, old_hdr.s, + hdr->name.len, hdr->name.s); + /* get the body of the old body, based on hdr template : + * - body starts at the same start offset in the hdr buffer + * - body ends with the same end offset in the hdr buffer */ + body.s = old_hdr.s + (hdr->body.s - hdr->name.s); + body.len = hdr->body.len + (old_hdr.len - hdr->len); + + /* parse the body, to be sure it is valid */ + if (parse_list_hdr( body.s, body.len, &lh)<0) { + LM_ERR("failed to parse body <%.*s> as CSV for hdr <%.*s>\n", + body.len, body.s, hdr->name.len, hdr->name.s); + return -1; + } + + /* search the value in the list */ + for(lh_it=lh,lh_prev=NULL ;lh_it; lh_prev=lh_it,lh_it=lh_it->next){ + LM_DBG("testing option <%.*s>/%d against <%.*s>/%d\n", + lh_it->token.len, lh_it->token.s, lh_it->token.len, + val->len, val->s, val->len); + if (lh_it->token.len==val->len && + strncasecmp(lh_it->token.s, val->s ,val->len)==0 ) { + + /* found, so remove it now */ + + if (lh_it==lh && lh->next==NULL) { + + /* only one element in the list -> remove the whole + * header now */ + new_hdr.s = NULL; + new_hdr.len = 0; + + } else { + + /* remove only the current option/token */ + new_hdr.len = old_hdr.len - lh_it->token.len - 1; + new_hdr.s = (char*)pkg_malloc( new_hdr.len ); + if (new_hdr.s==NULL) { + LM_ERR("failed to allocate buffer for new body " + "lump (needed %d)\n", new_hdr.len); + return -1; + } + memcpy( new_hdr.s, old_hdr.s, + lh_it->token.s-old_hdr.s ); + p = new_hdr.s + (lh_it->token.s-old_hdr.s); + /* skip to the next option (do not write the current + * option) - KEEP in MIND that the lh list is in the + * revers order, so we actually jump to prev, not to + * next in order to get the following option token */ + if (lh_prev) { + memcpy( p, lh_prev->token.s, + (old_hdr.s+old_hdr.len)-lh_prev->token.s); + p += (old_hdr.s+old_hdr.len)-lh_prev->token.s; + } else { + /* first in the linked list, last in the hdr -> + * shift back to remove the ',' of the last + * option/token */ + p -= lh->token.s - + (lh->next->token.s + lh->next->token.len); + memcpy( p, + lh->token.s+lh->token.len, + (old_hdr.s+old_hdr.len)- + (lh->token.s+lh->token.len)); + p += (old_hdr.s+old_hdr.len)- + (lh->token.s+lh->token.len); + } + + /* adjust the len according to what was actually + * writen (we may allocated a bit more) */ + new_hdr.len = p - new_hdr.s; + + } + + LM_DBG("resulting new buffer is <%.*s>\n", + new_hdr.len, new_hdr.s ); + + /* we have the new hdr value, push it as change in msg */ + if (_push_changes_into_lumps(msg,l,hdr,&new_hdr)==NULL) { + LM_ERR("failed to insert lump with new changes\n"); + pkg_free(new_hdr.s); + return -1; + } + + removed++; + /* only one removal per header for the moment */ + break; + } + } + + free_list_hdr(lh); + lh = NULL; + + } /* done with this hdr instance */ + + /* check the next hdr too, if any */ + hdr = _get_next_hdr(msg, hdr); + } while (hdr!=NULL); + + return removed?1:-1; +} + + + diff --git a/modules/sipmsgops/list_hdr.h b/modules/sipmsgops/list_hdr.h new file mode 100644 index 00000000000..495d52ea495 --- /dev/null +++ b/modules/sipmsgops/list_hdr.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _SIPMSGOPS_LIST_HDR_H +#define _SIPMSGOPS_LIST_HDR_H + +#include "../../dprint.h" +#include "../../mod_fix.h" + + +int list_hdr_has_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val); + +int list_hdr_add_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val); + +int list_hdr_remove_val(struct sip_msg *msg, gparam_t *gp_hdr, str *val); + +#endif diff --git a/modules/sipmsgops/sipmsgops.c b/modules/sipmsgops/sipmsgops.c index 76b2b5412df..b9cd231ed6d 100644 --- a/modules/sipmsgops/sipmsgops.c +++ b/modules/sipmsgops/sipmsgops.c @@ -82,7 +82,8 @@ #include "../../mod_fix.h" #include "../../trim.h" -#include"codecs.h" +#include "codecs.h" +#include "list_hdr.h" #include #include @@ -137,6 +138,11 @@ static int fixup_body_type(void** param, int param_no); static int fixup_privacy(void** param, int param_no); static int fixup_sip_validate(void** param, int param_no); +static int hl_opt_fixup(void** param, int param_no); +static int list_hdr_has_option(struct sip_msg*, char*, char *); +static int list_hdr_add_option(struct sip_msg*, char*, char *); +static int list_hdr_remove_option(struct sip_msg*, char*, char *); + static int change_reply_status_f(struct sip_msg*, char*, char *); static int change_reply_status_fixup(void** param, int param_no); @@ -263,6 +269,15 @@ static cmd_export_t cmds[]={ {"stream_delete", (cmd_function)stream_delete, 1, fixup_regexp_dynamic_null,0, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, + {"list_hdr_has_option", (cmd_function)list_hdr_has_option, 2, + hl_opt_fixup, NULL, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, + {"list_hdr_add_option", (cmd_function)list_hdr_add_option, 2, + hl_opt_fixup, NULL, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, + {"list_hdr_remove_option", (cmd_function)list_hdr_remove_option, 2, + hl_opt_fixup, NULL, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {0,0,0,0,0,0} }; @@ -1960,3 +1975,59 @@ static int change_reply_status_f(struct sip_msg* msg, char* str1, char* str2) return 1; } + +static int hl_opt_fixup(void** param, int param_no) +{ + if (param_no==1) + /* name of the header */ + return hname_fixup( param, param_no); + + if (param_no==2) + /* value for the option */ + return fixup_spve( param ); + + LM_BUG("too many parameters found\n"); + return -1; +} + +static int list_hdr_has_option(struct sip_msg *msg, char *s1, char *s2) +{ + str option; + + /* evaluate the value for the option */ + if (fixup_get_svalue( msg, (gparam_p)s2, &option)==-1) { + LM_ERR("failed to evaluate the value for the option\n"); + return -1; + } + + return list_hdr_has_val(msg, (gparam_p)s1, &option); +} + + +static int list_hdr_add_option(struct sip_msg *msg, char *s1, char *s2) +{ + str option; + + /* evaluate the value for the option */ + if (fixup_get_svalue( msg, (gparam_p)s2, &option)==-1) { + LM_ERR("failed to evaluate the value for the option\n"); + return -1; + } + + return list_hdr_add_val(msg, (gparam_p)s1, &option); +} + + +static int list_hdr_remove_option(struct sip_msg *msg, char *s1, char *s2) +{ + str option; + + /* evaluate the value for the option */ + if (fixup_get_svalue( msg, (gparam_p)s2, &option)==-1) { + LM_ERR("failed to evaluate the value for the option\n"); + return -1; + } + + return list_hdr_remove_val(msg, (gparam_p)s1, &option); +} +