From 6aff9a12b1c1a4d94442614f9659370bf1071e58 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Mon, 25 Nov 2019 14:09:39 +0100 Subject: [PATCH] TT#64607 rework pv_headers module * use pvh_ prefix not to collide with pv * use generic PKG_MEM_ERROR, SHM_MEM_ERROR * split code into more small files * create proper source doc * clang-format with kamailio's settings Change-Id: I6baaa658ae56763bca48f298e37410966414f270 --- .../sipwise/add_pv_headers_module.patch | 5215 ++++++++++------- 1 file changed, 2990 insertions(+), 2225 deletions(-) diff --git a/debian/patches/sipwise/add_pv_headers_module.patch b/debian/patches/sipwise/add_pv_headers_module.patch index 936fb96e1..48712abcd 100644 --- a/debian/patches/sipwise/add_pv_headers_module.patch +++ b/debian/patches/sipwise/add_pv_headers_module.patch @@ -2,18 +2,43 @@ From: Sipwise Development Team Date: Mon, 18 Nov 2019 10:36:39 +0100 Subject: add_pv_headers_module -Change-Id: I9f7efd8872a815b86ced8a06f663a10c5f327dcf --- - src/Makefile.groups | 2 +- - src/modules/pv_headers/Makefile | 12 + - src/modules/pv_headers/README | 488 +++++++++ - src/modules/pv_headers/pv_headers.c | 2069 +++++++++++++++++++++++++++++++++++ - src/modules/pv_headers/pv_headers.h | 30 + - 5 files changed, 2600 insertions(+), 1 deletion(-) + src/Makefile.groups | 2 +- + src/modules/pv_headers/Makefile | 12 + + src/modules/pv_headers/README | 332 +++++++ + src/modules/pv_headers/doc/Makefile | 4 + + src/modules/pv_headers/doc/functions.xml | 131 +++ + src/modules/pv_headers/doc/params.xml | 186 ++++ + src/modules/pv_headers/doc/pv_headers.xml | 39 + + src/modules/pv_headers/doc/pv_headers_admin.xml | 116 +++ + src/modules/pv_headers/pv_headers.c | 654 ++++++++++++++ + src/modules/pv_headers/pv_headers.h | 42 + + src/modules/pv_headers/pvh_hash.c | 132 +++ + src/modules/pv_headers/pvh_hash.h | 39 + + src/modules/pv_headers/pvh_hdr.c | 235 +++++ + src/modules/pv_headers/pvh_hdr.h | 39 + + src/modules/pv_headers/pvh_str.c | 144 ++++ + src/modules/pv_headers/pvh_str.h | 38 + + src/modules/pv_headers/pvh_xavp.c | 1050 +++++++++++++++++++++++ + src/modules/pv_headers/pvh_xavp.h | 64 ++ + 18 files changed, 3258 insertions(+), 1 deletion(-) create mode 100644 src/modules/pv_headers/Makefile create mode 100644 src/modules/pv_headers/README + create mode 100644 src/modules/pv_headers/doc/Makefile + create mode 100644 src/modules/pv_headers/doc/functions.xml + create mode 100644 src/modules/pv_headers/doc/params.xml + create mode 100644 src/modules/pv_headers/doc/pv_headers.xml + create mode 100644 src/modules/pv_headers/doc/pv_headers_admin.xml create mode 100644 src/modules/pv_headers/pv_headers.c create mode 100644 src/modules/pv_headers/pv_headers.h + create mode 100644 src/modules/pv_headers/pvh_hash.c + create mode 100644 src/modules/pv_headers/pvh_hash.h + create mode 100644 src/modules/pv_headers/pvh_hdr.c + create mode 100644 src/modules/pv_headers/pvh_hdr.h + create mode 100644 src/modules/pv_headers/pvh_str.c + create mode 100644 src/modules/pv_headers/pvh_str.h + create mode 100644 src/modules/pv_headers/pvh_xavp.c + create mode 100644 src/modules/pv_headers/pvh_xavp.h diff --git a/src/Makefile.groups b/src/Makefile.groups index dba8073..33cbf0a 100644 @@ -48,508 +73,859 @@ index 0000000..42b80dd +include ../../Makefile.modules diff --git a/src/modules/pv_headers/README b/src/modules/pv_headers/README new file mode 100644 -index 0000000..30bd1da +index 0000000..b99cc42 --- /dev/null +++ b/src/modules/pv_headers/README -@@ -0,0 +1,488 @@ -+The pv_headers Module +@@ -0,0 +1,332 @@ ++pv_headers Module + +Kirill Solomko + + Sipwise GmbH ++ + -+ Copyright © 2018 Sipwise GmbH -+ __________________________________________________________________ ++Edited by + -+ Table of Contents ++Victor Seva + -+ 1. Admin Guide ++ Sipwise GmbH ++ + -+ 1. Dependencies ++ Copyright © 2018 Sipwise GmbH ++ __________________________________________________________________ + -+ 1.1. Kamailio modules ++ Table of Contents + -+ 2. Parameters ++ 1. Admin Guide + -+ 2.1. xavp_name (string) ++ 1. Overview ++ 2. Dependencies + -+ 3. Functions ++ 2.1. Kamailio Modules ++ 2.2. External Libraries or Applications + -+ 3.1. pv_collect_headers() -+ 3.2. pv_apply_headers() -+ 3.3. pv_check_header(hname) -+ 3.4. pv_append_header(hname, hvalue) -+ 3.5. pv_modify_header(hname, hvalue) -+ 3.6. pv_remove_header(hname) ++ 3. Parameters + -+ 4. Pseudovariables ++ 3.1. xavp_name (string) ++ 3.2. header_value_size (int) ++ 3.3. header_collect_flag (int) ++ 3.4. header_apply_flag (int) ++ 3.5. skip_headers (string) ++ 3.6. split_headers (string) ++ 3.7. auto_msg (int) + -+ 4.1. $x_hdr -+ 4.2. $x_fu, $x_tu -+ 4.3. $x_fU, $x_tU -+ 4.4. $x_fd, $x_td -+ 4.5. $x_fn, $x_tn -+ 4.6. $x_ft, $x_tt -+ 4.7. $x_rs, $x_rr ++ 4. Functions + -+1. Design Goals ++ 4.1. pvh_collect_headers() ++ 4.2. pvh_apply_headers() ++ 4.3. pvh_reset_headers() ++ 4.4. pvh_check_header(hname) ++ 4.5. pvh_append_header(hname, hvalue) ++ 4.6. pvh_modify_header(hname, [idx], hvalue) ++ 4.7. pvh_remove_header(hname, [idx]) + -+ The main goal of the module is to offload the intermediate header processing -+ into the XAVP dynamic container as well as provide with high level methods -+ and pseudovariables to simplify SIP message header modifications. ++ 5. Exported Variables + -+2. Overview of Operation ++ List of Examples + -+ This module enables storing all headers in XAVP to freely modify -+ them in the kamailio logic and only apply them once when it's time -+ for the packet to be routed outside. ++ 1.1. Set xavp_name parameter ++ 1.2. Set header_value_size parameter ++ 1.3. Set header_collect_flag parameter ++ 1.4. Set header_apply_flag parameter ++ 1.5. Set skip_headers parameter ++ 1.6. Set split_headers parameter ++ 1.7. Set auto_msg parameter + +Chapter 1. Admin Guide + -+ Table of Contents -+ -+ 1. Dependencies -+ -+ 1.1. Kamailio modules -+ -+ 2. Parameters -+ -+ 2.1. xavp_name (string) -+ 2.2. skip_headers (string) -+ 2.3. split_headers (string) -+ 2.4. auto_msg (int) -+ -+ 3. Functions -+ -+ 3.1. pv_collect_headers() -+ 3.2. pv_apply_headers() -+ 3.3. pv_reset_headers() -+ 3.4. pv_check_header(hname) -+ 3.5. pv_append_header(hname, hvalue) -+ 3.6. pv_modify_header(hname, hvalue) -+ 3.7. pv_modify_header(hname, idx, hvalue) -+ 3.8. pv_remove_header(hname) -+ 3.9. pv_remove_header(hname, idx) -+ -+ 4. Pseudovariables -+ -+ 4.1. $x_hdr -+ 4.2. $x_fu, $x_tu -+ 4.3. $x_fU, $x_tU -+ 4.4. $x_fd, $x_td -+ 4.5. $x_fn, $x_tn -+ 4.6. $x_ft, $x_tt -+ 4.7. $x_rs, $x_rr -+ -+1. Dependencies -+ -+ 1.1. Kamailio modules -+ -+1.1. Kamailio modules -+ -+ The following modules are optional to be loaded and enable additional -+ functionality: -+ * uac -+ From/To headers are applied using uac_replace_from/uac_replace_to -+ -+2. Parameters -+ -+ 2.1. xavp_name (string) -+ -+2.1. xavp_name (string) -+ -+ Name of the XAVP there the collected headers are stored. -+ -+ Default: headers -+ -+ Example 2.1. Set xavp_name parameter -+modparam("pv_headers", "xavp_name", "headers") -+ -+ Result: -+ $xavp(headers[0]=>From) -+ $xavp(headers[0]=>To) -+ $xavp(headers[0]=>Call-ID) -+ .... -+ -+ 2.2. skip_headers (string) -+ -+2.2. skip_headers (string) -+ -+ A comma separated headers list that must be excluded from processing -+ (they are skipped when pv_apply_headers() changes the sip message headers). -+ If the parameter is not set then the "Default" list is used. -+ If the parameter is set to an empty string then all the sip message headers are processed. -+ -+ Default: Record-Route,Via,Route,Content-Length,Max-Forwards -+ -+ 2.3. split_headers (string) -+ -+2.3. split_headers (string) -+ -+ A comma separated headers list that must be split into multi headers if -+ their value is a comma separated list. -+ If the parameter is not set then the "Default" is used. -+ If the parameter is set to an empty string then no headers are split. -+ -+ Default: None -+ -+ Example 2.3. Set split_headers parameters -+modparam("pv_headers", "split_headers", "Diversion") -+ -+ Result: -+ Received Diversion header: -+ Diversion: ,, -+ After split: -+ Diversion: -+ Diversion: -+ Diversion: -+ Becomes handy if used together with pv_modify_header() or pv_remove_header() -+ to change or remove value 2 for instance. -+ -+ 2.4. auto_msg (int) -+ -+2.4. auto_msg (int) -+ -+ This parameter defines wether the headers are automatically collected for incoming -+ messages, as well as automatically applied for forwarded messages. It is enabled -+ by default and requires the 'tm' module to be loaded, otherwise the mode is disabled -+ and manual invocation of pv_collect_headers()/pv_apply_headers() is required. -+ -+ Default: 1 -+ -+3. Functions -+ -+ 3.1. pv_collect_headers() -+ 3.2. pv_apply_headers() -+ 3.3. pv_reset_headers() -+ 3.4. pv_check_header(hname) -+ 3.5. pv_append_header(hname, hvalue) -+ 3.6. pv_modify_header(hname, hvalue) -+ 3.7. pv_modify_header(hname, idx, hvalue) -+ 3.8. pv_remove_header(hname) -+ 3.9. pv_remove_header(hname, idx) -+ -+3.1. pv_collect_headers() -+ -+ This function collects all headers from the message into the XAVP. -+ It should be used preferably just when the sip message is reveived by kamailio. -+ -+ Returns: -+ 1 - on success -+ -1 - if there were errors -+ -+3.2. pv_apply_headers() -+ -+ This function applies the current XAVP headers state to the real headers. -+ and should be called only once per branch when the message is about to leave kamailio. -+ -+ The following rules apply: -+ -+ - all headers in the XAVP except for ones provided in the "skip_headers" -+ parameter and From/To are recreated in the sip message. -+ - From/To headers are processed by the uac module if it is loaded. -+ - From/To headers are not changed in the reply messages. -+ - headers with NULL value are removed if exist in the sip message. -+ - the initial order of the sip headers is preserved. -+ -+ Usage: -+ -+ if (pv_apply_headers()) -+ { -+ "success" -+ } -+ else -+ { -+ "errors" -+ } -+ -+3.3. pv_reset_headers() -+ -+ This function resets the current XAVP headers list and enables -+ pv_collect_headers() and pv_apply_headers() to be called again in the same branch. -+ -+ Usage: -+ -+ if (pv_reset_headers()) -+ { -+ "success" -+ } -+ else -+ { -+ "errors" -+ } -+ -+ -+3.4. pv_check_header(hname) -+ -+ This function checks if the header already exists in the XAVP. -+ It can be freely called from anywere, but only after pv_collect_headers(). -+ -+ Usage: -+ -+ if (pv_check_header(hname)) -+ { -+ "exists" -+ } -+ else -+ { -+ "does not exist" -+ } -+ -+ -+3.5. pv_append_header(hname, hvalue) -+ -+ This function appends a new header into the XAVP. -+ It can be freely called from anywere, but only after pv_collect_headers(). -+ -+ Please note that subsequent "pv_append_header" calls will result in multiple -+ headers. -+ -+ If the provided "hvalue" is $null then the header is added into the XAVP -+ but it is not going to be added into the message. -+ -+ Usage: -+ -+ if (pv_append_header(hname, hvalue)) -+ { -+ "appended" -+ } -+ else -+ { -+ "errors" -+ } -+ -+3.6. pv_modify_header(hname, hvalue) -+ -+ This function modifies an existing header in the XAVP. -+ It can be freely called from anywere, but only after pv_collect_headers(). -+ -+ Please note that if the header does not exist it will be explicitly appended. -+ If there are multiple headers with the same name only the first one will be affected. -+ -+ If the provided header value is $null then the header is modified in the XAVP -+ then it is removed from the sip message when pv_apply_headers() is called. -+ -+ Usage: -+ -+ if (pv_modify_header(hname, hvalue)) -+ { -+ "modified" -+ } -+ else -+ { -+ "errors" -+ } -+ -+3.7. pv_modify_header(hname, idx, hvalue) ++ Table of Contents + -+ This function works similar to pv_modify_header(hname, hvalue) but -+ should be used when there are multiple headers with the same name one of them -+ to be modified. Index order is top to bottom. ++ 1. Overview ++ 2. Dependencies + -+ Usage: ++ 2.1. Kamailio Modules ++ 2.2. External Libraries or Applications + -+ if (pv_modify_header(hname, idx, hvalue)) -+ { -+ "modified" -+ } -+ else -+ { -+ "errors" -+ } ++ 3. Parameters + -+3.8. pv_remove_header(hname) ++ 3.1. xavp_name (string) ++ 3.2. header_value_size (int) ++ 3.3. header_collect_flag (int) ++ 3.4. header_apply_flag (int) ++ 3.5. skip_headers (string) ++ 3.6. split_headers (string) ++ 3.7. auto_msg (int) + -+ This function removes an existing header from the XAVP. -+ It can be freely called from anywere, but only after pv_collect_headers(). ++ 4. Functions + -+ If there are multiple headers with the same name all of them are removed. ++ 4.1. pvh_collect_headers() ++ 4.2. pvh_apply_headers() ++ 4.3. pvh_reset_headers() ++ 4.4. pvh_check_header(hname) ++ 4.5. pvh_append_header(hname, hvalue) ++ 4.6. pvh_modify_header(hname, [idx], hvalue) ++ 4.7. pvh_remove_header(hname, [idx]) + -+ It returns -1 if the header does not exist. ++ 5. Exported Variables + -+ Usage: ++1. Overview + -+ if (pv_remove_header(hname, hvalue)) -+ { -+ "removed" -+ } -+ else -+ { -+ "does not exist or errors" -+ } ++ The main goal of the module is to offload the intermediate header ++ processing into the XAVP dynamic container as well as provide with high ++ level methods and pseudovariables to simplify SIP message header ++ modifications. + -+3.9. pv_remove_header(hname, idx, hvalue) ++2. Dependencies + -+ This function works similar to pv_remove_header(hname, hvalue) but -+ should be used when there are multiple headers with the same name one of them -+ to be removed. Index order is top to bottom. ++ 2.1. Kamailio Modules ++ 2.2. External Libraries or Applications + -+ Usage: ++2.1. Kamailio Modules + -+ if (pv_remove_header(hname, idx, hvalue)) -+ { -+ "removed" -+ } -+ else -+ { -+ "does not exist or errors" -+ } ++ The following modules must be loaded before this module: ++ * uac. ++ * tm. ++ Needed only if "auto_msg" parameter is set to 1. + -+4. Pseudovariables ++2.2. External Libraries or Applications + -+ 4.1. $x_hdr -+ 4.2. $x_fu, $x_tu -+ 4.3. $x_fU, $x_tU -+ 4.4. $x_fd, $x_td -+ 4.5. $x_fn, $x_tn -+ 4.6. $x_ft, $x_tt -+ 4.7. $x_rs, $x_rr ++ The following libraries or applications must be installed before ++ running Kamailio with this module loaded: ++ * None. + -+4.1. $x_hdr ++3. Parameters + -+ This pseudovariable is used to append/modify/remove headers by their name and can be used instead of -+ the pv_append_header(), pv_modify_header(), pv_remove_header() functions. ++ 3.1. xavp_name (string) ++ 3.2. header_value_size (int) ++ 3.3. header_collect_flag (int) ++ 3.4. header_apply_flag (int) ++ 3.5. skip_headers (string) ++ 3.6. split_headers (string) ++ 3.7. auto_msg (int) + -+ Usage: ++3.1. xavp_name (string) + -+ * append header "X-Header" with value "example". NOTE: It always appends a header, even there is already one with the same name ++ Name of the XAVP there the collected headers are stored. + -+ $x_hdr(X-Header) = "example"; ++ Default value is "headers". + -+ * modify header "X-Header" with index 0. Returns an error if there is no such index ++ Example 1.1. Set xavp_name parameter ++... ++modparam("pv_headers", "xavp_name", "pvh") ++... + -+ $(x_hdr(X-Header)[0]) = "example"; ++ Result: $xavp(headers[0]=>From) $xavp(headers[0]=>To) ++ $xavp(headers[0]=>Call-ID) + -+ * remove all occurrences of header "X-Header" and append one with value "example" ++3.2. header_value_size (int) + -+ $(x_hdr(X-Header)[*]) = "example"; ++ ??????. + -+ * remove header "X-Header" with index 2 (if there are multiple headers). Returns an error if there is no such index ++ Default value is 1024. + -+ $(x_hdr(X-Header)[2]) = $null; ++ Example 1.2. Set header_value_size parameter ++... ++modparam("pv_headers", "header_value_size", 512) ++... + -+ * remove all occurrences of the header. Does not produce an error if there is no such header ++3.3. header_collect_flag (int) + -+ $(x_hdr(X-Header)[*]) = $null; ++ ??????. + -+ * retrieve a value of header "X-Header" with index 0, otherwise $null ++ Default value is 27. + -+ $var(test) = $x_hdr(X-Header); ++ Example 1.3. Set header_collect_flag parameter ++... ++modparam("pv_headers", "header_collect_flag", 37) ++... + -+ * retrieve a value of header "X-Header" with index 2, otherwise $null ++3.4. header_apply_flag (int) + -+ $var(test) = $(x_hdr(X-Header)[2]); ++ ??????. + ++ Default value is 28. + -+4.2. $x_fu, $x_tu ++ Example 1.4. Set header_apply_flag parameter ++... ++modparam("pv_headers", "header_apply_flag", 38) ++... + -+ These pseudovariables are used to modify/retreive the "From" and "To" headers. ++3.5. skip_headers (string) + -+ Usage: ++ A comma separated headers list that must be excluded from processing ++ (they are skipped when pvh_apply_headers() changes the SIP message ++ headers). + -+ * modify the header ++ If the parameter is set to an empty string then all the SIP message ++ headers are processed. + -+ $x_fu = "User1 <440001@example.local>"; ++ Default value is ++ "Record-Route,Via,Route,Content-Length,Max-Forwards,CSeq". + -+ * retrieve a value of the header ++ Example 1.5. Set skip_headers parameter ++... ++modparam("pv_headers", "skip_headers", "Record-Route,Via,Route") ++... + -+ $var(test) = $x_fu; ++3.6. split_headers (string) + -+ * $x_tu usage is the same ++ A comma separated headers list that must be split into multi headers if ++ their value is a comma separated list. + -+4.3. $x_fU, $x_tU ++ If the parameter is set to an empty string then no headers are split. + -+ These pseudovariables are used to modify/retreive the username part of the "From" and "To" headers. ++ Default value is "". + -+ Usage: ++ Example 1.6. Set split_headers parameter ++... ++modparam("pv_headers", "split_headers", "Diversion") ++... + -+ * modify the username part ++ Result: Received Diversion header: Diversion: ++ ,, After split: ++ Diversion: Diversion: Diversion: ++ + -+ $x_fU = "440001"; ++ Becomes handy if used together with pvh_modify_header() or ++ pvh_remove_header() to change or remove value 2 for instance. + -+ * retrieve the username part ++3.7. auto_msg (int) + -+ $var(test) = $x_fU; ++ Defines wether the headers are automatically collected for incoming ++ messages, as well as automatically applied for forwarded messages. + -+ * $x_tU usage is the same ++ It is enabled by default and requires the 'tm' module to be loaded, ++ otherwise the mode is disabled and manual invocation of ++ pvh_collect_headers()/pvh_apply_headers() is required. + -+4.4. $x_fd, $x_td ++ Default value is 1 (enabled). + -+ These pseudovariables are used to modify/retreive the domain part of the "From" and "To" headers. ++ Example 1.7. Set auto_msg parameter ++... ++modparam("pvh", "auto_msg", 1) ++... + -+ Usage: ++4. Functions + -+ * modify the domain part ++ 4.1. pvh_collect_headers() ++ 4.2. pvh_apply_headers() ++ 4.3. pvh_reset_headers() ++ 4.4. pvh_check_header(hname) ++ 4.5. pvh_append_header(hname, hvalue) ++ 4.6. pvh_modify_header(hname, [idx], hvalue) ++ 4.7. pvh_remove_header(hname, [idx]) + -+ $x_fd = "example.local"; ++ \ + -+ * retrieve the domain part ++4.1. pvh_collect_headers() + -+ $var(test) = $x_fd; ++ Collects all headers from the message into the XAVP. It should be used ++ preferably just when the SIP message is received by Kamailio. + -+ * $x_td usage is the same ++ This function can be used from ANY_ROUTE. + -+4.5. $x_fn, $x_tn ++4.2. pvh_apply_headers() + -+ These pseudovariables are used to modify/retreive the display part of the "From" and "To" headers. ++ Applies the current XAVP headers state to the real headers. and should ++ be called only once per branch when the message is about to leave ++ Kamailio. + -+ Usage: ++ The following rules apply: ++ * all headers in the XAVP except for ones provided in the ++ skip_headers parameter and From/To are recreated in the SIP ++ message. ++ * From/To headers are processed by the uac module if it is loaded. ++ * From/To headers are not changed in the reply messages. ++ * headers with NULL value are removed if exist in the SIP message. ++ * the initial order of the SIP headers is preserved. + -+ * modify the username part ++ This function can be used from ANY_ROUTE. + -+ $x_fn = "User1"; ++4.3. pvh_reset_headers() + -+ * retrieve the domain part ++ Collects all headers from the message into the XAVP. It should be used ++ preferably just when the SIP message is received by >Kamailio. + -+ $var(test) = $x_fn; ++ This function can be used from ANY_ROUTE. + -+ * $x_tn usage is the same ++4.4. pvh_check_header(hname) + -+4.6. $x_ft, $x_tt ++ Checks if the header "hname" already exists in the XAVP. + -+ These pseudovariables are used to retreive the tag part of the "From" and "To" headers. ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with "auto_msg" parameter enabled. + -+ Usage: ++4.5. pvh_append_header(hname, hvalue) + -+ * retrieve the tag part ++ Appends a new header "hname" with the value "hvalue" into the XAVP. ++ Please note that subsequent "pv_append_header" calls will result in ++ multiple headers. + -+ $var(test) = $x_ft; ++ If the provided "hvalue" is $null then the header is added into the ++ XAVP but it is not going to be added into the message. + -+ * $x_tt usage is the same ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with "auto_msg" parameter enabled. + -+4.7. $x_rs, $x_rr ++4.6. pvh_modify_header(hname, [idx], hvalue) + -+ These pseudovariables are used to modify/retreive or change "status" and "code" of the SIP reply ++ Modifies an existing header in the XAVP "hname" with the value "hvalue" ++ into the XAVP. Index order is top to bottom. Please note that ++ subsequent pvh_append_header calls will result in multiple headers. + -+ NOTE: Only messages with reply status > 300 can be changed as well as reply status 1xx and 2xx cannot be set ++ Please note that if the header "hname"does not exist it will be ++ explicitly appended. If there are multiple headers with the same name ++ and "idx" is omitted, only the first one will be affected. + -+ Usage: ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with "auto_msg" parameter enabled. + -+ * modify the reply status ++4.7. pvh_remove_header(hname, [idx]) + -+ $x_rs = 486 ++ Removes an existing header "hname" from the XAVP. Index order is top to ++ bottom. + -+ * retrieve the reply status ++ If there are multiple headers with the same name and "idx" is omitted, ++ all of them will be removed. + -+ $var(test) = $x_rs; ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with "auto_msg" parameter enabled. + -+ * modify the reply reason ++5. Exported Variables + -+ $x_rr = "Custom Reason" ++ * $x_hdr ++ * $x_fu ++ * $x_fU ++ * $x_fd ++ * $x_fn ++ * $x_ft ++ * $x_tu ++ * $x_tU ++ * $x_td ++ * $x_tn ++ * $x_tt ++ * $x_rs ++ * $x_rr + -+ * retrieve the reply reason ++ Exported pseudo-variables are documented at ++ https://www.kamailio.org/wiki/. +diff --git a/src/modules/pv_headers/doc/Makefile b/src/modules/pv_headers/doc/Makefile +new file mode 100644 +index 0000000..b143109 +--- /dev/null ++++ b/src/modules/pv_headers/doc/Makefile +@@ -0,0 +1,4 @@ ++docs = pv_headers.xml + -+ $var(test) = $x_rr; ++docbook_dir = ../../../../doc/docbook ++include $(docbook_dir)/Makefile.module +diff --git a/src/modules/pv_headers/doc/functions.xml b/src/modules/pv_headers/doc/functions.xml +new file mode 100644 +index 0000000..d390061 +--- /dev/null ++++ b/src/modules/pv_headers/doc/functions.xml +@@ -0,0 +1,131 @@ ++ ++ ++ ++%docentities; ++ ++]> ++ ++ ++
++ ++ \ ++ ++ Functions ++
++ ++ <function moreinfo="none">pvh_collect_headers()</function> ++ ++ ++ Collects all headers from the message into the XAVP. ++ It should be used preferably just when the &sip; message is received by &kamailio;. ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++
++
++ ++ <function moreinfo="none">pvh_apply_headers()</function> ++ ++ ++ Applies the current XAVP headers state to the real headers. ++ and should be called only once per branch when the message is about to leave &kamailio;. ++ ++ ++ The following rules apply: ++ ++ ++ all headers in the XAVP except for ones provided in the ++ skip_headers parameter and ++ From/To are recreated in the &sip; message. ++ ++ From/To headers are processed by the uac module if it is loaded. ++ From/To headers are not changed in the reply messages. ++ headers with NULL value are removed if exist in the &sip; message. ++ the initial order of the &sip; headers is preserved. ++ ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++
++
++ ++ <function moreinfo="none">pvh_reset_headers()</function> ++ ++ ++ Collects all headers from the message into the XAVP. ++ It should be used preferably just when the &sip; message is received by >&kamailio;. ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++
++
++ ++ <function moreinfo="none">pvh_check_header(hname)</function> ++ ++ ++ Checks if the header hname already exists in the XAVP. ++ ++ ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with auto_msg parameter enabled. ++ ++
++
++ ++ <function moreinfo="none">pvh_append_header(hname, hvalue)</function> ++ ++ ++ Appends a new header hname with the value hvalue into the XAVP. ++ Please note that subsequent "pv_append_header" calls will result in multiple headers. ++ ++ ++ If the provided hvalue is $null then the header is added into the XAVP but it is ++ not going to be added into the message. ++ ++ ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with auto_msg parameter enabled. ++ ++
++
++ ++ <function moreinfo="none">pvh_modify_header(hname, [idx], hvalue)</function> ++ ++ ++ Modifies an existing header in the XAVP hname with the value hvalue into the XAVP. ++ Index order is top to bottom. ++ Please note that subsequent pvh_append_header calls ++ will result in multiple headers. ++ ++ ++ Please note that if the header hnamedoes not exist it will be explicitly appended. ++ If there are multiple headers with the same name and idx is omitted, only the first one ++ will be affected. ++ ++ ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with auto_msg parameter enabled. ++ ++
++
++ ++ <function moreinfo="none">pvh_remove_header(hname, [idx])</function> ++ ++ ++ Removes an existing header hname from the XAVP. Index order is top to bottom. ++ ++ ++ If there are multiple headers with the same name and idx is omitted, all of them will be removed. ++ ++ ++ This function can be used from ANY_ROUTE but only after ++ pvh_collect_headers() or with auto_msg parameter enabled. ++ ++
++
+diff --git a/src/modules/pv_headers/doc/params.xml b/src/modules/pv_headers/doc/params.xml +new file mode 100644 +index 0000000..83e74d6 +--- /dev/null ++++ b/src/modules/pv_headers/doc/params.xml +@@ -0,0 +1,186 @@ ++ ++ ++ ++%docentities; ++ ++]> ++ ++
++ ++ ++ ++ Parameters ++
++ <varname>xavp_name</varname> (string) ++ ++ Name of the XAVP there the collected headers are stored. ++ ++ ++ ++ Default value is headers. ++ ++ ++ ++ Set <varname>xavp_name</varname> parameter ++ ++... ++modparam("pv_headers", "xavp_name", "pvh") ++... ++ ++ ++ Result: ++ $xavp(headers[0]=>From) ++ $xavp(headers[0]=>To) ++ $xavp(headers[0]=>Call-ID) ++ ++ ++
++
++ <varname>header_value_size</varname> (int) ++ ++ Defines an internal maximum SIP header value size. Header values ++ longer than this setting will be stripped down when collected or applied. ++ ++ ++ ++ Default value is 1024. ++ ++ ++ ++ Set <varname>header_value_size</varname> parameter ++ ++... ++modparam("pv_headers", "header_value_size", 512) ++... ++ ++ ++
++
++ <varname>header_collect_flag</varname> (int) ++ ++ Used to mark that headers are collected for the &sip; message, leading to ++ subsequent headers collection on this message to be declined with an error. ++ Should be used only in branches and replies. ++ ++ ++ ++ Default value is 27. ++ ++ ++ ++ Set <varname>header_collect_flag</varname> parameter ++ ++... ++modparam("pv_headers", "header_collect_flag", 37) ++... ++ ++ ++
++
++ <varname>header_apply_flag</varname> (int) ++ ++ Used to mark that headers are applied for the &sip; message, leading to subsequent ++ headers applies on this message to be declined with an error. ++ Should be used only in branches and replies. ++ ++ ++ ++ Default value is 28. ++ ++ ++ ++ Set <varname>header_apply_flag</varname> parameter ++ ++... ++modparam("pv_headers", "header_apply_flag", 38) ++... ++ ++ ++
++
++ <varname>skip_headers</varname> (string) ++ ++ A comma separated headers list that must be excluded from processing (they are skipped when ++ pvh_apply_headers() ++ changes the &sip; message headers). ++ ++ ++ If the parameter is set to an empty string then all the &sip; message headers are processed. ++ ++ ++ ++ Default value is Record-Route,Via,Route,Content-Length,Max-Forwards,CSeq. ++ ++ ++ ++ Set <varname>skip_headers</varname> parameter ++ ++... ++modparam("pv_headers", "skip_headers", "Record-Route,Via,Route") ++... ++ ++ ++
++
++ <varname>split_headers</varname> (string) ++ ++ A comma separated headers list that must be split into multi headers if ++ their value is a comma separated list. ++ ++ ++ If the parameter is set to an empty string then no headers are split. ++ ++ ++ ++ Default value is . ++ ++ ++ ++ Set <varname>split_headers</varname> parameter ++ ++... ++modparam("pv_headers", "split_headers", "Diversion") ++... ++ ++ ++ Result: ++ Received Diversion header: ++ Diversion: <user1@test.local>,<user2@test.local>,<user3@test.local> ++ After split: ++ Diversion: <user1@test.local> ++ Diversion: <user2@test.local> ++ Diversion: <user3@test.local> ++ ++ ++ Becomes handy if used together with pvh_modify_header() ++ or pvh_remove_header() to change or remove value 2 for instance. ++ ++ ++
++
++ <varname>auto_msg</varname> (int) ++ ++ Defines wether the headers are automatically collected for incoming ++ messages, as well as automatically applied for forwarded messages. ++ ++ It is enabled by default and requires the 'tm' module to be loaded, otherwise the mode is disabled ++ and manual invocation of pvh_collect_headers()/pvh_apply_headers() is required. ++ ++ ++ ++ Default value is 1 (enabled). ++ ++ ++ ++ Set <varname>auto_msg</varname> parameter ++ ++... ++modparam("pvh", "auto_msg", 1) ++... ++ ++ ++
++
+\ No newline at end of file +diff --git a/src/modules/pv_headers/doc/pv_headers.xml b/src/modules/pv_headers/doc/pv_headers.xml +new file mode 100644 +index 0000000..a5211b9 +--- /dev/null ++++ b/src/modules/pv_headers/doc/pv_headers.xml +@@ -0,0 +1,39 @@ ++ ++ ++ ++ ++ %docentities; ++ ] ++> ++ ++ ++ ++ pv_headers Module ++ &kamailioname; ++ ++ ++ Kirill ++ Solomko ++ ksolomko@sipwise.com ++ Sipwise GmbH ++ ++ ++ Victor ++ Seva ++ vseva@sipwise.com ++ Sipwise GmbH ++ ++ ++ ++ 2018 ++ Sipwise GmbH ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/modules/pv_headers/doc/pv_headers_admin.xml b/src/modules/pv_headers/doc/pv_headers_admin.xml +new file mode 100644 +index 0000000..9c5c009 +--- /dev/null ++++ b/src/modules/pv_headers/doc/pv_headers_admin.xml +@@ -0,0 +1,116 @@ ++ ++ ++ ++ ++%docentities; ++ ++]> ++ ++ ++ ++ ++ ++ &adminguide; ++ ++
++ Overview ++ ++ The main goal of the module is to offload the intermediate header processing ++ into the XAVP dynamic container as well as provide with high level methods ++ and pseudovariables to simplify &sip; message header modifications. ++ ++
++
++ Dependencies ++
++ &kamailio; Modules ++ ++ The following modules must be loaded before this module: ++ ++ ++ ++ uac. ++ ++ ++ ++ ++ tm. ++ ++ ++ Needed only if auto_msg parameter is set to 1. ++ ++ ++ ++ ++
++
++ External Libraries or Applications ++ ++ The following libraries or applications must be installed before running ++ &kamailio; with this module loaded: ++ ++ ++ ++ None. ++ ++ ++ ++ ++
++
++ ++ ++ ++ ++
++ Exported Variables ++ ++ ++ $x_hdr ++ ++ ++ $x_fu ++ ++ ++ $x_fU ++ ++ ++ $x_fd ++ ++ ++ $x_fn ++ ++ ++ $x_ft ++ ++ ++ $x_tu ++ ++ ++ $x_tU ++ ++ ++ $x_td ++ ++ ++ $x_tn ++ ++ ++ $x_tt ++ ++ ++ $x_rs ++ ++ ++ $x_rr ++ ++ ++ ++ Exported pseudo-variables are documented at &kamwikilink;. ++ ++
++ ++
diff --git a/src/modules/pv_headers/pv_headers.c b/src/modules/pv_headers/pv_headers.c new file mode 100644 -index 0000000..79e91e6 +index 0000000..7d7863f --- /dev/null +++ b/src/modules/pv_headers/pv_headers.c -@@ -0,0 +1,2069 @@ +@@ -0,0 +1,654 @@ +/* + * pv_headers + * -+ * Copyright (C) 2018 Kirill Solomko ++ * Copyright (C) 2018 Kirill Solomko + * + * This file is part of SIP Router, a free SIP server. + * @@ -569,27 +945,19 @@ index 0000000..79e91e6 + * + */ + -+#include -+#include -+#include -+ +#include "../../core/sr_module.h" +#include "../../core/mod_fix.h" -+#include "../../core/dprint.h" +#include "../../core/dset.h" -+#include "../../core/flags.h" -+#include "../../core/xavp.h" -+#include "../../core/pvar.h" -+#include "../../core/str_hash.h" -+#include "../../core/data_lump.h" -+#include "../../core/mem/shm_mem.h" -+#include "../../core/parser/parse_from.h" -+#include "../../core/parser/parse_uri.h" -+#include "../../core/parser/msg_parser.h" +#include "../../core/script_cb.h" +#include "../../modules/uac/api.h" +#include "../../modules/tm/tm_load.h" + ++#include "pv_headers.h" ++#include "pvh_hash.h" ++#include "pvh_str.h" ++#include "pvh_xavp.h" ++#include "pvh_hdr.h" ++ +MODULE_VERSION + +#define MODULE_NAME "pv_headers" @@ -605,2028 +973,2389 @@ index 0000000..79e91e6 +static uac_api_t uac; +static tm_api_t tmb; + -+static str xavp_name = str_init(XAVP_NAME); ++str xavp_name = str_init(XAVP_NAME); + -+static str xavp_helper_xname = str_init("modparam_pv_headers"); -+static str xavp_parsed_xname = str_init("parsed_pv_headers"); -+static str xavp_helper_name = str_init("xavp_name"); -+static str skip_headers_param = str_init("Record-Route,Via,Route,Content-Length,Max-Forwards,CSeq"); ++static str xavp_helper_xname = str_init("modparam_pv_headers"); ++str xavp_parsed_xname = str_init("parsed_pv_headers"); ++static str xavp_helper_name = str_init("xavp_name"); ++static str skip_headers_param = ++ str_init("Record-Route,Via,Route,Content-Length,Max-Forwards,CSeq"); +static str split_headers_param = STR_NULL; -+static int auto_msg_param = 1; ++static int auto_msg_param = 1; + +static str single_headers_param = str_init(""); + -+static unsigned int header_name_size = 255; -+static unsigned int header_value_size = 1024; -+ -+static struct str_hash_table skip_headers; -+static struct str_hash_table split_headers; -+static struct str_hash_table single_headers; ++unsigned int header_name_size = 255; ++unsigned int header_value_size = 1024; + -+typedef struct _xavp_c_data { -+ struct to_body to_b; -+ struct to_param *to_params; -+ str value; -+} xavp_c_data_t; + +static void mod_destroy(void); +static int mod_init(void); + -+static void handle_tm_t(tm_cell_t *t, int type, struct tmcb_params* params); -+static int handle_msg_cb(struct sip_msg *msg, unsigned int flags, void *cb); -+ -+static int pv_collect_headers(struct sip_msg *msg, char *is_auto, char *_s2); -+static int pv_apply_headers(struct sip_msg *msg, char *is_auto, char *_s2); -+static int pv_reset_headers(struct sip_msg *_m, char *_s1, char *_s2); -+ -+static int pv_check_header(struct sip_msg *_m, char *hname, char *_s2); -+static int pv_append_header(struct sip_msg *_m, char *hname, char *hvalue); -+static int pv_modify_header(struct sip_msg *_m, char *hname, char *hvalue); -+static int pv_modify_header_idx(struct sip_msg *_m, char *hname, char *hidx, char *hvalue); -+static int pv_remove_header(struct sip_msg *_m, char *hname, char *_s2); -+static int pv_remove_header_idx(struct sip_msg *_m, char *hname, char *hidx); -+ -+static int pv_set_xavp(struct sip_msg *msg, str *xname, str *name, void *data, sr_xtype_t type, int idx, int append); -+static int pv_free_xavp(str *xname); -+ -+static int pv_fixup_svalue(struct sip_msg *msg, char *src, str *dst); -+static int pv_real_hdr_append(struct sip_msg *msg, str *hname, str *hvalue); -+static int pv_real_hdr_replace(struct sip_msg *msg, str *hname, str *hvalue); -+static int pv_real_hdr_del_by_name(struct sip_msg *msg, str *hname); -+static int pv_real_hdr_remove_display(struct sip_msg *msg, str *hname); -+static int pv_real_replace_reply_reason(struct sip_msg *msg, str *value); -+static int pv_create_hdr_str(str *hname, str *hvalue, str *dst); -+static int pv_str_new(str *s, int size); -+static int pv_str_free(str *s); -+static int pv_str_copy(str *dst, str *src, unsigned int max_size); -+static int pv_extract_display_uri(char *suri, str *display, str *duri); -+static int pv_split_values(str *s, char d[][header_value_size], int *d_size, int keep_spaces); -+ -+static sr_xavp_t * pv_xavp_new_value(str *name, sr_xval_t *val); -+static int pv_xavp_append_value(str *name, sr_xval_t *val, sr_xavp_t **start); -+static int pv_xavp_set_value(str *name, sr_xval_t *val, int idx, sr_xavp_t **start); -+static sr_xval_t * pv_xavp_get_value(struct sip_msg *msg, str *xname, str *name, int idx); -+static sr_xavp_t * pv_xavp_get_child(struct sip_msg *msg, str *xname, str *name); -+static int pv_xavp_is_null(sr_xavp_t * xavp); -+static void pv_xavp_free_data(void *p, sr_xavp_sfree_f sfree); -+static void pv_free_to_params(struct to_param *param, sr_xavp_sfree_f sfree); -+ -+static int pv_skip_header(str *hname); -+static int pv_single_header(str *hname); -+ -+static int pv_xavp_keys_count(sr_xavp_t **start); -+ -+static int pv_str_hash_init(struct str_hash_table *ht, str *keys, char *desc); -+static int pv_str_hash_add_key(struct str_hash_table *ht, str *key); -+static int pv_str_hash_free(struct str_hash_table *ht); -+ -+static int pv_parse_header_name(pv_spec_p sp, str *hname); -+static int pv_get_header(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); -+static int pv_set_header(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); -+static int pv_get_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); -+static int pv_set_uri(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); -+static int pv_merge_uri(struct sip_msg *msg, enum action_type type, str *cur, str *new, xavp_c_data_t *c_data); -+static int pv_get_reply_sr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); -+static int pv_set_reply_sr(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); -+ -+static int pv_get_branch_index(struct sip_msg *msg, int *br_idx); -+static int pv_get_branch_xname(struct sip_msg *msg, str *xname, str *dst); -+static int pv_clone_branch_xavp(struct sip_msg *msg, str *xname); ++static int pvh_fixup_svalue(struct sip_msg *msg, char *src, str *dst); ++ ++static void handle_tm_t(tm_cell_t *t, int type, struct tmcb_params *params); ++static int handle_msg_cb(struct sip_msg *msg, unsigned int flags, void *cb); ++ ++static int pvh_collect_headers(struct sip_msg *msg, char *is_auto, char *_s2); ++static int pvh_apply_headers(struct sip_msg *msg, char *is_auto, char *_s2); ++static int pvh_reset_headers(struct sip_msg *_m, char *_s1, char *_s2); ++ ++static int pvh_check_header(struct sip_msg *_m, char *hname, char *_s2); ++static int pvh_append_header(struct sip_msg *_m, char *hname, char *hvalue); ++static int pvh_modify_header(struct sip_msg *_m, char *hname, char *hvalue); ++static int pvh_modify_header_idx( ++ struct sip_msg *_m, char *hname, char *hidx, char *hvalue); ++static int pvh_remove_header(struct sip_msg *_m, char *hname, char *_s2); ++static int pvh_remove_header_idx(struct sip_msg *_m, char *hname, char *hidx); + +/* + * Exported functions + */ +static cmd_export_t cmds[] = { -+ {"pv_collect_headers", (cmd_function)pv_collect_headers, 0, 0, 0, ANY_ROUTE}, -+ {"pv_apply_headers", (cmd_function)pv_apply_headers, 0, 0, 0, ANY_ROUTE}, -+ {"pv_reset_headers", (cmd_function)pv_reset_headers, 0, 0, 0, ANY_ROUTE}, -+ {"pv_check_header", (cmd_function)pv_check_header, 1, fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, -+ {"pv_append_header", (cmd_function)pv_append_header, 2, fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, -+ {"pv_modify_header", (cmd_function)pv_modify_header, 2, fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, -+ {"pv_modify_header", (cmd_function)pv_modify_header_idx, 3, fixup_spve_all, fixup_free_spve_all, ANY_ROUTE}, -+ {"pv_remove_header", (cmd_function)pv_remove_header, 1, fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, -+ {"pv_remove_header", (cmd_function)pv_remove_header_idx, 2, fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, -+ {0, 0, 0, 0, 0, 0} -+}; ++ {"pvh_collect_headers", (cmd_function)pvh_collect_headers, 0, 0, 0, ++ ANY_ROUTE}, ++ {"pvh_apply_headers", (cmd_function)pvh_apply_headers, 0, 0, 0, ++ ANY_ROUTE}, ++ {"pvh_reset_headers", (cmd_function)pvh_reset_headers, 0, 0, 0, ++ ANY_ROUTE}, ++ {"pvh_check_header", (cmd_function)pvh_check_header, 1, fixup_spve_null, ++ fixup_free_spve_null, ANY_ROUTE}, ++ {"pvh_append_header", (cmd_function)pvh_append_header, 2, ++ fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, ++ {"pvh_modify_header", (cmd_function)pvh_modify_header, 2, ++ fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, ++ {"pvh_modify_header", (cmd_function)pvh_modify_header_idx, 3, ++ fixup_spve_all, fixup_free_spve_all, ANY_ROUTE}, ++ {"pvh_remove_header", (cmd_function)pvh_remove_header, 1, ++ fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, ++ {"pvh_remove_header", (cmd_function)pvh_remove_header_idx, 2, ++ fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, ++ {0, 0, 0, 0, 0, 0}}; + +static pv_export_t mod_pvs[] = { -+ {{"x_hdr", (sizeof("x_hdr")-1)}, PVT_OTHER, pv_get_header, pv_set_header, pv_parse_header_name, pv_parse_index, 0, 0}, -+ {{"x_fu", (sizeof("x_fu")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 1}, -+ {{"x_fU", (sizeof("x_fU")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 2}, -+ {{"x_fd", (sizeof("x_fd")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 3}, -+ {{"x_fn", (sizeof("x_fn")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 4}, -+ {{"x_ft", (sizeof("x_ft")-1)}, PVT_OTHER, pv_get_uri, /* ro */ 0, 0, 0, pv_init_iname, 5}, -+ {{"x_tu", (sizeof("x_tu")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 6}, -+ {{"x_tU", (sizeof("x_tU")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 7}, -+ {{"x_td", (sizeof("x_td")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 8}, -+ {{"x_tn", (sizeof("x_tn")-1)}, PVT_OTHER, pv_get_uri, pv_set_uri, 0, 0, pv_init_iname, 9}, -+ {{"x_tt", (sizeof("x_tt")-1)}, PVT_OTHER, pv_get_uri, /* ro */ 0, 0, 0, pv_init_iname, 10}, -+ {{"x_rs", (sizeof("x_rs")-1)}, PVT_OTHER, pv_get_reply_sr, pv_set_reply_sr, 0, 0, pv_init_iname, 1}, -+ {{"x_rr", (sizeof("x_rr")-1)}, PVT_OTHER, pv_get_reply_sr, pv_set_reply_sr, 0, 0, pv_init_iname, 2}, -+ { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } -+}; -+ -+static param_export_t params[] = { -+ {"xavp_name", PARAM_STR, &xavp_name}, -+ {"header_value_size", PARAM_INT, &header_value_size}, -+ {"header_collect_flag", PARAM_INT, &FL_PV_HDRS_COLLECTED}, -+ {"header_apply_flag", PARAM_INT, &FL_PV_HDRS_APPLIED}, -+ {"skip_headers", PARAM_STR, &skip_headers_param}, -+ {"split_headers", PARAM_STR, &split_headers_param}, -+ {"auto_msg", PARAM_INT, &auto_msg_param}, -+ {0, 0, 0} -+}; ++ {{"x_hdr", (sizeof("x_hdr") - 1)}, PVT_OTHER, pvh_get_header, ++ pvh_set_header, pvh_parse_header_name, pv_parse_index, 0, 0}, ++ {{"x_fu", (sizeof("x_fu") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 1}, ++ {{"x_fU", (sizeof("x_fU") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 2}, ++ {{"x_fd", (sizeof("x_fd") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 3}, ++ {{"x_fn", (sizeof("x_fn") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 4}, ++ {{"x_ft", (sizeof("x_ft") - 1)}, PVT_OTHER, pvh_get_uri, /* ro */ 0, 0, ++ 0, pv_init_iname, 5}, ++ {{"x_tu", (sizeof("x_tu") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 6}, ++ {{"x_tU", (sizeof("x_tU") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 7}, ++ {{"x_td", (sizeof("x_td") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 8}, ++ {{"x_tn", (sizeof("x_tn") - 1)}, PVT_OTHER, pvh_get_uri, pvh_set_uri, 0, ++ 0, pv_init_iname, 9}, ++ {{"x_tt", (sizeof("x_tt") - 1)}, PVT_OTHER, pvh_get_uri, /* ro */ 0, 0, ++ 0, pv_init_iname, 10}, ++ {{"x_rs", (sizeof("x_rs") - 1)}, PVT_OTHER, pvh_get_reply_sr, ++ pvh_set_reply_sr, 0, 0, pv_init_iname, 1}, ++ {{"x_rr", (sizeof("x_rr") - 1)}, PVT_OTHER, pvh_get_reply_sr, ++ pvh_set_reply_sr, 0, 0, pv_init_iname, 2}, ++ {{0, 0}, 0, 0, 0, 0, 0, 0, 0}}; ++ ++static param_export_t params[] = {{"xavp_name", PARAM_STR, &xavp_name}, ++ {"header_value_size", PARAM_INT, &header_value_size}, ++ {"header_collect_flag", PARAM_INT, &FL_PV_HDRS_COLLECTED}, ++ {"header_apply_flag", PARAM_INT, &FL_PV_HDRS_APPLIED}, ++ {"skip_headers", PARAM_STR, &skip_headers_param}, ++ {"split_headers", PARAM_STR, &split_headers_param}, ++ {"auto_msg", PARAM_INT, &auto_msg_param}, {0, 0, 0}}; + +struct module_exports exports = { -+ MODULE_NAME, /* module name */ -+ DEFAULT_DLFLAGS, /* dlopen flags */ -+ cmds, /* exported functions */ -+ params, /* exported parameters */ -+ 0, /* RPC method exports */ -+ mod_pvs, /* exported pseudo-variables */ -+ 0, /* response handling function */ -+ mod_init, /* module initialization function */ -+ 0, /* per-child init function */ -+ mod_destroy /* module destroy function */ ++ MODULE_NAME, /* module name */ ++ DEFAULT_DLFLAGS, /* dlopen flags */ ++ cmds, /* exported functions */ ++ params, /* exported parameters */ ++ 0, /* RPC method exports */ ++ mod_pvs, /* exported pseudo-variables */ ++ 0, /* response handling function */ ++ mod_init, /* module initialization function */ ++ 0, /* per-child init function */ ++ mod_destroy /* module destroy function */ +}; + +int mod_init(void) +{ -+ LM_INFO("%s module init...\n", MODULE_NAME); -+ -+ if (load_uac_api(&uac) < 0) { -+ LM_NOTICE("could not bind to the 'uac' module, From/To headers will not be modifed\n"); -+ } -+ -+ if (load_tm_api(&tmb) < 0) { -+ LM_NOTICE("could not bind to the 'tm' module, automatic headers collect/apply is disabled\n"); -+ auto_msg_param = 0; -+ } else { -+ if (auto_msg_param && -+ register_script_cb(handle_msg_cb, PRE_SCRIPT_CB|REQUEST_CB, 0) < 0) { -+ LM_ERR("cannot register PRE_SCRIPT_CB callbacks\n"); -+ return -1; -+ } -+ } -+ -+ if (header_value_size == 0) { -+ LM_ERR("header_value_size must be >=0\n"); -+ return -1; -+ } -+ -+ pv_str_hash_init(&skip_headers, &skip_headers_param, "skip_headers"); -+ pv_str_hash_init(&split_headers, &split_headers_param, "split_headers"); -+ pv_str_hash_init(&single_headers, &single_headers_param, "single_headers"); -+ -+ return 0; -+} ++ LM_INFO("%s module init...\n", MODULE_NAME); + -+void mod_destroy(void) -+{ -+ pv_str_hash_free(&skip_headers); -+ pv_str_hash_free(&split_headers); -+ pv_str_hash_free(&single_headers); -+ pv_free_xavp(&xavp_name); -+ pv_free_xavp(&xavp_parsed_xname); -+ LM_INFO("%s module unload...\n", MODULE_NAME); -+} ++ if(load_uac_api(&uac) < 0) { ++ LM_NOTICE("could not bind to the 'uac' module, From/To headers will " ++ "not be modifed\n"); ++ } + -+void handle_tm_t(tm_cell_t *t, int type, struct tmcb_params* params) -+{ -+ struct sip_msg *msg = NULL; -+ -+ if (type & TMCB_RESPONSE_IN) { -+ msg = params->rpl; -+ if (msg != NULL && msg != FAKED_REPLY) { -+ pv_reset_headers(msg, NULL, NULL); -+ pv_collect_headers(msg, "1", NULL); -+ } -+ } else if (type & TMCB_REQUEST_FWDED) { -+ msg = params->req; -+ } else if (type &(TMCB_ON_BRANCH_FAILURE|TMCB_RESPONSE_FWDED)) { -+ msg = params->rpl; -+ } else { -+ LM_ERR("unknown callback: %d\n", type); -+ return; -+ } -+ -+ if (msg != NULL && msg != FAKED_REPLY) -+ pv_apply_headers(msg, "1", NULL); -+ -+ return; -+} ++ if(load_tm_api(&tmb) < 0) { ++ LM_NOTICE("could not bind to the 'tm' module, automatic headers " ++ "collect/apply is disabled\n"); ++ auto_msg_param = 0; ++ } else { ++ if(auto_msg_param ++ && register_script_cb( ++ handle_msg_cb, PRE_SCRIPT_CB | REQUEST_CB, 0) ++ < 0) { ++ LM_ERR("cannot register PRE_SCRIPT_CB callbacks\n"); ++ return -1; ++ } ++ } + -+int handle_msg_cb(struct sip_msg *msg, unsigned int flags, void *cb) -+{ -+ int cbs = TMCB_REQUEST_FWDED | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_IN | TMCB_ON_BRANCH_FAILURE; -+ -+ if (flags & (PRE_SCRIPT_CB|REQUEST_CB)) { -+ if (tmb.register_tmcb( msg, 0, cbs, handle_tm_t, 0, 0) <=0) { -+ LM_ERR("cannot register TM callbacks\n"); -+ return -1; -+ } -+ pv_collect_headers(msg, "1", NULL); -+ } else { -+ LM_ERR("unknown callback: %d\n", flags); -+ } -+ -+ return 1; -+} ++ if(header_value_size == 0) { ++ LM_ERR("header_value_size must be >=0\n"); ++ return -1; ++ } + -+int pv_collect_headers(struct sip_msg *msg, char *is_auto, char *_s2) -+{ -+ struct hdr_field *hf = NULL; -+ str name = STR_NULL; -+ str val = STR_NULL; -+ char hvals[header_name_size][header_value_size]; -+ int idx = 0, d_size = 0; -+ str val_part = STR_NULL; -+ int br_idx; -+ -+ pv_get_branch_index(msg, &br_idx); -+ LM_DBG("br_idx: %d\n", br_idx); -+ if (!(is_auto && strcmp(is_auto, "1") == 0) && -+ ((msg->first_line.type == SIP_REPLY && isflagset(msg, FL_PV_HDRS_COLLECTED) == 1) || -+ (msg->first_line.type != SIP_REPLY && isbflagset(br_idx, FL_PV_HDRS_COLLECTED) == 1))) { -+ LM_ERR("headers are already collected\n"); -+ return -1; -+ } -+ -+ if (parse_headers(msg, HDR_EOH_F, 0) < 0) { -+ LM_ERR("error parsing headers\n"); -+ return -1; -+ } -+ -+ if (pv_str_new(&name, header_name_size) < 0) goto err; -+ if (pv_str_new(&val, header_value_size) < 0) goto err; -+ -+ if (name.s == NULL || val.s == NULL) -+ goto err; -+ -+ for (hf=msg->headers; hf; hf=hf->next) { -+ LM_DBG("collect header[%.*s]: %.*s\n", hf->name.len, hf->name.s, hf->body.len, hf->body.s); -+ -+ pv_str_copy(&val, &hf->body, header_value_size); -+ pv_str_copy(&name, &hf->name, header_name_size); -+ -+ if (str_hash_get(&split_headers, name.s, name.len) && -+ strchr(val.s, ',') != NULL) { -+ -+ if (pv_split_values(&val, hvals, &d_size, 1) < 0) { -+ LM_ERR("could not parse Diversion header comma separated value"); -+ return -1; -+ } -+ -+ for (idx=0;idxfirst_line.type == SIP_REPLY ? setflag(msg, FL_PV_HDRS_COLLECTED) -+ : setbflag(br_idx, FL_PV_HDRS_COLLECTED); -+ -+ return 1; ++ pvh_str_hash_init(&skip_headers, &skip_headers_param, "skip_headers"); ++ pvh_str_hash_init(&split_headers, &split_headers_param, "split_headers"); ++ pvh_str_hash_init(&single_headers, &single_headers_param, "single_headers"); + -+err: -+ pv_str_free(&name); -+ pv_str_free(&val); -+ return -1; ++ return 0; +} + -+int pv_apply_headers(struct sip_msg *msg, char *is_auto, char *_s2) ++void mod_destroy(void) +{ -+ sr_xavp_t *xavp = NULL; -+ sr_xavp_t *sub = NULL; -+ str display = STR_NULL; -+ str uri = STR_NULL; -+ struct str_hash_table rm_hdrs; -+ int from_cnt = 0, to_cnt = 0; -+ str br_xname = STR_NULL; -+ int br_idx, keys_count; -+ -+ rm_hdrs.size = 0; -+ -+ pv_get_branch_index(msg, &br_idx); -+ -+ if (!(is_auto && strcmp(is_auto, "1") == 0) && -+ ((msg->first_line.type == SIP_REPLY && isflagset(msg, FL_PV_HDRS_APPLIED) == 1) || -+ (msg->first_line.type != SIP_REPLY && isbflagset(br_idx, FL_PV_HDRS_APPLIED) == 1))) { -+ LM_ERR("headers are already applied\n"); -+ return -1; -+ } -+ -+ if (parse_headers(msg, HDR_EOH_F, 0) < 0) { -+ LM_ERR("error parsing headers\n"); -+ return -1; -+ } -+ -+ if (pv_str_new(&display, header_value_size) < 0) goto err; -+ if (pv_str_new(&uri, header_value_size) < 0) goto err; -+ if (pv_str_new(&br_xname, header_value_size) < 0) goto err; -+ -+ pv_get_branch_xname(msg, &xavp_name, &br_xname); -+ -+ if ((xavp = xavp_get(&br_xname, NULL)) == NULL && -+ (xavp = xavp_get(&xavp_name, NULL)) == NULL) { -+ LM_ERR("missing xavp %s, run pv_collect_headers() first\n", xavp_name.s); -+ goto err; -+ } -+ if (xavp->val.type != SR_XTYPE_XAVP) { -+ LM_ERR("not xavp child type %s\n" , xavp_name.s); -+ goto err; -+ } -+ -+ if ((sub = xavp->val.v.xavp) == NULL) { -+ LM_ERR("invalid xavp structure: %s\n", xavp_name.s); -+ goto err; -+ } -+ keys_count = pv_xavp_keys_count(&sub); -+ if (str_hash_alloc(&rm_hdrs, keys_count) < 0) { -+ LM_ERR("memory allocation error\n"); -+ goto err; -+ } -+ LM_DBG("xavp->name:%.*s br_xname:%.*s keys_count: %d\n", -+ xavp->name.len, xavp->name.s, br_xname.len, br_xname.s, keys_count); -+ str_hash_init(&rm_hdrs); -+ -+ do { -+ if (pv_skip_header(&sub->name)) -+ continue; -+ -+ if (strncasecmp(sub->name.s, "From", sub->name.len) == 0) { -+ if (msg->first_line.type == SIP_REPLY || -+ msg->first_line.u.request.method_value == METHOD_ACK || -+ msg->first_line.u.request.method_value == METHOD_PRACK || -+ msg->first_line.u.request.method_value == METHOD_BYE) { -+ LM_DBG("skip From header change in reply messages\n"); -+ continue; -+ } -+ if (strncmp(sub->val.v.s.s, msg->from->body.s, sub->val.v.s.len) == 0) { -+ LM_DBG("skip unchanged From header\n"); -+ continue; -+ } -+ if (from_cnt > 0) -+ continue; -+ -+ memset(display.s, 0, header_value_size); -+ memset(uri.s, 0, header_value_size); -+ -+ if (pv_extract_display_uri(sub->val.v.s.s, &display, &uri) < 0) { -+ LM_ERR("error parsing From header\n"); -+ goto err; -+ } -+ -+ if (uac.replace_from != NULL) { -+ LM_DBG("replace_from[%s]: %s %s\n", sub->name.s, display.s, uri.s); -+ if (display.len == 0) -+ pv_real_hdr_remove_display(msg, &sub->name); -+ uac.replace_from(msg, &display, &uri); -+ } -+ -+ from_cnt++; -+ continue; -+ } -+ -+ if (strncasecmp(sub->name.s, "To", sub->name.len) == 0) { -+ if (msg->first_line.type == SIP_REPLY || -+ msg->first_line.u.request.method_value == METHOD_ACK || -+ msg->first_line.u.request.method_value == METHOD_PRACK || -+ msg->first_line.u.request.method_value == METHOD_BYE) { -+ LM_DBG("skip To header change in reply messages\n"); -+ continue; -+ } -+ if (strncmp(sub->val.v.s.s, msg->to->body.s, sub->val.v.s.len) == 0) { -+ LM_DBG("skip unchanged To header\n"); -+ continue; -+ } -+ if (to_cnt > 0) -+ continue; -+ -+ memset(display.s, 0, header_value_size); -+ memset(uri.s, 0, header_value_size); -+ -+ if (pv_extract_display_uri(sub->val.v.s.s, &display, &uri) < 0) { -+ LM_ERR("error parsing To header\n"); -+ goto err; -+ } -+ -+ if (uac.replace_to != NULL) { -+ LM_DBG("replace_to[%s]: %s %s\n", sub->name.s, display.s, uri.s); -+ if (display.len == 0) -+ pv_real_hdr_remove_display(msg, &sub->name); -+ uac.replace_to(msg, &display, &uri); -+ } -+ -+ to_cnt++; -+ continue; -+ } -+ -+ if (strncasecmp(sub->name.s, "@Reply-Reason", sub->name.len) == 0) { -+ if (str_hash_get(&rm_hdrs, sub->name.s, sub->name.len)) -+ continue; -+ pv_real_replace_reply_reason(msg, &sub->val.v.s); -+ pv_str_hash_add_key(&rm_hdrs, &sub->name); -+ continue; -+ } -+ -+ if (!str_hash_get(&rm_hdrs, sub->name.s, sub->name.len)) { -+ if (!pv_xavp_is_null(sub) && xavp_count(&sub->name, &sub) == 1) { -+ LM_DBG("replace header[%s]: %s\n", sub->name.s, sub->val.v.s.s); -+ pv_real_hdr_replace(msg, &sub->name, &sub->val.v.s); -+ pv_str_hash_add_key(&rm_hdrs, &sub->name); -+ continue; -+ } -+ LM_DBG("remove header[%s]: %s\n", sub->name.s, sub->val.v.s.s); -+ pv_real_hdr_del_by_name(msg, &sub->name); -+ pv_str_hash_add_key(&rm_hdrs, &sub->name); -+ } -+ -+ if (!pv_xavp_is_null(sub) && !pv_single_header(&sub->name)) { -+ pv_real_hdr_append(msg, &sub->name, &sub->val.v.s); -+ LM_DBG("append header[%s]: %s\n", sub->name.s, sub->val.v.s.s); -+ } -+ } while ((sub = sub->next) != NULL); -+ -+ msg->first_line.type == SIP_REPLY ? setflag(msg, FL_PV_HDRS_APPLIED) -+ : setbflag(br_idx, FL_PV_HDRS_APPLIED); -+ -+ pv_str_free(&display); -+ pv_str_free(&uri); -+ pv_str_free(&br_xname); -+ if (rm_hdrs.size) -+ pv_str_hash_free(&rm_hdrs); -+ -+ return 1; -+ -+err: -+ pv_str_free(&display); -+ pv_str_free(&uri); -+ pv_str_free(&br_xname); -+ if (rm_hdrs.size) -+ pv_str_hash_free(&rm_hdrs); -+ return -1; ++ pvh_str_hash_free(&skip_headers); ++ pvh_str_hash_free(&split_headers); ++ pvh_str_hash_free(&single_headers); ++ pvh_free_xavp(&xavp_name); ++ pvh_free_xavp(&xavp_parsed_xname); ++ LM_INFO("%s module unload...\n", MODULE_NAME); +} + -+int pv_reset_headers(struct sip_msg *msg, char *_s1, char *_s2) ++void handle_tm_t(tm_cell_t *t, int type, struct tmcb_params *params) +{ -+ str br_xname = STR_NULL; -+ int br_idx; -+ -+ if (pv_str_new(&br_xname, header_name_size) < 0) -+ return -1; -+ -+ pv_get_branch_index(msg, &br_idx); -+ pv_get_branch_xname(msg, &xavp_name, &br_xname); -+ -+ pv_free_xavp(&br_xname); -+ pv_get_branch_xname(msg, &xavp_parsed_xname, &br_xname); -+ pv_free_xavp(&br_xname); -+ -+ if (msg->first_line.type == SIP_REPLY) { -+ resetflag(msg, FL_PV_HDRS_COLLECTED); -+ resetflag(msg, FL_PV_HDRS_APPLIED); -+ } else { -+ resetbflag(br_idx, FL_PV_HDRS_COLLECTED); -+ resetbflag(br_idx, FL_PV_HDRS_APPLIED); -+ } ++ struct sip_msg *msg = NULL; ++ ++ if(type & TMCB_RESPONSE_IN) { ++ msg = params->rpl; ++ if(msg != NULL && msg != FAKED_REPLY) { ++ pvh_reset_headers(msg, NULL, NULL); ++ pvh_collect_headers(msg, "1", NULL); ++ } ++ } else if(type & TMCB_REQUEST_FWDED) { ++ msg = params->req; ++ } else if(type & (TMCB_ON_BRANCH_FAILURE | TMCB_RESPONSE_FWDED)) { ++ msg = params->rpl; ++ } else { ++ LM_ERR("unknown callback: %d\n", type); ++ return; ++ } + -+ pv_str_free(&br_xname); ++ if(msg != NULL && msg != FAKED_REPLY) ++ pvh_apply_headers(msg, "1", NULL); + -+ return 1; ++ return; +} + -+int pv_check_header(struct sip_msg *msg, char *hname, char *_s2) ++int handle_msg_cb(struct sip_msg *msg, unsigned int flags, void *cb) +{ -+ str hname_s = STR_NULL; -+ -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; -+ -+ if (pv_xavp_get_child(msg, &xavp_name, &hname_s) == NULL) -+ return -1; ++ int cbs = TMCB_REQUEST_FWDED | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_IN ++ | TMCB_ON_BRANCH_FAILURE; ++ ++ if(flags & (PRE_SCRIPT_CB | REQUEST_CB)) { ++ if(tmb.register_tmcb(msg, 0, cbs, handle_tm_t, 0, 0) <= 0) { ++ LM_ERR("cannot register TM callbacks\n"); ++ return -1; ++ } ++ pvh_collect_headers(msg, "1", NULL); ++ } else { ++ LM_ERR("unknown callback: %d\n", flags); ++ } + -+ return 1; ++ return 1; +} + -+int pv_append_header(struct sip_msg *msg, char *hname, char *hvalue) ++int pvh_collect_headers(struct sip_msg *msg, char *is_auto, char *_s2) +{ -+ str hname_s = STR_NULL, hvalue_s = STR_NULL; ++ struct hdr_field *hf = NULL; ++ str name = STR_NULL; ++ str val = STR_NULL; ++ char hvals[header_name_size][header_value_size]; ++ int idx = 0, d_size = 0; ++ str val_part = STR_NULL; ++ int br_idx; ++ ++ pvh_get_branch_index(msg, &br_idx); ++ LM_DBG("br_idx: %d\n", br_idx); ++ if(!(is_auto && strcmp(is_auto, "1") == 0) ++ && ((msg->first_line.type == SIP_REPLY ++ && isflagset(msg, FL_PV_HDRS_COLLECTED) == 1) ++ || (msg->first_line.type != SIP_REPLY ++ && isbflagset(br_idx, FL_PV_HDRS_COLLECTED) ++ == 1))) { ++ LM_ERR("headers are already collected\n"); ++ return -1; ++ } + -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; ++ if(parse_headers(msg, HDR_EOH_F, 0) < 0) { ++ LM_ERR("error parsing headers\n"); ++ return -1; ++ } + -+ if (hvalue != NULL) -+ if (pv_fixup_svalue(msg, hvalue, &hvalue_s) < 0) -+ return -1; ++ if(pvh_str_new(&name, header_name_size) < 0) ++ goto err; ++ if(pvh_str_new(&val, header_value_size) < 0) ++ goto err; ++ ++ if(name.s == NULL || val.s == NULL) ++ goto err; ++ ++ for(hf = msg->headers; hf; hf = hf->next) { ++ LM_DBG("collect header[%.*s]: %.*s\n", hf->name.len, hf->name.s, ++ hf->body.len, hf->body.s); ++ ++ pvh_str_copy(&val, &hf->body, header_value_size); ++ pvh_str_copy(&name, &hf->name, header_name_size); ++ ++ if(str_hash_get(&split_headers, name.s, name.len) ++ && strchr(val.s, ',') != NULL) { ++ ++ if(pvh_split_values(&val, hvals, &d_size, 1) < 0) { ++ LM_ERR("could not parse Diversion header comma separated " ++ "value"); ++ return -1; ++ } ++ ++ for(idx = 0; idx < d_size; idx++) { ++ val_part.s = hvals[idx]; ++ val_part.len = strlen(hvals[idx]); ++ if(pvh_set_xavp(msg, &xavp_name, &name, &val_part, SR_XTYPE_STR, ++ 0, 1) ++ < 0) ++ goto err; ++ } ++ continue; ++ } ++ if(pvh_set_xavp(msg, &xavp_name, &name, &val, SR_XTYPE_STR, 0, 1) < 0) ++ goto err; ++ } + -+ return pv_set_xavp(msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, 0, 1); -+} ++ if(pvh_set_xavp(msg, &xavp_helper_xname, &xavp_helper_name, &xavp_name, ++ SR_XTYPE_STR, 0, 0) ++ < 0) ++ goto err; + -+int pv_modify_header(struct sip_msg *msg, char *hname, char *hvalue) -+{ -+ str hname_s = STR_NULL, hvalue_s = STR_NULL; ++ pvh_str_free(&name); ++ pvh_str_free(&val); + -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; ++ msg->first_line.type == SIP_REPLY ? setflag(msg, FL_PV_HDRS_COLLECTED) ++ : setbflag(br_idx, FL_PV_HDRS_COLLECTED); + -+ if (hvalue != NULL) -+ if (pv_fixup_svalue(msg, hvalue, &hvalue_s) < 0) -+ return -1; ++ return 1; + -+ return pv_set_xavp(msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, 0, 0); ++err: ++ pvh_str_free(&name); ++ pvh_str_free(&val); ++ return -1; +} + -+int pv_modify_header_idx(struct sip_msg *msg, char *hname, char *hidx, char *hvalue) ++int pvh_apply_headers(struct sip_msg *msg, char *is_auto, char *_s2) +{ -+ str hname_s = STR_NULL, hidx_s = STR_NULL, hvalue_s = STR_NULL; -+ int idx = 0; ++ sr_xavp_t *xavp = NULL; ++ sr_xavp_t *sub = NULL; ++ str display = STR_NULL; ++ str uri = STR_NULL; ++ struct str_hash_table rm_hdrs; ++ int from_cnt = 0, to_cnt = 0; ++ str br_xname = STR_NULL; ++ int br_idx, keys_count; ++ int res = -1; ++ ++ rm_hdrs.size = 0; ++ ++ pvh_get_branch_index(msg, &br_idx); ++ ++ if(!(is_auto && strcmp(is_auto, "1") == 0) ++ && ((msg->first_line.type == SIP_REPLY ++ && isflagset(msg, FL_PV_HDRS_APPLIED) == 1) ++ || (msg->first_line.type != SIP_REPLY ++ && isbflagset(br_idx, FL_PV_HDRS_APPLIED) ++ == 1))) { ++ LM_ERR("headers are already applied\n"); ++ return -1; ++ } ++ ++ if(parse_headers(msg, HDR_EOH_F, 0) < 0) { ++ LM_ERR("error parsing headers\n"); ++ return -1; ++ } ++ ++ if(pvh_str_new(&display, header_value_size) < 0) ++ goto err; ++ if(pvh_str_new(&uri, header_value_size) < 0) ++ goto err; ++ if(pvh_str_new(&br_xname, header_value_size) < 0) ++ goto err; + -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; ++ pvh_get_branch_xname(msg, &xavp_name, &br_xname); + -+ if (hidx == NULL || pv_fixup_svalue(msg, hidx, &hidx_s) < 0) -+ return -1; -+ idx = atoi(hidx_s.s); ++ if((xavp = xavp_get(&br_xname, NULL)) == NULL ++ && (xavp = xavp_get(&xavp_name, NULL)) == NULL) { ++ LM_ERR("missing xavp %s, run pv_collect_headers() first\n", ++ xavp_name.s); ++ goto err; ++ } ++ if(xavp->val.type != SR_XTYPE_XAVP) { ++ LM_ERR("not xavp child type %s\n", xavp_name.s); ++ goto err; ++ } + -+ if (hvalue != NULL) -+ if (pv_fixup_svalue(msg, hvalue, &hvalue_s) < 0) -+ return -1; ++ if((sub = xavp->val.v.xavp) == NULL) { ++ LM_ERR("invalid xavp structure: %s\n", xavp_name.s); ++ goto err; ++ } ++ keys_count = pvh_xavp_keys_count(&sub); ++ if(str_hash_alloc(&rm_hdrs, keys_count) < 0) { ++ PKG_MEM_ERROR; ++ goto err; ++ } ++ LM_DBG("xavp->name:%.*s br_xname:%.*s keys_count: %d\n", xavp->name.len, ++ xavp->name.s, br_xname.len, br_xname.s, keys_count); ++ str_hash_init(&rm_hdrs); ++ ++ do { ++ if(pvh_skip_header(&sub->name)) ++ continue; ++ ++ if(strncasecmp(sub->name.s, "From", sub->name.len) == 0) { ++ if(msg->first_line.type == SIP_REPLY ++ || msg->first_line.u.request.method_value == METHOD_ACK ++ || msg->first_line.u.request.method_value == METHOD_PRACK ++ || msg->first_line.u.request.method_value == METHOD_BYE) { ++ LM_DBG("skip From header change in reply messages\n"); ++ continue; ++ } ++ if(strncmp(sub->val.v.s.s, msg->from->body.s, sub->val.v.s.len) ++ == 0) { ++ LM_DBG("skip unchanged From header\n"); ++ continue; ++ } ++ if(from_cnt > 0) ++ continue; ++ ++ memset(display.s, 0, header_value_size); ++ memset(uri.s, 0, header_value_size); ++ ++ if(pvh_extract_display_uri(sub->val.v.s.s, &display, &uri) < 0) { ++ LM_ERR("error parsing From header\n"); ++ goto err; ++ } ++ ++ if(uac.replace_from != NULL) { ++ LM_DBG("replace_from[%s]: %s %s\n", sub->name.s, display.s, ++ uri.s); ++ if(display.len == 0) ++ pvh_real_hdr_remove_display(msg, &sub->name); ++ uac.replace_from(msg, &display, &uri); ++ } ++ ++ from_cnt++; ++ continue; ++ } ++ ++ if(strncasecmp(sub->name.s, "To", sub->name.len) == 0) { ++ if(msg->first_line.type == SIP_REPLY ++ || msg->first_line.u.request.method_value == METHOD_ACK ++ || msg->first_line.u.request.method_value == METHOD_PRACK ++ || msg->first_line.u.request.method_value == METHOD_BYE) { ++ LM_DBG("skip To header change in reply messages\n"); ++ continue; ++ } ++ if(strncmp(sub->val.v.s.s, msg->to->body.s, sub->val.v.s.len) ++ == 0) { ++ LM_DBG("skip unchanged To header\n"); ++ continue; ++ } ++ if(to_cnt > 0) ++ continue; ++ ++ memset(display.s, 0, header_value_size); ++ memset(uri.s, 0, header_value_size); ++ ++ if(pvh_extract_display_uri(sub->val.v.s.s, &display, &uri) < 0) { ++ LM_ERR("error parsing To header\n"); ++ goto err; ++ } ++ ++ if(uac.replace_to != NULL) { ++ LM_DBG("replace_to[%s]: %s %s\n", sub->name.s, display.s, ++ uri.s); ++ if(display.len == 0) ++ pvh_real_hdr_remove_display(msg, &sub->name); ++ uac.replace_to(msg, &display, &uri); ++ } ++ ++ to_cnt++; ++ continue; ++ } ++ ++ if(strncasecmp(sub->name.s, "@Reply-Reason", sub->name.len) == 0) { ++ if(str_hash_get(&rm_hdrs, sub->name.s, sub->name.len)) ++ continue; ++ pvh_real_replace_reply_reason(msg, &sub->val.v.s); ++ pvh_str_hash_add_key(&rm_hdrs, &sub->name); ++ continue; ++ } ++ ++ if(!str_hash_get(&rm_hdrs, sub->name.s, sub->name.len)) { ++ if(!pvh_xavp_is_null(sub) && xavp_count(&sub->name, &sub) == 1) { ++ LM_DBG("replace header[%s]: %s\n", sub->name.s, sub->val.v.s.s); ++ pvh_real_hdr_replace(msg, &sub->name, &sub->val.v.s); ++ pvh_str_hash_add_key(&rm_hdrs, &sub->name); ++ continue; ++ } ++ LM_DBG("remove header[%s]: %s\n", sub->name.s, sub->val.v.s.s); ++ pvh_real_hdr_del_by_name(msg, &sub->name); ++ pvh_str_hash_add_key(&rm_hdrs, &sub->name); ++ } ++ ++ if(!pvh_xavp_is_null(sub) && !pvh_single_header(&sub->name)) { ++ pvh_real_hdr_append(msg, &sub->name, &sub->val.v.s); ++ LM_DBG("append header[%s]: %s\n", sub->name.s, sub->val.v.s.s); ++ } ++ } while((sub = sub->next) != NULL); ++ ++ msg->first_line.type == SIP_REPLY ? setflag(msg, FL_PV_HDRS_APPLIED) ++ : setbflag(br_idx, FL_PV_HDRS_APPLIED); ++ ++ res = 1; + -+ return pv_set_xavp(msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, idx, 0); ++err: ++ pvh_str_free(&display); ++ pvh_str_free(&uri); ++ pvh_str_free(&br_xname); ++ if(rm_hdrs.size) ++ pvh_str_hash_free(&rm_hdrs); ++ return res; +} + -+int pv_remove_header(struct sip_msg *msg, char *hname, char *_s2) ++int pvh_reset_headers(struct sip_msg *msg, char *_s1, char *_s2) +{ -+ str hname_s = STR_NULL; -+ sr_xavp_t *avp = NULL; -+ int idx = 0; -+ int count = 0; ++ str br_xname = STR_NULL; ++ int br_idx; ++ ++ if(pvh_str_new(&br_xname, header_name_size) < 0) ++ return -1; + -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; ++ pvh_get_branch_index(msg, &br_idx); ++ pvh_get_branch_xname(msg, &xavp_name, &br_xname); + -+ if ((avp = pv_xavp_get_child(msg, &xavp_name, &hname_s)) == NULL) -+ return 1; ++ pvh_free_xavp(&br_xname); ++ pvh_get_branch_xname(msg, &xavp_parsed_xname, &br_xname); ++ pvh_free_xavp(&br_xname); + -+ count = xavp_count(&hname_s, &avp); ++ if(msg->first_line.type == SIP_REPLY) { ++ resetflag(msg, FL_PV_HDRS_COLLECTED); ++ resetflag(msg, FL_PV_HDRS_APPLIED); ++ } else { ++ resetbflag(br_idx, FL_PV_HDRS_COLLECTED); ++ resetbflag(br_idx, FL_PV_HDRS_APPLIED); ++ } + -+ while (idx < count) { -+ if (pv_set_xavp(msg, &xavp_name, &hname_s, NULL, SR_XTYPE_STR, idx++, 0) < 1) -+ return -1; -+ } ++ pvh_str_free(&br_xname); + -+ return 1; ++ return 1; +} + -+int pv_remove_header_idx(struct sip_msg *msg, char *hname, char *hidx) ++int pvh_check_header(struct sip_msg *msg, char *hname, char *_s2) +{ -+ str hname_s = STR_NULL; -+ str hidx_s = STR_NULL; -+ int idx = 0; ++ str hname_s = STR_NULL; + -+ if (hname == NULL || pv_fixup_svalue(msg, hname, &hname_s) < 0) -+ return -1; ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+ if (hidx == NULL || pv_fixup_svalue(msg, hidx, &hidx_s) < 0) -+ return -1; -+ idx = atoi(hidx_s.s); ++ if(pvh_xavp_get_child(msg, &xavp_name, &hname_s) == NULL) ++ return -1; + -+ return pv_set_xavp(msg, &xavp_name, &hname_s, NULL, SR_XTYPE_STR, idx, 0); ++ return 1; +} + -+int pv_set_xavp(struct sip_msg *msg, str *xname, str *name, void *data, sr_xtype_t type, int idx, int append) ++int pvh_append_header(struct sip_msg *msg, char *hname, char *hvalue) +{ -+ sr_xavp_t **xavp = NULL; -+ sr_xavp_t *root = NULL; -+ sr_xval_t root_xval; -+ sr_xval_t xval; -+ str br_xname = STR_NULL; -+ int br_idx; -+ -+ if (xname == NULL || name == NULL) { -+ LM_ERR("missing xavp/pv name\n"); -+ return -1; -+ } -+ -+ pv_get_branch_index(msg, &br_idx); -+ if (pv_str_new(&br_xname, header_name_size) < 0) -+ return -1; -+ pv_get_branch_xname(msg, xname, &br_xname); -+ LM_DBG("br_xname: %.*s name: %.*s\n", -+ br_xname.len, br_xname.s, name->len, name->s); -+ memset(&xval, 0, sizeof(sr_xval_t)); -+ if (data == NULL || SR_XTYPE_NULL) { -+ xval.type = SR_XTYPE_NULL; -+ } else if (type == SR_XTYPE_STR) { -+ xval.type = SR_XTYPE_STR; -+ xval.v.s = *(str*)data; -+ } else if (type == SR_XTYPE_DATA) { -+ xval.type = SR_XTYPE_DATA; -+ xval.v.data = (sr_data_t*)shm_malloc(sizeof(sr_data_t)); -+ if (xval.v.data == NULL) { -+ LM_ERR("out of shared memory\n"); -+ goto err; -+ } -+ memset(xval.v.data, 0, sizeof(sr_data_t)); -+ xval.v.data->p = data; -+ xval.v.data->pfree = pv_xavp_free_data; -+ } -+ -+ root = xavp_get(&br_xname, NULL); -+ -+ if (root == NULL && br_idx > 0) { -+ pv_clone_branch_xavp(msg, xname); -+ root = xavp_get(&br_xname, NULL); -+ } -+ -+ xavp = root ? &root->val.v.xavp : &root; -+ -+ if (root == NULL) { -+ append = 1; -+ memset(&root_xval, 0, sizeof(sr_xval_t)); -+ root_xval.type = SR_XTYPE_XAVP; -+ root_xval.v.xavp = NULL; -+ -+ if ((root = xavp_add_value(&br_xname, &root_xval, NULL)) == NULL) { -+ LM_ERR("error create xavp %s\n", br_xname.s); -+ goto err; -+ } -+ xavp = &root->val.v.xavp; -+ } else if (xavp_get_child(&br_xname, name) == NULL) { -+ append = 1; -+ } -+ -+ if (append) { -+ if (pv_xavp_append_value(name, &xval, xavp) < 0) { -+ LM_ERR("error append xavp=>name %s=>%.*s\n", br_xname.s, name->len, name->s); -+ goto err; -+ } -+ } else { -+ if (pv_xavp_set_value(name, &xval, idx, xavp) < 0) { -+ LM_ERR("error modify xavp=>name %s=>%.*s idx=%d\n", br_xname.s, name->len, name->s, idx); -+ goto err; -+ } -+ } -+ -+ pv_str_free(&br_xname); -+ return 1; ++ str hname_s = STR_NULL, hvalue_s = STR_NULL; + -+err: -+ pv_str_free(&br_xname); -+ return -1; -+} ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+int pv_free_xavp(str *xname) -+{ -+ sr_xavp_t *xavp = NULL; -+ xavp_rm_by_name(xname, 1, NULL); -+ if ((xavp = xavp_get(xname, NULL)) != NULL) -+ xavp_rm(xavp, NULL); -+ return 1; -+} ++ if(hvalue != NULL) ++ if(pvh_fixup_svalue(msg, hvalue, &hvalue_s) < 0) ++ return -1; + -+int pv_fixup_svalue(struct sip_msg *msg, char *src, str *dst) -+{ -+ if (fixup_get_svalue(msg, (gparam_p)src, dst) < 0) { -+ LM_ERR("unable to parse param value\n"); -+ return -1; -+ } -+ return 1; ++ return pvh_set_xavp( ++ msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, 0, 1); +} + -+int pv_real_hdr_append(struct sip_msg *msg, str *hname, str *hvalue) ++int pvh_modify_header(struct sip_msg *msg, char *hname, char *hvalue) +{ -+ struct lump *anchor = NULL; -+ hdr_field_t *hf = NULL; -+ hdr_field_t *m_hf = NULL; -+ str new_h; ++ str hname_s = STR_NULL, hvalue_s = STR_NULL; + -+ if (hname->s == NULL || hvalue->s == NULL) { -+ LM_ERR("header name/value cannot be empty"); -+ return -1; -+ } ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+ // find last header matching the name -+ for (hf=msg->headers; hf; hf=hf->next) { -+ if (hf->name.len == hname->len && -+ strncasecmp(hf->name.s, hname->s, hname->len) == 0) { -+ m_hf = hf; -+ } -+ if (!hf->next) -+ break; -+ } -+ -+ if (m_hf == NULL) -+ anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); -+ else -+ anchor = anchor_lump(msg, m_hf->name.s + m_hf->len - msg->buf, 0, 0); -+ -+ if (anchor == 0) { -+ LM_ERR("unable to find header lump\n"); -+ return -1; -+ } ++ if(hvalue != NULL) ++ if(pvh_fixup_svalue(msg, hvalue, &hvalue_s) < 0) ++ return -1; + -+ if (pv_create_hdr_str(hname, hvalue, &new_h) <= 0) -+ return -1; ++ return pvh_set_xavp( ++ msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, 0, 0); ++} + -+ if (insert_new_lump_after(anchor, new_h.s, new_h.len, 0) == 0) { -+ LM_ERR("cannot insert header lump\n"); -+ pkg_free(new_h.s); -+ return -1; -+ } ++int pvh_modify_header_idx( ++ struct sip_msg *msg, char *hname, char *hidx, char *hvalue) ++{ ++ str hname_s = STR_NULL, hidx_s = STR_NULL, hvalue_s = STR_NULL; ++ int idx = 0; + -+ LM_DBG("append header: %.*s\n", new_h.len, new_h.s); ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+ return 1; -+} ++ if(hidx == NULL || pvh_fixup_svalue(msg, hidx, &hidx_s) < 0) ++ return -1; ++ idx = atoi(hidx_s.s); + -+int pv_real_hdr_replace(struct sip_msg *msg, str *hname, str *hvalue) -+{ -+ struct lump *anchor = NULL; -+ hdr_field_t *hf = NULL; -+ str new_h; -+ int new = 1; -+ -+ if (hname->s == NULL || hvalue->s == NULL) { -+ LM_ERR("header name/value cannot be empty"); -+ return -1; -+ } -+ -+ for (hf=msg->headers; hf; hf=hf->next) { -+ if (hf->name.len == hname->len && -+ strncasecmp(hf->name.s, hname->s, hname->len) == 0) { -+ if (hf->body.len == hvalue->len && -+ strncasecmp(hf->body.s, hvalue->s, hvalue->len) == 0) { -+ return 1; -+ } -+ new = 0; -+ break; -+ } -+ if (!hf->next) -+ break; -+ } -+ -+ if (hf == NULL) { -+ LM_ERR("unable to find header lump\n"); -+ return -1; -+ } -+ -+ if (new == 0) { -+ if ((anchor = del_lump(msg, hf->name.s - msg->buf, hf->len, 0)) == 0) { -+ LM_ERR("unable to delete header lump\n"); -+ return -1; -+ } -+ } else { -+ anchor = anchor_lump(msg, hf->name.s + hf->len - msg->buf, 0, 0); -+ } -+ -+ if (anchor == 0) { -+ LM_ERR("unable to find header lump\n"); -+ return -1; -+ } -+ -+ if (pv_create_hdr_str(hname, hvalue, &new_h) <= 0) -+ return -1; -+ -+ if (insert_new_lump_after(anchor, new_h.s, new_h.len, 0) == 0) { -+ LM_ERR("cannot insert header lump\n"); -+ pkg_free(new_h.s); -+ return -1; -+ } -+ -+ LM_DBG("%s header: %.*s\n", new ? "append" : "replace", -+ new_h.len, new_h.s); -+ -+ return 1; -+} ++ if(hvalue != NULL) ++ if(pvh_fixup_svalue(msg, hvalue, &hvalue_s) < 0) ++ return -1; + -+int pv_real_hdr_del_by_name(struct sip_msg *msg, str *hname) -+{ -+ hdr_field_t *hf = NULL; -+ -+ for (hf=msg->headers; hf; hf=hf->next) { -+ if (hf->name.len==hname->len -+ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { -+ LM_DBG("remove header[%.*s]: %.*s\n", -+ hf->name.len, hf->name.s, hf->body.len, hf->body.s); -+ del_lump(msg, hf->name.s-msg->buf, hf->len, 0); -+ } -+ } -+ return 1; ++ return pvh_set_xavp( ++ msg, &xavp_name, &hname_s, &hvalue_s, SR_XTYPE_STR, idx, 0); +} + -+int pv_real_hdr_remove_display(struct sip_msg *msg, str *hname) ++int pvh_remove_header(struct sip_msg *msg, char *hname, char *_s2) +{ -+ hdr_field_t *hf = NULL; -+ struct to_body *d_hf = NULL; -+ int disp_len = 0; -+ -+ for (hf=msg->headers; hf; hf=hf->next) { -+ if (hf->name.len==hname->len -+ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { -+ d_hf = (struct to_body*)hf->parsed; -+ if ((disp_len = d_hf->display.len) > 0) { -+ LM_DBG("remove display[%.*s]: %.*s\n", -+ hf->name.len, hf->name.s, disp_len, d_hf->display.s); -+ if (strncmp(d_hf->display.s+disp_len, " ", 1) == 0) -+ disp_len++; -+ del_lump(msg, d_hf->display.s-msg->buf, disp_len, 0); -+ } -+ } -+ } -+ return 1; -+} ++ str hname_s = STR_NULL; ++ sr_xavp_t *avp = NULL; ++ int idx = 0; ++ int count = 0; + -+int pv_real_replace_reply_reason(struct sip_msg *msg, str *value) -+{ -+ struct lump *anchor = NULL; -+ char *reason = NULL; -+ -+ anchor = del_lump(msg, msg->first_line.u.reply.reason.s - msg->buf, -+ msg->first_line.u.reply.reason.len, 0); -+ if (!anchor) { -+ LM_ERR("set reply: failed to del lump\n"); -+ goto err; -+ } -+ -+ reason = (char*)pkg_malloc(value->len); -+ if (reason == NULL) { -+ LM_ERR("set reply: out of pkg memory\n"); -+ goto err; -+ } -+ memcpy(reason, value->s, value->len); -+ -+ if (insert_new_lump_after(anchor, reason, value->len, 0) == 0) { -+ LM_ERR("set reply: failed to add lump: %.*s\n", value->len, value->s); -+ goto err; -+ } -+ -+ return 1; ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+err: -+ if (reason) -+ pkg_free(reason); -+ return -1; -+} ++ if((avp = pvh_xavp_get_child(msg, &xavp_name, &hname_s)) == NULL) ++ return 1; + -+int pv_create_hdr_str(str *hname, str *hvalue, str *dst) -+{ -+ int os; -+ if (hname->s == NULL || hvalue->s == NULL) { -+ LM_ERR("header name/value cannot be empty"); -+ return -1; -+ } -+ -+ if (dst == NULL) { -+ LM_ERR("new header str cannot be null"); -+ return -1; -+ } -+ -+ dst->len = hname->len + 2 + hvalue->len + CRLF_LEN; -+ dst->s = (char*)pkg_malloc(dst->len+1); -+ if (dst->s == NULL) { -+ LM_ERR("out of pkg memory\n"); -+ return -1; -+ } -+ memset(dst->s, 0, dst->len+1); -+ -+ os = 0; -+ memcpy(dst->s, hname->s, hname->len); os+=hname->len; -+ memcpy(dst->s+os, ": ", 2); os+=2; -+ memcpy(dst->s+os, hvalue->s, hvalue->len); os+=hvalue->len; -+ memcpy(dst->s+os, CRLF, CRLF_LEN); os+=CRLF_LEN; -+ dst->s[dst->len] = '\0'; -+ -+ return 1; -+} ++ count = xavp_count(&hname_s, &avp); + -+int pv_str_new(str *s, int size) -+{ -+ s->s = (char*)pkg_malloc(size); -+ if(s->s == NULL) { -+ LM_ERR("out of pkg memory\n"); -+ return -1; -+ } -+ memset(s->s, 0, size); -+ s->len = 0; -+ -+ return 1; -+} ++ while(idx < count) { ++ if(pvh_set_xavp(msg, &xavp_name, &hname_s, NULL, SR_XTYPE_STR, idx++, 0) ++ < 1) ++ return -1; ++ } + -+int pv_str_free(str *s) -+{ -+ if (s->s) -+ pkg_free(s->s); -+ s->s = NULL; -+ return 1; ++ return 1; +} + -+int pv_str_copy(str *dst, str *src, unsigned int max_size) ++int pvh_remove_header_idx(struct sip_msg *msg, char *hname, char *hidx) +{ -+ unsigned int src_len = src->len+1 >= max_size ? max_size-1 : src->len; ++ str hname_s = STR_NULL; ++ str hidx_s = STR_NULL; ++ int idx = 0; + -+ if (src == NULL || dst == NULL || src->len <= 0) -+ return -1; ++ if(hname == NULL || pvh_fixup_svalue(msg, hname, &hname_s) < 0) ++ return -1; + -+ memset(dst->s, 0, dst->len); -+ memcpy(dst->s, src->s, src_len); -+ dst->s[src_len] = '\0'; -+ dst->len = src_len; ++ if(hidx == NULL || pvh_fixup_svalue(msg, hidx, &hidx_s) < 0) ++ return -1; ++ idx = atoi(hidx_s.s); + -+ return 1; ++ return pvh_set_xavp(msg, &xavp_name, &hname_s, NULL, SR_XTYPE_STR, idx, 0); +} + -+int pv_extract_display_uri(char *suri, str *display, str *duri) -+{ -+ char *ptr_a = NULL; -+ char *ptr_b = NULL; -+ int display_len = 0; -+ int uri_len = 0; -+ -+ if (suri == NULL || strlen(suri) == 0) -+ return -1; -+ -+ ptr_a = strchr(suri, '<'); -+ ptr_b = strchr(suri, '>'); -+ -+ if (ptr_a == NULL && ptr_b == NULL) { -+ ptr_a = suri; -+ uri_len = strlen(suri); -+ } else if (ptr_a == NULL || ptr_b == NULL) { -+ return -1; -+ } else { -+ display_len = ptr_a-suri; -+ ptr_a++; -+ uri_len = ptr_b-ptr_a; -+ } -+ -+ if (uri_len <= 0) -+ return -1; -+ -+ if (display_len > 0) { -+ memcpy(display->s, suri, display_len); -+ display->len = strlen(display->s); -+ display->s[display->len] = '\0'; -+ } else { -+ display->len = 0; -+ } -+ -+ memcpy(duri->s, ptr_a, uri_len); -+ duri->len = strlen(duri->s); -+ duri->s[duri->len] = '\0'; -+ -+ return 1; -+} + -+int pv_split_values(str *s, char d[][header_value_size], int *d_size, int keep_spaces) ++int pvh_fixup_svalue(struct sip_msg *msg, char *src, str *dst) +{ -+ char p; -+ int idx = 0, c_idx = 0; -+ -+ *d_size = -1; -+ -+ if (s == NULL || s->len == 0 || d == NULL) { -+ *d_size = 0; -+ return 1; -+ } -+ -+ while (idx < s->len) { -+ strncpy(&p, s->s+idx++,1); -+ if (keep_spaces == 0 && strncmp(&p, " ", 1) == 0) -+ continue; -+ if (strncmp(&p, ",", 1) == 0) { -+ if (c_idx == 0) -+ continue; -+ if (c_idx+1 < header_value_size) -+ c_idx++; -+ d[*d_size][c_idx] = '\0'; -+ c_idx = 0; -+ continue; -+ } -+ if (c_idx == 0) -+ (*d_size)++; -+ strncpy(&d[*d_size][c_idx++], &p, 1); -+ } -+ -+ if (c_idx > 0) { -+ if (c_idx >= header_value_size) -+ c_idx--; -+ d[*d_size][c_idx] = '\0'; -+ } -+ -+ (*d_size)++; -+ -+ return 1; ++ if(fixup_get_svalue(msg, (gparam_p)src, dst) < 0) { ++ LM_ERR("unable to parse param value\n"); ++ return -1; ++ } ++ return 1; +} +diff --git a/src/modules/pv_headers/pv_headers.h b/src/modules/pv_headers/pv_headers.h +new file mode 100644 +index 0000000..e694f36 +--- /dev/null ++++ b/src/modules/pv_headers/pv_headers.h +@@ -0,0 +1,42 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 ++ * ++ */ + -+sr_xavp_t * pv_xavp_new_value(str *name, sr_xval_t *val) -+{ -+ sr_xavp_t *avp = NULL; -+ int size; -+ unsigned int id; -+ -+ if (name==NULL || name->s==NULL || val==NULL) -+ return NULL; -+ id = get_hash1_raw(name->s, name->len); -+ -+ size = sizeof(sr_xavp_t) + name->len + 1; -+ if (val->type == SR_XTYPE_STR) -+ size += val->v.s.len + 1; -+ avp = (sr_xavp_t*)shm_malloc(size); -+ if (avp == NULL) { -+ LM_ERR("out of shared memory\n"); -+ return NULL; -+ } -+ memset(avp, 0, size); -+ avp->id = id; -+ avp->name.s = (char*)avp + sizeof(sr_xavp_t); -+ memcpy(avp->name.s, name->s, name->len); -+ avp->name.s[name->len] = '\0'; -+ avp->name.len = name->len; -+ memcpy(&avp->val, val, sizeof(sr_xval_t)); -+ if (val->type == SR_XTYPE_STR) { -+ avp->val.v.s.s = avp->name.s + avp->name.len + 1; -+ memcpy(avp->val.v.s.s, val->v.s.s, val->v.s.len); -+ avp->val.v.s.s[val->v.s.len] = '\0'; -+ avp->val.v.s.len = val->v.s.len; -+ } -+ -+ return avp; -+} ++#ifndef PV_HEADERS_H ++#define PV_HEADERS_H + -+int pv_xavp_append_value(str *name, sr_xval_t *val, sr_xavp_t **start) ++#include "../../core/parser/parse_addr_spec.h" ++ ++typedef struct _xavp_c_data +{ -+ sr_xavp_t *last = NULL; -+ sr_xavp_t *xavp = NULL; ++ struct to_body to_b; ++ struct to_param *to_params; ++ str value; ++} xavp_c_data_t; + -+ if ((xavp = pv_xavp_new_value(name, val)) == NULL) -+ return -1; ++extern str xavp_name; ++extern str xavp_parsed_xname; + -+ if (*start == NULL) { -+ xavp->next = *start; -+ *start = xavp; -+ return 1; -+ } ++extern unsigned int header_name_size; ++extern unsigned int header_value_size; + -+ last = *start; -+ while (last->next) -+ last = last->next; -+ last->next = xavp; ++#endif /* PV_HEADERS_H */ +diff --git a/src/modules/pv_headers/pvh_hash.c b/src/modules/pv_headers/pvh_hash.c +new file mode 100644 +index 0000000..f269868 +--- /dev/null ++++ b/src/modules/pv_headers/pvh_hash.c +@@ -0,0 +1,132 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 ++ * ++ */ + -+ return 1; -+} ++#include "pv_headers.h" ++#include "pvh_hash.h" ++#include "pvh_str.h" + -+int pv_xavp_set_value(str *name, sr_xval_t *val, int idx, sr_xavp_t **start) -+{ -+ int cnt = 0; -+ -+ if (idx < 0) { -+ cnt = xavp_count(name, start); -+ idx = idx + cnt; -+ if (idx < 0) -+ return -1; -+ } -+ LM_DBG("xavp name: %.s\n", name->len, name->s); -+ if (xavp_set_value(name, idx, val, start) == NULL) -+ return -1; -+ -+ return 1; -+} ++struct str_hash_table skip_headers; ++struct str_hash_table split_headers; ++struct str_hash_table single_headers; + -+sr_xval_t * pv_xavp_get_value(struct sip_msg *msg, str *xname, str *name, int idx) ++int pvh_str_hash_init(struct str_hash_table *ht, str *keys, char *desc) +{ -+ sr_xavp_t *xavp = NULL; -+ sr_xavp_t *sub = NULL; -+ str br_xname = STR_NULL; -+ -+ if (pv_str_new(&br_xname, header_name_size) < 0) -+ return NULL; -+ -+ pv_get_branch_xname(msg, xname, &br_xname); -+ if ((xavp = xavp_get(&br_xname, NULL)) == NULL && -+ (xavp = xavp_get(xname, NULL)) == NULL) { -+ goto err; -+ } -+ -+ if (xavp->val.type != SR_XTYPE_XAVP) { -+ LM_ERR("not xavp child type %s\n" , br_xname.s); -+ goto err; -+ } ++ char split[header_name_size][header_value_size]; ++ int idx = 0, d_size = 0; ++ str val = STR_NULL; + -+ sub = xavp_get_by_index(name, idx, &xavp->val.v.xavp); ++ if(pvh_split_values(keys, split, &d_size, 0) < 0) { ++ LM_ERR("could not parse %s param\n", desc); ++ return -1; ++ } + -+ pv_str_free(&br_xname); -+ return sub ? &sub->val : NULL; ++ if(str_hash_alloc(ht, d_size + 1) < 0) { ++ PKG_MEM_ERROR; ++ return -1; ++ } ++ str_hash_init(ht); ++ ++ for(idx = 0; idx < d_size; idx++) { ++ val.s = split[idx]; ++ val.len = strlen(split[idx]); ++ if(pvh_str_hash_add_key(ht, &val) < 0) { ++ LM_ERR("cannot add a hash key=>%s", desc); ++ return -1; ++ } ++ } + -+err: -+ pv_str_free(&br_xname); -+ return NULL; ++ return 1; +} + -+sr_xavp_t * pv_xavp_get_child(struct sip_msg *msg, str *xname, str *name) ++int pvh_str_hash_add_key(struct str_hash_table *ht, str *key) +{ -+ sr_xavp_t *xavp = NULL; -+ str br_xname = STR_NULL; ++ struct str_hash_entry *e = NULL; ++ int e_size; + -+ if (pv_str_new(&br_xname, header_name_size) < 0) -+ return NULL; ++ if(ht->table == NULL || key == NULL || key->len == 0) ++ return -1; + -+ pv_get_branch_xname(msg, xname, &br_xname); -+ xavp = xavp_get_child(&br_xname, name); -+ if (xavp == NULL) -+ xavp = xavp_get_child(xname, name); -+ -+ pv_str_free(&br_xname); -+ return xavp; -+} -+ -+int pv_xavp_is_null(sr_xavp_t * avp) -+{ -+ if (avp == NULL) -+ return 1; ++ e_size = sizeof(struct str_hash_entry) + sizeof(char) * key->len; ++ e = pkg_malloc(e_size); ++ if(e == NULL) { ++ PKG_MEM_ERROR; ++ return -1; ++ } ++ memset(e, 0, e_size); + -+ if (avp->val.type == SR_XTYPE_NULL || -+ (avp->val.type == SR_XTYPE_STR && -+ (strncasecmp(avp->val.v.s.s, "NULL", 4) == 0))) { -+ return 1; -+ } ++ if(pvh_str_new(&e->key, key->len + 1) < 0) ++ goto err; ++ pvh_str_copy(&e->key, key, key->len + 1); + -+ return 0; -+} ++ str_hash_add(ht, e); ++ return 1; + -+void pv_xavp_free_data(void *p, sr_xavp_sfree_f sfree) -+{ -+ xavp_c_data_t *c_data = NULL; -+ -+ if ((c_data = (xavp_c_data_t*)p) != NULL) { -+ pv_free_to_params(c_data->to_params, sfree); -+ sfree(c_data->value.s); -+ c_data->value.s = NULL; -+ sfree(c_data); -+ c_data = NULL; -+ } ++err: ++ pvh_str_free(&e->key); ++ return -1; +} + -+int pv_skip_header(str *hname) ++int pvh_str_hash_free(struct str_hash_table *ht) +{ -+ if (hname == NULL) -+ return 0; -+ -+ if (str_hash_get(&skip_headers, hname->s, hname->len)) -+ return 1; ++ struct str_hash_entry *e = NULL; ++ struct str_hash_entry *bak = NULL; ++ int r; ++ ++ if(ht == NULL) ++ return -1; ++ ++ if(ht->table) { ++ for(r = 0; r < ht->size; r++) { ++ clist_foreach_safe(&ht->table[r], e, bak, next) ++ { ++ pvh_str_free(&e->key); ++ pkg_free(e); ++ } ++ } ++ pkg_free(ht->table); ++ } + -+ return 0; ++ return 1; +} + -+int pv_single_header(str *hname) ++int pvh_skip_header(str *hname) +{ -+ if (hname == NULL) -+ return 0; ++ if(hname == NULL) ++ return 0; + -+ if (str_hash_get(&single_headers, hname->s, hname->len)) -+ return 1; ++ if(str_hash_get(&skip_headers, hname->s, hname->len)) ++ return 1; + -+ return 0; ++ return 0; +} + -+int pv_xavp_keys_count(sr_xavp_t **start) ++int pvh_single_header(str *hname) +{ -+ sr_xavp_t *xavp = NULL; -+ int cnt = 0; -+ -+ if (*start == NULL) -+ return 0; -+ -+ xavp = *start; ++ if(hname == NULL) ++ return 0; + -+ while (xavp) { -+ cnt++; -+ xavp = xavp->next; -+ } ++ if(str_hash_get(&single_headers, hname->s, hname->len)) ++ return 1; + -+ return cnt; ++ return 0; +} -+ -+int pv_str_hash_init(struct str_hash_table *ht, str *keys, char *desc) -+{ -+ char split[header_name_size][header_value_size]; -+ int idx = 0, d_size = 0; -+ str val = STR_NULL; -+ -+ if (pv_split_values(keys, split, &d_size, 0) < 0) { -+ LM_ERR("could not parse %s param\n", desc); -+ return -1; -+ } -+ -+ if (str_hash_alloc(ht, d_size+1) < 0) { -+ LM_ERR("memory allocation error\n"); -+ return -1; -+ } -+ str_hash_init(ht); -+ -+ for (idx=0;idx%s", desc); -+ return -1; -+ } -+ } -+ -+ return 1; -+} -+ -+int pv_str_hash_add_key(struct str_hash_table *ht, str *key) +diff --git a/src/modules/pv_headers/pvh_hash.h b/src/modules/pv_headers/pvh_hash.h +new file mode 100644 +index 0000000..8828cc6 +--- /dev/null ++++ b/src/modules/pv_headers/pvh_hash.h +@@ -0,0 +1,39 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 PVH_HASH_H ++#define PVH_HASH_H ++ ++#include "../../core/str_hash.h" ++ ++extern struct str_hash_table skip_headers; ++extern struct str_hash_table split_headers; ++extern struct str_hash_table single_headers; ++ ++int pvh_str_hash_init(struct str_hash_table *ht, str *keys, char *desc); ++int pvh_str_hash_add_key(struct str_hash_table *ht, str *key); ++int pvh_str_hash_free(struct str_hash_table *ht); ++int pvh_skip_header(str *hname); ++int pvh_single_header(str *hname); ++ ++#endif /* PVH_HASH_H */ +diff --git a/src/modules/pv_headers/pvh_hdr.c b/src/modules/pv_headers/pvh_hdr.c +new file mode 100644 +index 0000000..70be9ac +--- /dev/null ++++ b/src/modules/pv_headers/pvh_hdr.c +@@ -0,0 +1,235 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 "../../core/data_lump.h" ++ ++#include "pvh_hdr.h" ++ ++int pvh_real_hdr_append(struct sip_msg *msg, str *hname, str *hvalue) +{ -+ struct str_hash_entry *e = NULL; -+ int e_size; ++ struct lump *anchor = NULL; ++ hdr_field_t *hf = NULL; ++ hdr_field_t *m_hf = NULL; ++ str new_h; ++ ++ if(hname->s == NULL || hvalue->s == NULL) { ++ LM_ERR("header name/value cannot be empty"); ++ return -1; ++ } ++ ++ // find last header matching the name ++ for(hf = msg->headers; hf; hf = hf->next) { ++ if(hf->name.len == hname->len ++ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { ++ m_hf = hf; ++ } ++ if(!hf->next) ++ break; ++ } ++ ++ if(m_hf == NULL) ++ anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); ++ else ++ anchor = anchor_lump(msg, m_hf->name.s + m_hf->len - msg->buf, 0, 0); + -+ if (ht->table == NULL || key == NULL || key->len == 0) -+ return -1; ++ if(anchor == 0) { ++ LM_ERR("unable to find header lump\n"); ++ return -1; ++ } ++ ++ if(pvh_create_hdr_str(hname, hvalue, &new_h) <= 0) ++ return -1; + -+ e_size = sizeof(struct str_hash_entry)+sizeof(char)*key->len; -+ e = pkg_malloc(e_size); -+ if (e == NULL) { -+ LM_ERR("out of pkg memory\n"); -+ return -1; -+ } -+ memset(e, 0, e_size); ++ if(insert_new_lump_after(anchor, new_h.s, new_h.len, 0) == 0) { ++ LM_ERR("cannot insert header lump\n"); ++ pkg_free(new_h.s); ++ return -1; ++ } + -+ if (pv_str_new(&e->key, key->len+1) < 0) goto err; -+ pv_str_copy(&e->key, key, key->len+1); ++ LM_DBG("append header: %.*s\n", new_h.len, new_h.s); + -+ str_hash_add(ht, e); -+ return 1; ++ return 1; ++} ++ ++int pvh_real_hdr_replace(struct sip_msg *msg, str *hname, str *hvalue) ++{ ++ struct lump *anchor = NULL; ++ hdr_field_t *hf = NULL; ++ str new_h; ++ int new = 1; ++ ++ if(hname->s == NULL || hvalue->s == NULL) { ++ LM_ERR("header name/value cannot be empty"); ++ return -1; ++ } ++ ++ for(hf = msg->headers; hf; hf = hf->next) { ++ if(hf->name.len == hname->len ++ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { ++ if(hf->body.len == hvalue->len ++ && strncasecmp(hf->body.s, hvalue->s, hvalue->len) == 0) { ++ return 1; ++ } ++ new = 0; ++ break; ++ } ++ if(!hf->next) ++ break; ++ } ++ ++ if(hf == NULL) { ++ LM_ERR("unable to find header lump\n"); ++ return -1; ++ } ++ ++ if(new == 0) { ++ if((anchor = del_lump(msg, hf->name.s - msg->buf, hf->len, 0)) == 0) { ++ LM_ERR("unable to delete header lump\n"); ++ return -1; ++ } ++ } else { ++ anchor = anchor_lump(msg, hf->name.s + hf->len - msg->buf, 0, 0); ++ } ++ ++ if(anchor == 0) { ++ LM_ERR("unable to find header lump\n"); ++ return -1; ++ } ++ ++ if(pvh_create_hdr_str(hname, hvalue, &new_h) <= 0) ++ return -1; ++ ++ if(insert_new_lump_after(anchor, new_h.s, new_h.len, 0) == 0) { ++ LM_ERR("cannot insert header lump\n"); ++ pkg_free(new_h.s); ++ return -1; ++ } ++ ++ LM_DBG("%s header: %.*s\n", new ? "append" : "replace", new_h.len, new_h.s); ++ ++ return 1; ++} ++ ++int pvh_real_hdr_del_by_name(struct sip_msg *msg, str *hname) ++{ ++ hdr_field_t *hf = NULL; ++ ++ for(hf = msg->headers; hf; hf = hf->next) { ++ if(hf->name.len == hname->len ++ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { ++ LM_DBG("remove header[%.*s]: %.*s\n", hf->name.len, hf->name.s, ++ hf->body.len, hf->body.s); ++ del_lump(msg, hf->name.s - msg->buf, hf->len, 0); ++ } ++ } ++ return 1; ++} ++ ++int pvh_real_hdr_remove_display(struct sip_msg *msg, str *hname) ++{ ++ hdr_field_t *hf = NULL; ++ struct to_body *d_hf = NULL; ++ int disp_len = 0; ++ ++ for(hf = msg->headers; hf; hf = hf->next) { ++ if(hf->name.len == hname->len ++ && strncasecmp(hf->name.s, hname->s, hname->len) == 0) { ++ d_hf = (struct to_body *)hf->parsed; ++ if((disp_len = d_hf->display.len) > 0) { ++ LM_DBG("remove display[%.*s]: %.*s\n", hf->name.len, hf->name.s, ++ disp_len, d_hf->display.s); ++ if(strncmp(d_hf->display.s + disp_len, " ", 1) == 0) ++ disp_len++; ++ del_lump(msg, d_hf->display.s - msg->buf, disp_len, 0); ++ } ++ } ++ } ++ return 1; ++} ++ ++int pvh_real_replace_reply_reason(struct sip_msg *msg, str *value) ++{ ++ struct lump *anchor = NULL; ++ char *reason = NULL; ++ ++ anchor = del_lump(msg, msg->first_line.u.reply.reason.s - msg->buf, ++ msg->first_line.u.reply.reason.len, 0); ++ if(!anchor) { ++ LM_ERR("set reply: failed to del lump\n"); ++ goto err; ++ } ++ ++ reason = (char *)pkg_malloc(value->len); ++ if(reason == NULL) { ++ PKG_MEM_ERROR; ++ goto err; ++ } ++ memcpy(reason, value->s, value->len); ++ ++ if(insert_new_lump_after(anchor, reason, value->len, 0) == 0) { ++ LM_ERR("set reply: failed to add lump: %.*s\n", value->len, value->s); ++ goto err; ++ } ++ ++ return 1; + +err: -+ pv_str_free(&e->key); -+ return -1; ++ if(reason) ++ pkg_free(reason); ++ return -1; +} + -+int pv_str_hash_free(struct str_hash_table *ht) ++int pvh_create_hdr_str(str *hname, str *hvalue, str *dst) +{ -+ struct str_hash_entry *e = NULL; -+ struct str_hash_entry *bak = NULL; -+ int r; -+ -+ if (ht == NULL) -+ return -1; -+ -+ if (ht->table) { -+ for (r=0; r< ht->size; r++) { -+ clist_foreach_safe(&ht->table[r], e, bak, next) { -+ pv_str_free(&e->key); -+ pkg_free(e); -+ } -+ } -+ pkg_free(ht->table); -+ } -+ -+ return 1; ++ int os; ++ if(hname->s == NULL || hvalue->s == NULL) { ++ LM_ERR("header name/value cannot be empty"); ++ return -1; ++ } ++ ++ if(dst == NULL) { ++ LM_ERR("new header str cannot be null"); ++ return -1; ++ } ++ ++ dst->len = hname->len + 2 + hvalue->len + CRLF_LEN; ++ dst->s = (char *)pkg_malloc(dst->len + 1); ++ if(dst->s == NULL) { ++ PKG_MEM_ERROR; ++ return -1; ++ } ++ memset(dst->s, 0, dst->len + 1); ++ ++ os = 0; ++ memcpy(dst->s, hname->s, hname->len); ++ os += hname->len; ++ memcpy(dst->s + os, ": ", 2); ++ os += 2; ++ memcpy(dst->s + os, hvalue->s, hvalue->len); ++ os += hvalue->len; ++ memcpy(dst->s + os, CRLF, CRLF_LEN); ++ os += CRLF_LEN; ++ dst->s[dst->len] = '\0'; ++ ++ return 1; +} +diff --git a/src/modules/pv_headers/pvh_hdr.h b/src/modules/pv_headers/pvh_hdr.h +new file mode 100644 +index 0000000..f05d7e4 +--- /dev/null ++++ b/src/modules/pv_headers/pvh_hdr.h +@@ -0,0 +1,39 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 PV_HDR_H ++#define PV_HDR_H ++ ++#include "../../core/parser/msg_parser.h" ++#include "../../core/xavp.h" ++ ++#include "pv_headers.h" ++ ++int pvh_real_hdr_append(struct sip_msg *msg, str *hname, str *hvalue); ++int pvh_real_hdr_replace(struct sip_msg *msg, str *hname, str *hvalue); ++int pvh_real_hdr_del_by_name(struct sip_msg *msg, str *hname); ++int pvh_real_hdr_remove_display(struct sip_msg *msg, str *hname); ++int pvh_real_replace_reply_reason(struct sip_msg *msg, str *value); ++int pvh_create_hdr_str(str *hname, str *hvalue, str *dst); ++ ++#endif /* PV_HDR_H */ +\ No newline at end of file +diff --git a/src/modules/pv_headers/pvh_str.c b/src/modules/pv_headers/pvh_str.c +new file mode 100644 +index 0000000..53e2c9c +--- /dev/null ++++ b/src/modules/pv_headers/pvh_str.c +@@ -0,0 +1,144 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 "pvh_str.h" ++ ++int pvh_str_new(str *s, int size) ++{ ++ s->s = (char *)pkg_malloc(size); ++ if(s->s == NULL) { ++ PKG_MEM_ERROR; ++ return -1; ++ } ++ memset(s->s, 0, size); ++ s->len = 0; ++ ++ return 1; ++} ++ ++int pvh_str_free(str *s) ++{ ++ if(s->s) ++ pkg_free(s->s); ++ s->s = NULL; ++ return 1; ++} ++ ++int pvh_str_copy(str *dst, str *src, unsigned int max_size) ++{ ++ unsigned int src_len = src->len + 1 >= max_size ? max_size - 1 : src->len; ++ ++ if(src == NULL || dst == NULL || src->len <= 0) ++ return -1; ++ ++ memset(dst->s, 0, dst->len); ++ memcpy(dst->s, src->s, src_len); ++ dst->s[src_len] = '\0'; ++ dst->len = src_len; ++ ++ return 1; ++} ++ ++int pvh_extract_display_uri(char *suri, str *display, str *duri) ++{ ++ char *ptr_a = NULL; ++ char *ptr_b = NULL; ++ int display_len = 0; ++ int uri_len = 0; ++ ++ if(suri == NULL || strlen(suri) == 0) ++ return -1; ++ ++ ptr_a = strchr(suri, '<'); ++ ptr_b = strchr(suri, '>'); ++ ++ if(ptr_a == NULL && ptr_b == NULL) { ++ ptr_a = suri; ++ uri_len = strlen(suri); ++ } else if(ptr_a == NULL || ptr_b == NULL) { ++ return -1; ++ } else { ++ display_len = ptr_a - suri; ++ ptr_a++; ++ uri_len = ptr_b - ptr_a; ++ } ++ ++ if(uri_len <= 0) ++ return -1; ++ ++ if(display_len > 0) { ++ memcpy(display->s, suri, display_len); ++ display->len = strlen(display->s); ++ display->s[display->len] = '\0'; ++ } else { ++ display->len = 0; ++ } ++ ++ memcpy(duri->s, ptr_a, uri_len); ++ duri->len = strlen(duri->s); ++ duri->s[duri->len] = '\0'; ++ ++ return 1; ++} ++ ++int pvh_split_values( ++ str *s, char d[][header_value_size], int *d_size, int keep_spaces) ++{ ++ char p; ++ int idx = 0, c_idx = 0; ++ ++ *d_size = -1; ++ ++ if(s == NULL || s->len == 0 || d == NULL) { ++ *d_size = 0; ++ return 1; ++ } ++ ++ while(idx < s->len) { ++ strncpy(&p, s->s + idx++, 1); ++ if(keep_spaces == 0 && strncmp(&p, " ", 1) == 0) ++ continue; ++ if(strncmp(&p, ",", 1) == 0) { ++ if(c_idx == 0) ++ continue; ++ if(c_idx + 1 < header_value_size) ++ c_idx++; ++ d[*d_size][c_idx] = '\0'; ++ c_idx = 0; ++ continue; ++ } ++ if(c_idx == 0) ++ (*d_size)++; ++ strncpy(&d[*d_size][c_idx++], &p, 1); ++ } ++ ++ if(c_idx > 0) { ++ if(c_idx >= header_value_size) ++ c_idx--; ++ d[*d_size][c_idx] = '\0'; ++ } ++ ++ (*d_size)++; ++ ++ return 1; ++} +\ No newline at end of file +diff --git a/src/modules/pv_headers/pvh_str.h b/src/modules/pv_headers/pvh_str.h +new file mode 100644 +index 0000000..95aa0b9 +--- /dev/null ++++ b/src/modules/pv_headers/pvh_str.h +@@ -0,0 +1,38 @@ ++/* ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 PV_STR_H ++#define PV_STR_H ++ ++#include "../../core/str.h" ++ ++#include "pv_headers.h" ++ ++int pvh_str_new(str *s, int size); ++int pvh_str_free(str *s); ++int pvh_str_copy(str *dst, str *src, unsigned int max_size); ++int pvh_extract_display_uri(char *suri, str *display, str *duri); ++int pvh_split_values( ++ str *s, char d[][header_value_size], int *d_size, int keep_spaces); ++ ++#endif /* PV_STR_H */ +\ No newline at end of file +diff --git a/src/modules/pv_headers/pvh_xavp.c b/src/modules/pv_headers/pvh_xavp.c +new file mode 100644 +index 0000000..67c44fd +--- /dev/null ++++ b/src/modules/pv_headers/pvh_xavp.c +@@ -0,0 +1,1050 @@ ++/* ++ * ++ * PV Headers ++ * ++ * Copyright (C) 2018 Kirill Solomko ++ * ++ * This file is part of SIP Router, a free SIP server. ++ * ++ * SIP Router 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 ++ * ++ * SIP Router 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 ++ ++#include "../../core/hashes.h" ++#include "../../core/route_struct.h" + -+void pv_free_to_params(struct to_param *param, sr_xavp_sfree_f sfree) ++#include "pvh_xavp.h" ++#include "pvh_str.h" ++#include "pvh_hash.h" ++ ++sr_xavp_t *pvh_xavp_new_value(str *name, sr_xval_t *val) +{ -+ struct to_param *n = NULL; -+ -+ while (param) { -+ n = param->next; -+ sfree(param); -+ param = n; -+ } -+ param = NULL; ++ sr_xavp_t *avp = NULL; ++ int size; ++ unsigned int id; ++ ++ if(name == NULL || name->s == NULL || val == NULL) ++ return NULL; ++ id = get_hash1_raw(name->s, name->len); ++ ++ size = sizeof(sr_xavp_t) + name->len + 1; ++ if(val->type == SR_XTYPE_STR) ++ size += val->v.s.len + 1; ++ avp = (sr_xavp_t *)shm_malloc(size); ++ if(avp == NULL) { ++ SHM_MEM_ERROR; ++ return NULL; ++ } ++ memset(avp, 0, size); ++ avp->id = id; ++ avp->name.s = (char *)avp + sizeof(sr_xavp_t); ++ memcpy(avp->name.s, name->s, name->len); ++ avp->name.s[name->len] = '\0'; ++ avp->name.len = name->len; ++ memcpy(&avp->val, val, sizeof(sr_xval_t)); ++ if(val->type == SR_XTYPE_STR) { ++ avp->val.v.s.s = avp->name.s + avp->name.len + 1; ++ memcpy(avp->val.v.s.s, val->v.s.s, val->v.s.len); ++ avp->val.v.s.s[val->v.s.len] = '\0'; ++ avp->val.v.s.len = val->v.s.len; ++ } ++ ++ return avp; +} + -+int pv_parse_header_name(pv_spec_p sp, str *hname) ++int pvh_xavp_append_value(str *name, sr_xval_t *val, sr_xavp_t **start) +{ -+ pv_spec_p psp = NULL; -+ -+ if (hname->s == NULL || hname->len == 0) { -+ LM_ERR("empty header name\n"); -+ return -1; -+ } -+ -+ if (hname->len >= header_name_size) { -+ LM_ERR("header name is too long\n"); -+ return -1; -+ } -+ -+ if(*hname->s == PV_MARKER) -+ { -+ psp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); -+ if (psp == NULL) { -+ LM_ERR("out of pkg memory\n"); -+ return -1; -+ } -+ if (pv_parse_spec(hname, psp) == NULL) { -+ LM_ERR("invalid avp name [%.*s]\n", hname->len, hname->s); -+ pv_spec_free(psp); -+ return -1; -+ } -+ sp->pvp.pvn.type = PV_NAME_PVAR; -+ sp->pvp.pvn.u.dname = (void*)psp; -+ sp->pvp.pvn.u.isname.name.s = *hname; -+ return 0; -+ } -+ -+ sp->pvp.pvn.type = PV_NAME_INTSTR; -+ sp->pvp.pvn.u.isname.type = AVP_NAME_STR; -+ sp->pvp.pvn.u.isname.name.s = *hname; -+ -+ return 0; ++ sr_xavp_t *last = NULL; ++ sr_xavp_t *xavp = NULL; ++ ++ if((xavp = pvh_xavp_new_value(name, val)) == NULL) ++ return -1; ++ ++ if(*start == NULL) { ++ xavp->next = *start; ++ *start = xavp; ++ return 1; ++ } ++ ++ last = *start; ++ while(last->next) ++ last = last->next; ++ last->next = xavp; ++ ++ return 1; +} + -+int pv_get_header(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) ++int pvh_xavp_set_value(str *name, sr_xval_t *val, int idx, sr_xavp_t **start) +{ -+ sr_xavp_t *xavp = NULL; -+ sr_xval_t *xval = NULL; -+ pv_value_t tv; -+ str hname = STR_NULL; -+ int idx = 0; -+ int cnt = 0; -+ -+ idx = param->pvi.u.ival; -+ -+ if (param->pvn.type == PV_NAME_PVAR) -+ { -+ if(pv_get_spec_value(msg, (pv_spec_p)(param->pvn.u.dname), &tv) != 0) -+ { -+ LM_ERR("cannot get avp value\n"); -+ return -1; -+ } -+ if (!(tv.flags & PV_VAL_STR)) { -+ return pv_get_null(msg, param, res); -+ } -+ hname = tv.rs; -+ } else if (param->pvn.u.isname.type == AVP_NAME_STR) { -+ hname = param->pvn.u.isname.name.s; -+ } else { -+ return pv_get_null(msg, param, res); -+ } -+ -+ if (idx < 0) { -+ if ((xavp = pv_xavp_get_child(msg, &xavp_name, &hname)) == NULL) -+ cnt = 0; -+ else -+ cnt = xavp_count(&hname, &xavp); -+ idx = idx + cnt; -+ if (idx < 0) -+ pv_get_null(msg, param, res); -+ } -+ -+ xval = pv_xavp_get_value(msg, &xavp_name, &hname, idx); -+ -+ if (xval == NULL || !xval->v.s.s) -+ return pv_get_null(msg, param, res); -+ -+ return pv_get_strval(msg, param, res, &xval->v.s); ++ int cnt = 0; ++ ++ if(idx < 0) { ++ cnt = xavp_count(name, start); ++ idx = idx + cnt; ++ if(idx < 0) ++ return -1; ++ } ++ LM_DBG("xavp name: %.*s\n", name->len, name->s); ++ if(xavp_set_value(name, idx, val, start) == NULL) ++ return -1; ++ ++ return 1; +} + -+int pv_set_header(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) ++sr_xval_t *pvh_xavp_get_value( ++ struct sip_msg *msg, str *xname, str *name, int idx) +{ -+ sr_xavp_t *xavp = NULL; -+ pv_elem_p pv_format = NULL; -+ pv_value_t tv; -+ str hname = STR_NULL; -+ str orig_hname = STR_NULL; -+ str fval; -+ int idx = 0; -+ int cnt = 0; -+ int itype; -+ -+ idx = param->pvi.u.ival; -+ itype = param->pvi.type; -+ -+ if (param->pvn.type == PV_NAME_PVAR) -+ { -+ if(pv_get_spec_value(msg, (pv_spec_p)(param->pvn.u.dname), &tv) != 0) -+ { -+ LM_ERR("cannot get avp value\n"); -+ return -1; -+ } -+ if (!(tv.flags & PV_VAL_STR)) { -+ LM_ERR("invalid avp value, must be a string\n"); -+ return -1; -+ } -+ hname = tv.rs; -+ orig_hname = param->pvn.u.isname.name.s; -+ } else if (param->pvn.u.isname.type == AVP_NAME_STR) { -+ hname = param->pvn.u.isname.name.s; -+ orig_hname = hname; -+ } else { -+ LM_ERR("invalid header name, must be a string\n"); -+ return -1; -+ } -+ -+ if ((xavp = pv_xavp_get_child(msg, &xavp_name, &hname)) == NULL) -+ idx = 0; -+ else if (idx < 0) -+ idx = idx + xavp_count(&hname, &xavp); -+ -+ if (val==NULL || (val->flags & PV_VAL_NULL)) { -+ if (itype == PV_IDX_ALL) { -+ for (idx=xavp_count(&hname, &xavp)-1;idx>=0;idx--) { -+ if (pv_set_xavp(msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, idx, 0) < 0) -+ goto err; -+ } -+ } else { -+ if (pv_set_xavp(msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, idx, 0) < 0) -+ goto err; -+ } -+ } else if (val->flags & (PV_VAL_STR|PV_TYPE_INT|PV_VAL_INT)) { -+ if (val->flags & (PV_TYPE_INT|PV_VAL_INT)) { -+ if (pv_get_sintval(msg, param, val, val->ri) < 0) -+ goto err; -+ } -+ if (pv_parse_format(&val->rs, &pv_format) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ -+ if (pv_printf_s(msg, pv_format, &fval) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ if (strlen(orig_hname.s) > 1 && -+ strcmp(orig_hname.s+strlen(orig_hname.s)-2, "])") != 0) { -+ if (pv_set_xavp(msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, 0, 1) < 0) -+ goto err; -+ } else if (itype == PV_IDX_ALL) { -+ idx = 0; -+ cnt = xavp_count(&hname, &xavp); -+ while (idx < cnt) { -+ if (pv_set_xavp(msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, idx++, 0) < 1) -+ goto err; -+ } -+ if (pv_set_xavp(msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, 0, cnt ? 0 : 1) < 0) -+ goto err; -+ } else { -+ if (pv_set_xavp(msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, idx, 0) < 0) -+ goto err; -+ } -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ } else { -+ LM_ERR("x_hdr %.*s value can be either string, integer or null\n", hname.len, hname.s); -+ goto err; -+ } -+ return 1; ++ sr_xavp_t *xavp = NULL; ++ sr_xavp_t *sub = NULL; ++ str br_xname = STR_NULL; ++ ++ if(pvh_str_new(&br_xname, header_name_size) < 0) ++ return NULL; ++ ++ pvh_get_branch_xname(msg, xname, &br_xname); ++ if((xavp = xavp_get(&br_xname, NULL)) == NULL ++ && (xavp = xavp_get(xname, NULL)) == NULL) { ++ goto err; ++ } ++ ++ if(xavp->val.type != SR_XTYPE_XAVP) { ++ LM_ERR("not xavp child type %s\n", br_xname.s); ++ goto err; ++ } ++ ++ sub = xavp_get_by_index(name, idx, &xavp->val.v.xavp); ++ ++ pvh_str_free(&br_xname); ++ return sub ? &sub->val : NULL; + +err: -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ return -1; ++ pvh_str_free(&br_xname); ++ return NULL; +} + -+int pv_get_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) ++sr_xavp_t *pvh_xavp_get_child(struct sip_msg *msg, str *xname, str *name) +{ -+ sr_xval_t *xval = NULL; -+ sr_xval_t *xval_pd = NULL; -+ xavp_c_data_t *c_data = NULL; -+ int p_no = 0; -+ str sval = STR_NULL; -+ int ival = 0; -+ int is_strint = 0; -+ str hname = STR_NULL; -+ -+ p_no = param->pvn.u.isname.name.n; -+ if (pv_str_new(&hname, header_name_size) < 0) goto err; -+ -+ if (p_no >= 1 && p_no <= 5) -+ pv_str_copy(&hname, &(str){"From",4}, header_name_size); -+ else if (p_no >= 6 && p_no <= 10) -+ pv_str_copy(&hname, &(str){"To",2}, header_name_size); -+ -+ xval = pv_xavp_get_value(msg, &xavp_name, &hname, 0); -+ if (xval == NULL || !xval->v.s.s) -+ goto err; -+ -+ xval_pd = pv_xavp_get_value(msg, &xavp_parsed_xname, &hname, 0); -+ -+ if (xval_pd) -+ c_data = (xavp_c_data_t*)xval_pd->v.data->p; -+ -+ if (c_data != NULL && -+ strncmp(xval->v.s.s, c_data->value.s, c_data->value.len) != 0) { -+ c_data = NULL; -+ } -+ -+ if (c_data == NULL) { -+ c_data = (xavp_c_data_t*)shm_malloc(sizeof(xavp_c_data_t)); -+ if (c_data == NULL) { -+ LM_ERR("out of shared memory\n"); -+ goto err; -+ } -+ memset(c_data, 0, sizeof(xavp_c_data_t)); -+ if (pv_merge_uri(msg, SET_URI_T, &xval->v.s, &xval->v.s, c_data) < 0) -+ goto err; -+ if (pv_set_xavp(msg, &xavp_parsed_xname, &hname, c_data, SR_XTYPE_DATA, 0, 0) < 0) -+ goto err; -+ } -+ -+ switch (p_no) { -+ case 1: // full from -+ case 6: // full to -+ sval = c_data->to_b.uri; -+ break; -+ case 2: // username from -+ case 7: // username to -+ sval = c_data->to_b.parsed_uri.user; -+ break; -+ case 3: // domain from -+ case 8: // domain to -+ sval = c_data->to_b.parsed_uri.host; -+ break; -+ case 4: // displayname from -+ case 9: // displayname to -+ sval = c_data->to_b.display; -+ break; -+ case 5: // from tag -+ case 10: // to tag -+ sval = c_data->to_b.tag_value; -+ break; -+ default: -+ LM_ERR("unknown get uri op\n"); -+ } -+ -+ pv_str_free(&hname); -+ return sval.s ? is_strint ? pv_get_strintval(msg, param, res, &sval, ival) -+ : pv_get_strval(msg, param, res, &sval) -+ : pv_get_null(msg, param, res); ++ sr_xavp_t *xavp = NULL; ++ str br_xname = STR_NULL; ++ ++ if(pvh_str_new(&br_xname, header_name_size) < 0) ++ return NULL; ++ ++ pvh_get_branch_xname(msg, xname, &br_xname); ++ xavp = xavp_get_child(&br_xname, name); ++ if(xavp == NULL) ++ xavp = xavp_get_child(xname, name); ++ ++ pvh_str_free(&br_xname); ++ return xavp; ++} ++ ++int pvh_xavp_is_null(sr_xavp_t *avp) ++{ ++ if(avp == NULL) ++ return 1; ++ ++ if(avp->val.type == SR_XTYPE_NULL ++ || (avp->val.type == SR_XTYPE_STR ++ && (strncasecmp(avp->val.v.s.s, "NULL", 4) == 0))) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++void pvh_xavp_free_data(void *p, sr_xavp_sfree_f sfree) ++{ ++ xavp_c_data_t *c_data = NULL; ++ ++ if((c_data = (xavp_c_data_t *)p) != NULL) { ++ pvh_free_to_params(c_data->to_params, sfree); ++ sfree(c_data->value.s); ++ c_data->value.s = NULL; ++ sfree(c_data); ++ c_data = NULL; ++ } ++} ++ ++int pvh_xavp_keys_count(sr_xavp_t **start) ++{ ++ sr_xavp_t *xavp = NULL; ++ int cnt = 0; ++ ++ if(*start == NULL) ++ return 0; ++ ++ xavp = *start; ++ ++ while(xavp) { ++ cnt++; ++ xavp = xavp->next; ++ } ++ ++ return cnt; ++} ++ ++void pvh_free_to_params(struct to_param *param, sr_xavp_sfree_f sfree) ++{ ++ struct to_param *n = NULL; ++ ++ while(param) { ++ n = param->next; ++ sfree(param); ++ param = n; ++ } ++ param = NULL; ++} ++ ++int pvh_parse_header_name(pv_spec_p sp, str *hname) ++{ ++ pv_spec_p psp = NULL; ++ ++ if(hname->s == NULL || hname->len == 0) { ++ LM_ERR("empty header name\n"); ++ return -1; ++ } ++ ++ if(hname->len >= header_name_size) { ++ LM_ERR("header name is too long\n"); ++ return -1; ++ } ++ ++ if(*hname->s == PV_MARKER) { ++ psp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); ++ if(psp == NULL) { ++ PKG_MEM_ERROR; ++ return -1; ++ } ++ if(pv_parse_spec(hname, psp) == NULL) { ++ LM_ERR("invalid avp name [%.*s]\n", hname->len, hname->s); ++ pv_spec_free(psp); ++ return -1; ++ } ++ sp->pvp.pvn.type = PV_NAME_PVAR; ++ sp->pvp.pvn.u.dname = (void *)psp; ++ sp->pvp.pvn.u.isname.name.s = *hname; ++ return 0; ++ } ++ ++ sp->pvp.pvn.type = PV_NAME_INTSTR; ++ sp->pvp.pvn.u.isname.type = AVP_NAME_STR; ++ sp->pvp.pvn.u.isname.name.s = *hname; ++ ++ return 0; ++} ++ ++int pvh_set_xavp(struct sip_msg *msg, str *xname, str *name, void *data, ++ sr_xtype_t type, int idx, int append) ++{ ++ sr_xavp_t **xavp = NULL; ++ sr_xavp_t *root = NULL; ++ sr_xval_t root_xval; ++ sr_xval_t xval; ++ str br_xname = STR_NULL; ++ int br_idx; ++ ++ if(xname == NULL || name == NULL) { ++ LM_ERR("missing xavp/pv name\n"); ++ return -1; ++ } ++ ++ pvh_get_branch_index(msg, &br_idx); ++ if(pvh_str_new(&br_xname, header_name_size) < 0) ++ return -1; ++ pvh_get_branch_xname(msg, xname, &br_xname); ++ LM_DBG("br_xname: %.*s name: %.*s\n", br_xname.len, br_xname.s, name->len, ++ name->s); ++ memset(&xval, 0, sizeof(sr_xval_t)); ++ if(data == NULL || SR_XTYPE_NULL) { ++ xval.type = SR_XTYPE_NULL; ++ } else if(type == SR_XTYPE_STR) { ++ xval.type = SR_XTYPE_STR; ++ xval.v.s = *(str *)data; ++ } else if(type == SR_XTYPE_DATA) { ++ xval.type = SR_XTYPE_DATA; ++ xval.v.data = (sr_data_t *)shm_malloc(sizeof(sr_data_t)); ++ if(xval.v.data == NULL) { ++ SHM_MEM_ERROR; ++ goto err; ++ } ++ memset(xval.v.data, 0, sizeof(sr_data_t)); ++ xval.v.data->p = data; ++ xval.v.data->pfree = pvh_xavp_free_data; ++ } ++ ++ root = xavp_get(&br_xname, NULL); ++ ++ if(root == NULL && br_idx > 0) { ++ pvh_clone_branch_xavp(msg, xname); ++ root = xavp_get(&br_xname, NULL); ++ } ++ ++ xavp = root ? &root->val.v.xavp : &root; ++ ++ if(root == NULL) { ++ append = 1; ++ memset(&root_xval, 0, sizeof(sr_xval_t)); ++ root_xval.type = SR_XTYPE_XAVP; ++ root_xval.v.xavp = NULL; ++ ++ if((root = xavp_add_value(&br_xname, &root_xval, NULL)) == NULL) { ++ LM_ERR("error create xavp %s\n", br_xname.s); ++ goto err; ++ } ++ xavp = &root->val.v.xavp; ++ } else if(xavp_get_child(&br_xname, name) == NULL) { ++ append = 1; ++ } ++ ++ if(append) { ++ if(pvh_xavp_append_value(name, &xval, xavp) < 0) { ++ LM_ERR("error append xavp=>name %s=>%.*s\n", br_xname.s, name->len, ++ name->s); ++ goto err; ++ } ++ } else { ++ if(pvh_xavp_set_value(name, &xval, idx, xavp) < 0) { ++ LM_ERR("error modify xavp=>name %s=>%.*s idx=%d\n", br_xname.s, ++ name->len, name->s, idx); ++ goto err; ++ } ++ } ++ ++ pvh_str_free(&br_xname); ++ return 1; + +err: -+ pv_str_free(&hname); -+ return pv_get_null(msg, param, res); ++ pvh_str_free(&br_xname); ++ return -1; ++} ++ ++int pvh_free_xavp(str *xname) ++{ ++ sr_xavp_t *xavp = NULL; ++ xavp_rm_by_name(xname, 1, NULL); ++ if((xavp = xavp_get(xname, NULL)) != NULL) ++ xavp_rm(xavp, NULL); ++ return 1; +} + -+int pv_set_uri(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) ++int pvh_get_branch_index(struct sip_msg *msg, int *br_idx) +{ -+ sr_xval_t *xval = NULL; -+ xavp_c_data_t *c_data = NULL; -+ pv_elem_p pv_format = NULL; -+ int p_no = 0; -+ enum action_type a_type; -+ str hname; -+ str fval; -+ -+ p_no = param->pvn.u.isname.name.n; -+ if (pv_str_new(&hname, header_name_size) < 0) goto err; -+ if (p_no >= 1 && p_no <= 5) -+ pv_str_copy(&hname, &(str){"From",4}, header_name_size); -+ else if (p_no >= 6 && p_no <= 10) -+ pv_str_copy(&hname, &(str){"To",2}, header_name_size); -+ -+ switch (p_no) { -+ case 1: // uri from -+ case 6: // uri to -+ a_type = SET_URI_T; -+ break; -+ case 2: // username from -+ case 7: // username to -+ a_type = SET_USER_T; -+ break; -+ case 3: // domain from -+ case 8: // domain to -+ a_type = SET_HOST_T; -+ break; -+ case 4: // displayname from -+ case 9: // displayname to -+ a_type = SET_USERPHONE_T; -+ break; -+ default: -+ LM_ERR("unknown set uri op\n"); -+ goto err; -+ } -+ -+ if (val->flags & (PV_TYPE_INT|PV_VAL_INT)) { -+ if (pv_get_sintval(msg, param, val, val->ri) < 0) -+ goto err; -+ } -+ -+ if (pv_parse_format(&val->rs, &pv_format) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ -+ if (pv_printf_s(msg, pv_format, &fval) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ -+ xval = pv_xavp_get_value(msg, &xavp_name, &hname, 0); -+ if (xval == NULL || !xval->v.s.s) -+ goto err; -+ -+ c_data = (xavp_c_data_t*)shm_malloc(sizeof(xavp_c_data_t)); -+ if (c_data == NULL) { -+ LM_ERR("out of shared memory\n"); -+ goto err; -+ } -+ memset(c_data, 0, sizeof(xavp_c_data_t)); -+ if (pv_merge_uri(msg, a_type, &xval->v.s, &fval, c_data) < 0) -+ goto err; -+ -+ if (pv_set_xavp(msg, &xavp_name, &hname, &c_data->value, SR_XTYPE_STR, 0, 0) < 0) -+ goto err; -+ -+ if (pv_set_xavp(msg, &xavp_parsed_xname, &hname, c_data, SR_XTYPE_DATA, 0, 0) < 0) -+ goto err; -+ -+ pv_str_free(&hname); -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ return 1; ++ int os = 0; ++ int len = 0; ++ char parsed_br_idx[header_value_size]; ++ ++ if(msg->add_to_branch_len > header_value_size) { ++ LM_ERR("branch name is too long\n"); ++ return -1; ++ } ++ ++ os = msg->add_to_branch_len; ++ while(os > 0 && memcmp(msg->add_to_branch_s + os - 1, ".", 1)) ++ os--; ++ len = msg->add_to_branch_len - os; ++ if(os > 0 && len > 0) { ++ memcpy(parsed_br_idx, msg->add_to_branch_s + os, len); ++ parsed_br_idx[len] = '\0'; ++ *br_idx = atoi(parsed_br_idx) + 1; ++ } else { ++ *br_idx = 0; ++ } ++ ++ return 1; ++} ++ ++int pvh_get_branch_xname(struct sip_msg *msg, str *xname, str *dst) ++{ ++ int br_idx; ++ int os = 0; ++ char br_idx_s[32]; ++ char br_idx_len = 0; ++ ++ if(dst == NULL) ++ return -1; ++ ++ memset(dst->s, 0, dst->len); ++ memcpy(dst->s, xname->s, xname->len); ++ os += xname->len; ++ ++ pvh_get_branch_index(msg, &br_idx); ++ if(br_idx > 0) { ++ sprintf(br_idx_s, "%d", br_idx - 1); ++ br_idx_len = strlen(br_idx_s); ++ memcpy(dst->s + os, ".", 1); ++ os += 1; ++ memcpy(dst->s + os, br_idx_s, br_idx_len); ++ os += br_idx_len; ++ } ++ if(msg->first_line.type == SIP_REPLY) { ++ memcpy(dst->s + os, ".r", 2); ++ os += 2; ++ } ++ dst->len = os; ++ dst->s[dst->len] = '\0'; ++ ++ return 1; ++} ++ ++int pvh_clone_branch_xavp(struct sip_msg *msg, str *xname) ++{ ++ sr_xavp_t *xavp = NULL; ++ sr_xavp_t *br_xavp = NULL; ++ sr_xavp_t *sub = NULL; ++ sr_xval_t root_xval; ++ str br_xname = STR_NULL; ++ ++ if((xavp = xavp_get(xname, NULL)) == NULL) { ++ LM_ERR("cannot clone xavp from non existing %s\n", xname->s); ++ return -1; ++ } ++ ++ if(xavp->val.type != SR_XTYPE_XAVP) { ++ LM_ERR("not xavp child type %s\n", xavp_name.s); ++ return -1; ++ } ++ ++ if((sub = xavp->val.v.xavp) == NULL) { ++ LM_ERR("invalid xavp structure: %s\n", xavp_name.s); ++ return -1; ++ } ++ ++ if(pvh_str_new(&br_xname, header_name_size) < 0) ++ return -1; ++ pvh_get_branch_xname(msg, xname, &br_xname); ++ ++ memset(&root_xval, 0, sizeof(sr_xval_t)); ++ root_xval.type = SR_XTYPE_XAVP; ++ root_xval.v.xavp = NULL; ++ ++ if((br_xavp = xavp_add_value(&br_xname, &root_xval, NULL)) == NULL) { ++ LM_ERR("error create xavp %s\n", br_xname.s); ++ goto err; ++ } ++ ++ if(strncmp(xname->s, xavp_parsed_xname.s, xname->len) == 0) { ++ pvh_str_free(&br_xname); ++ return 1; ++ } ++ ++ do { ++ if(pvh_skip_header(&sub->name)) ++ continue; ++ if(sub->val.type == SR_XTYPE_DATA) ++ continue; ++ if(pvh_xavp_append_value(&sub->name, &sub->val, &br_xavp->val.v.xavp) ++ < 0) { ++ LM_ERR("cannot clone xavp %s\n", sub->name.s); ++ goto err; ++ } ++ } while((sub = sub->next) != NULL); ++ ++ pvh_str_free(&br_xname); ++ return 1; + +err: -+ pv_str_free(&hname); -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ return -1; ++ pvh_str_free(&br_xname); ++ return -1; +} + -+int pv_merge_uri(struct sip_msg *msg, enum action_type type, str *cur, str *new, xavp_c_data_t *c_data) ++int pvh_get_header(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) +{ -+ struct sip_uri puri; -+ struct to_body tb; -+ struct to_param *param = NULL; -+ struct to_param *sparam_start = NULL; -+ struct to_param **sparam = NULL; -+ str *merged = NULL; -+ char *c_ptr = NULL; -+ str uri_t; -+ int os = 0; -+ int t_len = 0; -+ -+ parse_addr_spec(cur->s, cur->s+cur->len, &tb, 0); -+ if (!tb.uri.s) { -+ LM_ERR("cannot parse addr spec\n"); -+ goto err; -+ } -+ -+ if (parse_uri(tb.uri.s, tb.uri.len, &tb.parsed_uri) < 0) { -+ LM_ERR("cannot parse uri %.*s\n", tb.uri.len, tb.uri.s); -+ goto err; -+ } -+ puri = tb.parsed_uri; -+ -+ c_data->value.s = (char*)shm_malloc(header_value_size); -+ if (c_data->value.s == NULL) { -+ LM_ERR("out of shared memory\n"); -+ goto err; -+ } -+ merged = &c_data->value; -+ -+ if (type == SET_URI_T && strchr(new->s, '<')) { -+ pv_str_copy(merged, new, header_value_size); -+ goto reparse; -+ } -+ -+ os = 0; -+ if (type == SET_USERPHONE_T) { -+ memcpy(merged->s+os, new->s, new->len); os+=new->len; -+ memcpy(merged->s+os, " ", 1); os+=1; -+ } else if (tb.display.len > 0) { -+ memcpy(merged->s+os, tb.display.s, tb.display.len); -+ os+=tb.display.len; -+ memcpy(merged->s+os, " ", 1); os+=1; -+ } -+ memcpy(merged->s+os, "<", 1); os+=1; -+ if (type != SET_URI_T) { -+ uri_type_to_str(puri.type, &uri_t); -+ t_len = uri_t.len+1; -+ memcpy(merged->s+os, uri_t.s, uri_t.len); os+=uri_t.len; -+ memcpy(merged->s+os, ":", 1); os+=1; -+ } -+ switch (type) { -+ case SET_USERPHONE_T: -+ memcpy(merged->s+os, tb.uri.s+t_len, tb.uri.len-t_len); -+ os+=tb.uri.len-t_len; -+ break; -+ case SET_URI_T: -+ memcpy(merged->s+os, new->s, new->len); os+=new->len; -+ break; -+ case SET_USER_T: -+ memcpy(merged->s+os, new->s, new->len); os+=new->len; -+ memcpy(merged->s+os, tb.uri.s+t_len+puri.user.len, -+ tb.uri.len-t_len-puri.user.len); -+ os+=tb.uri.len-t_len-puri.user.len; -+ break; -+ case SET_HOST_T: -+ if ((c_ptr = strchr(tb.uri.s, '@')) == NULL) { -+ LM_ERR("invalid uri: %.*s\n", tb.uri.len, tb.uri.s); -+ goto err; -+ } -+ memcpy(merged->s+os, tb.uri.s+t_len, c_ptr-tb.uri.s-t_len+1); -+ os+=c_ptr-tb.uri.s-t_len+1; -+ memcpy(merged->s+os, new->s, new->len); os+=new->len; -+ memcpy(merged->s+os, c_ptr+puri.host.len+1, -+ tb.uri.s+tb.uri.len-c_ptr-puri.host.len-1); -+ os+=tb.uri.s+tb.uri.len-c_ptr-puri.host.len-1; -+ break; -+ default: -+ LM_ERR("unknown set uri op\n"); -+ goto err; -+ } -+ memcpy(merged->s+os, ">", 1); os+=1; -+ if ((param = tb.param_lst) != NULL) { -+ while (param) { -+ memcpy(merged->s+os, ";", 1); os+=1; -+ memcpy(merged->s+os, param->name.s, param->name.len); -+ os+=param->name.len; -+ memcpy(merged->s+os, "=", 1); os+=1; -+ memcpy(merged->s+os, param->value.s, param->value.len); -+ os += param->value.len; -+ param = param->next; -+ } -+ } -+ merged->len = os; -+ merged->s[merged->len] = '\0'; ++ sr_xavp_t *xavp = NULL; ++ sr_xval_t *xval = NULL; ++ pv_value_t tv; ++ str hname = STR_NULL; ++ int idx = 0; ++ int cnt = 0; ++ ++ idx = param->pvi.u.ival; ++ ++ if(param->pvn.type == PV_NAME_PVAR) { ++ if(pv_get_spec_value(msg, (pv_spec_p)(param->pvn.u.dname), &tv) != 0) { ++ LM_ERR("cannot get avp value\n"); ++ return -1; ++ } ++ if(!(tv.flags & PV_VAL_STR)) { ++ return pv_get_null(msg, param, res); ++ } ++ hname = tv.rs; ++ } else if(param->pvn.u.isname.type == AVP_NAME_STR) { ++ hname = param->pvn.u.isname.name.s; ++ } else { ++ return pv_get_null(msg, param, res); ++ } + -+reparse: ++ if(idx < 0) { ++ if((xavp = pvh_xavp_get_child(msg, &xavp_name, &hname)) == NULL) ++ cnt = 0; ++ else ++ cnt = xavp_count(&hname, &xavp); ++ idx = idx + cnt; ++ if(idx < 0) ++ pv_get_null(msg, param, res); ++ } + -+ parse_addr_spec(merged->s, merged->s+merged->len, &c_data->to_b, 0); -+ if (!c_data->to_b.uri.s) { -+ LM_ERR("cannot parse addr spec\n"); -+ goto err; -+ } -+ -+ if ((param = tb.param_lst) != NULL) { -+ while (param) { -+ if (sparam == NULL) -+ sparam = &sparam_start; -+ *sparam = (struct to_param*)shm_malloc(sizeof(struct to_param)); -+ if (*sparam == NULL) { -+ LM_ERR("out of shared memory\n"); -+ goto err; -+ } -+ memset(*sparam, 0, sizeof(struct to_param)); -+ memcpy(*sparam, param, sizeof(struct to_param)); -+ (*sparam)->next = NULL; -+ sparam = &(*sparam)->next; -+ param = param->next; -+ } -+ c_data->to_params = sparam_start; -+ } -+ -+ if (parse_uri(c_data->to_b.uri.s, c_data->to_b.uri.len, -+ &c_data->to_b.parsed_uri) < 0) { -+ LM_ERR("cannot parse uri %.*s\n", -+ c_data->to_b.uri.len, c_data->to_b.uri.s); -+ goto err; -+ } -+ -+ free_to_params(&tb); -+ free_to_params(&c_data->to_b); -+ return 1; ++ xval = pvh_xavp_get_value(msg, &xavp_name, &hname, idx); ++ ++ if(xval == NULL || !xval->v.s.s) ++ return pv_get_null(msg, param, res); ++ ++ return pv_get_strval(msg, param, res, &xval->v.s); ++} ++ ++int pvh_set_header( ++ struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) ++{ ++ sr_xavp_t *xavp = NULL; ++ pv_elem_p pv_format = NULL; ++ pv_value_t tv; ++ str hname = STR_NULL; ++ str orig_hname = STR_NULL; ++ str fval; ++ int idx = 0; ++ int cnt = 0; ++ int itype; ++ ++ idx = param->pvi.u.ival; ++ itype = param->pvi.type; ++ ++ if(param->pvn.type == PV_NAME_PVAR) { ++ if(pv_get_spec_value(msg, (pv_spec_p)(param->pvn.u.dname), &tv) != 0) { ++ LM_ERR("cannot get avp value\n"); ++ return -1; ++ } ++ if(!(tv.flags & PV_VAL_STR)) { ++ LM_ERR("invalid avp value, must be a string\n"); ++ return -1; ++ } ++ hname = tv.rs; ++ orig_hname = param->pvn.u.isname.name.s; ++ } else if(param->pvn.u.isname.type == AVP_NAME_STR) { ++ hname = param->pvn.u.isname.name.s; ++ orig_hname = hname; ++ } else { ++ LM_ERR("invalid header name, must be a string\n"); ++ return -1; ++ } ++ ++ if((xavp = pvh_xavp_get_child(msg, &xavp_name, &hname)) == NULL) ++ idx = 0; ++ else if(idx < 0) ++ idx = idx + xavp_count(&hname, &xavp); ++ ++ if(val == NULL || (val->flags & PV_VAL_NULL)) { ++ if(itype == PV_IDX_ALL) { ++ for(idx = xavp_count(&hname, &xavp) - 1; idx >= 0; idx--) { ++ if(pvh_set_xavp( ++ msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, idx, 0) ++ < 0) ++ goto err; ++ } ++ } else { ++ if(pvh_set_xavp(msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, idx, 0) ++ < 0) ++ goto err; ++ } ++ } else if(val->flags & (PV_VAL_STR | PV_TYPE_INT | PV_VAL_INT)) { ++ if(val->flags & (PV_TYPE_INT | PV_VAL_INT)) { ++ if(pv_get_sintval(msg, param, val, val->ri) < 0) ++ goto err; ++ } ++ if(pv_parse_format(&val->rs, &pv_format) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ ++ if(pv_printf_s(msg, pv_format, &fval) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ if(strlen(orig_hname.s) > 1 ++ && strcmp(orig_hname.s + strlen(orig_hname.s) - 2, "])") != 0) { ++ if(pvh_set_xavp(msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, 0, 1) ++ < 0) ++ goto err; ++ } else if(itype == PV_IDX_ALL) { ++ idx = 0; ++ cnt = xavp_count(&hname, &xavp); ++ while(idx < cnt) { ++ if(pvh_set_xavp(msg, &xavp_name, &hname, NULL, SR_XTYPE_STR, ++ idx++, 0) ++ < 1) ++ goto err; ++ } ++ if(pvh_set_xavp(msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, 0, ++ cnt ? 0 : 1) ++ < 0) ++ goto err; ++ } else { ++ if(pvh_set_xavp( ++ msg, &xavp_name, &hname, &fval, SR_XTYPE_STR, idx, 0) ++ < 0) ++ goto err; ++ } ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ } else { ++ LM_ERR("x_hdr %.*s value can be either string, integer or null\n", ++ hname.len, hname.s); ++ goto err; ++ } ++ return 1; + +err: -+ free_to_params(&tb); -+ free_to_params(&c_data->to_b); -+ return -1; ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ return -1; +} + -+int pv_get_reply_sr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) ++int pvh_get_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) +{ -+ sr_xval_t *xval = NULL; -+ int p_no = 0; -+ str rhname = {"@Reply-Reason",13}; -+ -+ p_no = param->pvn.u.isname.name.n; -+ -+ if (msg->first_line.type != SIP_REPLY) -+ return pv_get_null(msg, param, res); -+ -+ switch (p_no) { -+ case 1: // status -+ return pv_get_intstrval(msg, param, res, -+ (int)msg->first_line.u.reply.statuscode, -+ &msg->first_line.u.reply.status); -+ break; -+ case 2: // reason -+ xval = pv_xavp_get_value(msg, &xavp_name, &rhname, 0); -+ return pv_get_strval(msg, param, res, -+ xval && xval->v.s.s -+ ? &xval->v.s -+ : &msg->first_line.u.reply.reason); -+ break; -+ default: -+ LM_ERR("unknown get reply op\n"); -+ } -+ -+ return pv_get_null(msg, param, res); ++ sr_xval_t *xval = NULL; ++ sr_xval_t *xval_pd = NULL; ++ xavp_c_data_t *c_data = NULL; ++ int p_no = 0; ++ str sval = STR_NULL; ++ int ival = 0; ++ int is_strint = 0; ++ str hname = STR_NULL; ++ ++ p_no = param->pvn.u.isname.name.n; ++ if(pvh_str_new(&hname, header_name_size) < 0) ++ goto err; ++ ++ if(p_no >= 1 && p_no <= 5) ++ pvh_str_copy(&hname, &(str){"From", 4}, header_name_size); ++ else if(p_no >= 6 && p_no <= 10) ++ pvh_str_copy(&hname, &(str){"To", 2}, header_name_size); ++ ++ xval = pvh_xavp_get_value(msg, &xavp_name, &hname, 0); ++ if(xval == NULL || !xval->v.s.s) ++ goto err; ++ ++ xval_pd = pvh_xavp_get_value(msg, &xavp_parsed_xname, &hname, 0); ++ ++ if(xval_pd) ++ c_data = (xavp_c_data_t *)xval_pd->v.data->p; ++ ++ if(c_data != NULL ++ && strncmp(xval->v.s.s, c_data->value.s, c_data->value.len) != 0) { ++ c_data = NULL; ++ } ++ ++ if(c_data == NULL) { ++ c_data = (xavp_c_data_t *)shm_malloc(sizeof(xavp_c_data_t)); ++ if(c_data == NULL) { ++ SHM_MEM_ERROR; ++ goto err; ++ } ++ memset(c_data, 0, sizeof(xavp_c_data_t)); ++ if(pvh_merge_uri(msg, SET_URI_T, &xval->v.s, &xval->v.s, c_data) < 0) ++ goto err; ++ if(pvh_set_xavp( ++ msg, &xavp_parsed_xname, &hname, c_data, SR_XTYPE_DATA, 0, 0) ++ < 0) ++ goto err; ++ } ++ ++ switch(p_no) { ++ case 1: // full from ++ case 6: // full to ++ sval = c_data->to_b.uri; ++ break; ++ case 2: // username from ++ case 7: // username to ++ sval = c_data->to_b.parsed_uri.user; ++ break; ++ case 3: // domain from ++ case 8: // domain to ++ sval = c_data->to_b.parsed_uri.host; ++ break; ++ case 4: // displayname from ++ case 9: // displayname to ++ sval = c_data->to_b.display; ++ break; ++ case 5: // from tag ++ case 10: // to tag ++ sval = c_data->to_b.tag_value; ++ break; ++ default: ++ LM_ERR("unknown get uri op\n"); ++ } ++ ++ pvh_str_free(&hname); ++ return sval.s ? is_strint ? pv_get_strintval(msg, param, res, &sval, ival) ++ : pv_get_strval(msg, param, res, &sval) ++ : pv_get_null(msg, param, res); ++ ++err: ++ pvh_str_free(&hname); ++ return pv_get_null(msg, param, res); +} + -+int pv_set_reply_sr(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) ++int pvh_set_uri(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) +{ -+ pv_elem_p pv_format = NULL; -+ int p_no = 0; -+ unsigned int code = 0; -+ str rhname = {"@Reply-Reason",13}; -+ str fval; -+ -+ p_no = param->pvn.u.isname.name.n; -+ -+ if (msg->first_line.type != SIP_REPLY) { -+ LM_ERR("set reply: not a reply message\n"); -+ goto err; -+ } -+ -+ if (val->flags & (PV_VAL_NULL)) { -+ LM_ERR("set reply: value cannot be null\n"); -+ goto err; -+ } -+ -+ if (val->flags & (PV_TYPE_INT|PV_VAL_INT)) { -+ if (pv_get_sintval(msg, param, val, val->ri) < 0) -+ goto err; -+ } -+ -+ if (pv_parse_format(&val->rs, &pv_format) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ -+ if (pv_printf_s(msg, pv_format, &fval) < 0) { -+ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); -+ goto err; -+ } -+ -+ switch (p_no) { -+ case 1: // status -+ code = atoi(fval.s); -+ if(code < 100 || code > 699) { -+ LM_ERR("set reply: wrong status code: %d\n", code); -+ goto err; -+ } -+ if((code < 300 || msg->REPLY_STATUS < 300) && -+ (code / 100 != msg->REPLY_STATUS / 100)) { -+ LM_ERR("set reply: 1xx or 2xx replies cannot be changed or set to\n"); -+ goto err; -+ } -+ msg->first_line.u.reply.statuscode = code; -+ msg->first_line.u.reply.status.s[2] = code % 10 + '0'; code /= 10; -+ msg->first_line.u.reply.status.s[1] = code % 10 + '0'; code /= 10; -+ msg->first_line.u.reply.status.s[0] = code + '0'; -+ break; -+ case 2: // reason -+ if (pv_set_xavp(msg, &xavp_name, &rhname, &fval, SR_XTYPE_STR, 0, 0) < 0) { -+ LM_ERR("set reply: cannot set reply reason\n"); -+ goto err; -+ } -+ break; -+ default: -+ LM_ERR("unknown set reply op\n"); -+ goto err; -+ } -+ -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ return 1; ++ sr_xval_t *xval = NULL; ++ xavp_c_data_t *c_data = NULL; ++ pv_elem_p pv_format = NULL; ++ int p_no = 0; ++ enum action_type a_type; ++ str hname; ++ str fval; ++ ++ p_no = param->pvn.u.isname.name.n; ++ if(pvh_str_new(&hname, header_name_size) < 0) ++ goto err; ++ if(p_no >= 1 && p_no <= 5) ++ pvh_str_copy(&hname, &(str){"From", 4}, header_name_size); ++ else if(p_no >= 6 && p_no <= 10) ++ pvh_str_copy(&hname, &(str){"To", 2}, header_name_size); ++ ++ switch(p_no) { ++ case 1: // uri from ++ case 6: // uri to ++ a_type = SET_URI_T; ++ break; ++ case 2: // username from ++ case 7: // username to ++ a_type = SET_USER_T; ++ break; ++ case 3: // domain from ++ case 8: // domain to ++ a_type = SET_HOST_T; ++ break; ++ case 4: // displayname from ++ case 9: // displayname to ++ a_type = SET_USERPHONE_T; ++ break; ++ default: ++ LM_ERR("unknown set uri op\n"); ++ goto err; ++ } ++ ++ if(val->flags & (PV_TYPE_INT | PV_VAL_INT)) { ++ if(pv_get_sintval(msg, param, val, val->ri) < 0) ++ goto err; ++ } ++ ++ if(pv_parse_format(&val->rs, &pv_format) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ ++ if(pv_printf_s(msg, pv_format, &fval) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ ++ xval = pvh_xavp_get_value(msg, &xavp_name, &hname, 0); ++ if(xval == NULL || !xval->v.s.s) ++ goto err; ++ ++ c_data = (xavp_c_data_t *)shm_malloc(sizeof(xavp_c_data_t)); ++ if(c_data == NULL) { ++ SHM_MEM_ERROR; ++ goto err; ++ } ++ memset(c_data, 0, sizeof(xavp_c_data_t)); ++ if(pvh_merge_uri(msg, a_type, &xval->v.s, &fval, c_data) < 0) ++ goto err; ++ ++ if(pvh_set_xavp(msg, &xavp_name, &hname, &c_data->value, SR_XTYPE_STR, 0, 0) ++ < 0) ++ goto err; ++ ++ if(pvh_set_xavp( ++ msg, &xavp_parsed_xname, &hname, c_data, SR_XTYPE_DATA, 0, 0) ++ < 0) ++ goto err; ++ ++ pvh_str_free(&hname); ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ return 1; + +err: -+ if (pv_format) -+ pv_elem_free_all(pv_format); -+ return -1; ++ pvh_str_free(&hname); ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ return -1; +} + -+int pv_get_branch_index(struct sip_msg *msg, int *br_idx) ++int pvh_merge_uri(struct sip_msg *msg, enum action_type type, str *cur, ++ str *new, xavp_c_data_t *c_data) +{ -+ int os = 0; -+ int len = 0; -+ char parsed_br_idx[header_value_size]; -+ -+ if (msg->add_to_branch_len > header_value_size) { -+ LM_ERR("branch name is too long\n"); -+ return -1; -+ } -+ -+ os = msg->add_to_branch_len; -+ while (os > 0 && memcmp(msg->add_to_branch_s+os-1, ".", 1)) os--; -+ len = msg->add_to_branch_len-os; -+ if (os > 0 && len > 0) { -+ memcpy(parsed_br_idx, msg->add_to_branch_s+os, len); -+ parsed_br_idx[len] = '\0'; -+ *br_idx = atoi(parsed_br_idx)+1; -+ } else { -+ *br_idx = 0; -+ } -+ -+ return 1; ++ struct sip_uri puri; ++ struct to_body tb; ++ struct to_param *param = NULL; ++ struct to_param *sparam_start = NULL; ++ struct to_param **sparam = NULL; ++ str *merged = NULL; ++ char *c_ptr = NULL; ++ str uri_t; ++ int os = 0; ++ int t_len = 0; ++ ++ parse_addr_spec(cur->s, cur->s + cur->len, &tb, 0); ++ if(!tb.uri.s) { ++ LM_ERR("cannot parse addr spec\n"); ++ goto err; ++ } ++ ++ if(parse_uri(tb.uri.s, tb.uri.len, &tb.parsed_uri) < 0) { ++ LM_ERR("cannot parse uri %.*s\n", tb.uri.len, tb.uri.s); ++ goto err; ++ } ++ puri = tb.parsed_uri; ++ ++ c_data->value.s = (char *)shm_malloc(header_value_size); ++ if(c_data->value.s == NULL) { ++ SHM_MEM_ERROR; ++ goto err; ++ } ++ merged = &c_data->value; ++ ++ if(type == SET_URI_T && strchr(new->s, '<')) { ++ pvh_str_copy(merged, new, header_value_size); ++ goto reparse; ++ } ++ ++ os = 0; ++ if(type == SET_USERPHONE_T) { ++ memcpy(merged->s + os, new->s, new->len); ++ os += new->len; ++ memcpy(merged->s + os, " ", 1); ++ os += 1; ++ } else if(tb.display.len > 0) { ++ memcpy(merged->s + os, tb.display.s, tb.display.len); ++ os += tb.display.len; ++ memcpy(merged->s + os, " ", 1); ++ os += 1; ++ } ++ memcpy(merged->s + os, "<", 1); ++ os += 1; ++ if(type != SET_URI_T) { ++ uri_type_to_str(puri.type, &uri_t); ++ t_len = uri_t.len + 1; ++ memcpy(merged->s + os, uri_t.s, uri_t.len); ++ os += uri_t.len; ++ memcpy(merged->s + os, ":", 1); ++ os += 1; ++ } ++ switch(type) { ++ case SET_USERPHONE_T: ++ memcpy(merged->s + os, tb.uri.s + t_len, tb.uri.len - t_len); ++ os += tb.uri.len - t_len; ++ break; ++ case SET_URI_T: ++ memcpy(merged->s + os, new->s, new->len); ++ os += new->len; ++ break; ++ case SET_USER_T: ++ memcpy(merged->s + os, new->s, new->len); ++ os += new->len; ++ memcpy(merged->s + os, tb.uri.s + t_len + puri.user.len, ++ tb.uri.len - t_len - puri.user.len); ++ os += tb.uri.len - t_len - puri.user.len; ++ break; ++ case SET_HOST_T: ++ if((c_ptr = strchr(tb.uri.s, '@')) == NULL) { ++ LM_ERR("invalid uri: %.*s\n", tb.uri.len, tb.uri.s); ++ goto err; ++ } ++ memcpy(merged->s + os, tb.uri.s + t_len, ++ c_ptr - tb.uri.s - t_len + 1); ++ os += c_ptr - tb.uri.s - t_len + 1; ++ memcpy(merged->s + os, new->s, new->len); ++ os += new->len; ++ memcpy(merged->s + os, c_ptr + puri.host.len + 1, ++ tb.uri.s + tb.uri.len - c_ptr - puri.host.len - 1); ++ os += tb.uri.s + tb.uri.len - c_ptr - puri.host.len - 1; ++ break; ++ default: ++ LM_ERR("unknown set uri op\n"); ++ goto err; ++ } ++ memcpy(merged->s + os, ">", 1); ++ os += 1; ++ if((param = tb.param_lst) != NULL) { ++ while(param) { ++ memcpy(merged->s + os, ";", 1); ++ os += 1; ++ memcpy(merged->s + os, param->name.s, param->name.len); ++ os += param->name.len; ++ memcpy(merged->s + os, "=", 1); ++ os += 1; ++ memcpy(merged->s + os, param->value.s, param->value.len); ++ os += param->value.len; ++ param = param->next; ++ } ++ } ++ merged->len = os; ++ merged->s[merged->len] = '\0'; ++ ++reparse: ++ ++ parse_addr_spec(merged->s, merged->s + merged->len, &c_data->to_b, 0); ++ if(!c_data->to_b.uri.s) { ++ LM_ERR("cannot parse addr spec\n"); ++ goto err; ++ } ++ ++ if((param = tb.param_lst) != NULL) { ++ while(param) { ++ if(sparam == NULL) ++ sparam = &sparam_start; ++ *sparam = (struct to_param *)shm_malloc(sizeof(struct to_param)); ++ if(*sparam == NULL) { ++ SHM_MEM_ERROR; ++ goto err; ++ } ++ memset(*sparam, 0, sizeof(struct to_param)); ++ memcpy(*sparam, param, sizeof(struct to_param)); ++ (*sparam)->next = NULL; ++ sparam = &(*sparam)->next; ++ param = param->next; ++ } ++ c_data->to_params = sparam_start; ++ } ++ ++ if(parse_uri(c_data->to_b.uri.s, c_data->to_b.uri.len, ++ &c_data->to_b.parsed_uri) ++ < 0) { ++ LM_ERR("cannot parse uri %.*s\n", c_data->to_b.uri.len, ++ c_data->to_b.uri.s); ++ goto err; ++ } ++ ++ free_to_params(&tb); ++ free_to_params(&c_data->to_b); ++ return 1; ++ ++err: ++ free_to_params(&tb); ++ free_to_params(&c_data->to_b); ++ return -1; +} + -+int pv_get_branch_xname(struct sip_msg *msg, str *xname, str *dst) ++int pvh_get_reply_sr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) +{ -+ int br_idx; -+ int os = 0; -+ char br_idx_s[32]; -+ char br_idx_len = 0; -+ -+ if (dst == NULL) -+ return -1; -+ -+ memset(dst->s, 0, dst->len); -+ memcpy(dst->s, xname->s, xname->len); os+=xname->len; -+ -+ pv_get_branch_index(msg, &br_idx); -+ if (br_idx > 0) { -+ sprintf(br_idx_s, "%d", br_idx-1); -+ br_idx_len = strlen(br_idx_s); -+ memcpy(dst->s+os, ".", 1); os+=1; -+ memcpy(dst->s+os, br_idx_s, br_idx_len); os+=br_idx_len; -+ } -+ if (msg->first_line.type == SIP_REPLY) { -+ memcpy(dst->s+os, ".r", 2); os+=2; -+ } -+ dst->len = os; -+ dst->s[dst->len] = '\0'; -+ -+ return 1; ++ sr_xval_t *xval = NULL; ++ int p_no = 0; ++ str rhname = {"@Reply-Reason", 13}; ++ ++ p_no = param->pvn.u.isname.name.n; ++ ++ if(msg->first_line.type != SIP_REPLY) ++ return pv_get_null(msg, param, res); ++ ++ switch(p_no) { ++ case 1: // status ++ return pv_get_intstrval(msg, param, res, ++ (int)msg->first_line.u.reply.statuscode, ++ &msg->first_line.u.reply.status); ++ break; ++ case 2: // reason ++ xval = pvh_xavp_get_value(msg, &xavp_name, &rhname, 0); ++ return pv_get_strval(msg, param, res, ++ xval && xval->v.s.s ? &xval->v.s ++ : &msg->first_line.u.reply.reason); ++ break; ++ default: ++ LM_ERR("unknown get reply op\n"); ++ } ++ ++ return pv_get_null(msg, param, res); +} + -+static int pv_clone_branch_xavp(struct sip_msg *msg, str *xname) ++int pvh_set_reply_sr( ++ struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) +{ -+ sr_xavp_t *xavp = NULL; -+ sr_xavp_t *br_xavp = NULL; -+ sr_xavp_t *sub = NULL; -+ sr_xval_t root_xval; -+ str br_xname = STR_NULL; -+ -+ if ((xavp = xavp_get(xname, NULL)) == NULL) { -+ LM_ERR("cannot clone xavp from non existing %s\n" , xname->s); -+ return -1; -+ } -+ -+ if (xavp->val.type != SR_XTYPE_XAVP) { -+ LM_ERR("not xavp child type %s\n" , xavp_name.s); -+ return -1; -+ } -+ -+ if ((sub = xavp->val.v.xavp) == NULL) { -+ LM_ERR("invalid xavp structure: %s\n", xavp_name.s); -+ return -1; -+ } -+ -+ if (pv_str_new(&br_xname, header_name_size) < 0) -+ return -1; -+ pv_get_branch_xname(msg, xname, &br_xname); -+ -+ memset(&root_xval, 0, sizeof(sr_xval_t)); -+ root_xval.type = SR_XTYPE_XAVP; -+ root_xval.v.xavp = NULL; -+ -+ if ((br_xavp = xavp_add_value(&br_xname, &root_xval, NULL)) == NULL) { -+ LM_ERR("error create xavp %s\n", br_xname.s); -+ goto err; -+ } -+ -+ if (strncmp(xname->s, xavp_parsed_xname.s, xname->len) == 0) { -+ pv_str_free(&br_xname); -+ return 1; -+ } -+ -+ do { -+ if (pv_skip_header(&sub->name)) -+ continue; -+ if (sub->val.type == SR_XTYPE_DATA) -+ continue; -+ if (pv_xavp_append_value(&sub->name, &sub->val, &br_xavp->val.v.xavp) < 0) { -+ LM_ERR("cannot clone xavp %s\n", sub->name.s); -+ goto err; -+ } -+ } while ((sub = sub->next) != NULL); -+ -+ pv_str_free(&br_xname); -+ return 1; ++ pv_elem_p pv_format = NULL; ++ int p_no = 0; ++ unsigned int code = 0; ++ str rhname = {"@Reply-Reason", 13}; ++ str fval; ++ ++ p_no = param->pvn.u.isname.name.n; ++ ++ if(msg->first_line.type != SIP_REPLY) { ++ LM_ERR("set reply: not a reply message\n"); ++ goto err; ++ } ++ ++ if(val->flags & (PV_VAL_NULL)) { ++ LM_ERR("set reply: value cannot be null\n"); ++ goto err; ++ } ++ ++ if(val->flags & (PV_TYPE_INT | PV_VAL_INT)) { ++ if(pv_get_sintval(msg, param, val, val->ri) < 0) ++ goto err; ++ } ++ ++ if(pv_parse_format(&val->rs, &pv_format) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ ++ if(pv_printf_s(msg, pv_format, &fval) < 0) { ++ LM_ERR("cannot parse format: %.*s\n", val->rs.len, val->rs.s); ++ goto err; ++ } ++ ++ switch(p_no) { ++ case 1: // status ++ code = atoi(fval.s); ++ if(code < 100 || code > 699) { ++ LM_ERR("set reply: wrong status code: %d\n", code); ++ goto err; ++ } ++ if((code < 300 || msg->REPLY_STATUS < 300) ++ && (code / 100 != msg->REPLY_STATUS / 100)) { ++ LM_ERR("set reply: 1xx or 2xx replies cannot be changed or set " ++ "to\n"); ++ goto err; ++ } ++ msg->first_line.u.reply.statuscode = code; ++ msg->first_line.u.reply.status.s[2] = code % 10 + '0'; ++ code /= 10; ++ msg->first_line.u.reply.status.s[1] = code % 10 + '0'; ++ code /= 10; ++ msg->first_line.u.reply.status.s[0] = code + '0'; ++ break; ++ case 2: // reason ++ if(pvh_set_xavp(msg, &xavp_name, &rhname, &fval, SR_XTYPE_STR, 0, 0) ++ < 0) { ++ LM_ERR("set reply: cannot set reply reason\n"); ++ goto err; ++ } ++ break; ++ default: ++ LM_ERR("unknown set reply op\n"); ++ goto err; ++ } ++ ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ return 1; + +err: -+ pv_str_free(&br_xname); -+ return -1; ++ if(pv_format) ++ pv_elem_free_all(pv_format); ++ return -1; +} -diff --git a/src/modules/pv_headers/pv_headers.h b/src/modules/pv_headers/pv_headers.h +diff --git a/src/modules/pv_headers/pvh_xavp.h b/src/modules/pv_headers/pvh_xavp.h new file mode 100644 -index 0000000..3944f64 +index 0000000..1245a1a --- /dev/null -+++ b/src/modules/pv_headers/pv_headers.h -@@ -0,0 +1,30 @@ ++++ b/src/modules/pv_headers/pvh_xavp.h +@@ -0,0 +1,64 @@ +/* -+ * $Id$ -+ * + * PV Headers + * -+ * Copyright (C) 2018 Kirill Solomko ++ * Copyright (C) 2018 Kirill Solomko + * + * This file is part of SIP Router, a free SIP server. + * @@ -2646,8 +3375,44 @@ index 0000000..3944f64 + * + */ + -+#ifndef PV_HEADERS_H -+#define PV_HEADERS_H ++#ifndef PV_XAVP_H ++#define PV_XAVP_H + ++#include "../../core/parser/parse_uri.h" ++#include "../../core/xavp.h" ++#include "../../core/pvar.h" + -+#endif /* PV_HEADERS_H */ ++#include "pv_headers.h" ++ ++sr_xavp_t *pvh_xavp_new_value(str *name, sr_xval_t *val); ++int pvh_xavp_append_value(str *name, sr_xval_t *val, sr_xavp_t **start); ++int pvh_xavp_set_value(str *name, sr_xval_t *val, int idx, sr_xavp_t **start); ++sr_xval_t *pvh_xavp_get_value( ++ struct sip_msg *msg, str *xname, str *name, int idx); ++sr_xavp_t *pvh_xavp_get_child(struct sip_msg *msg, str *xname, str *name); ++int pvh_xavp_is_null(sr_xavp_t *xavp); ++void pvh_xavp_free_data(void *p, sr_xavp_sfree_f sfree); ++int pvh_xavp_keys_count(sr_xavp_t **start); ++void pvh_free_to_params(struct to_param *param, sr_xavp_sfree_f sfree); ++int pvh_set_xavp(struct sip_msg *msg, str *xname, str *name, void *data, ++ sr_xtype_t type, int idx, int append); ++int pvh_free_xavp(str *xname); ++int pvh_parse_header_name(pv_spec_p sp, str *hname); ++ ++int pvh_get_header(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); ++int pvh_set_header( ++ struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); ++int pvh_get_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); ++int pvh_set_uri( ++ struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); ++int pvh_merge_uri(struct sip_msg *msg, enum action_type type, str *cur, ++ str *new, xavp_c_data_t *c_data); ++int pvh_get_reply_sr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); ++int pvh_set_reply_sr( ++ struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); ++ ++int pvh_get_branch_index(struct sip_msg *msg, int *br_idx); ++int pvh_get_branch_xname(struct sip_msg *msg, str *xname, str *dst); ++int pvh_clone_branch_xavp(struct sip_msg *msg, str *xname); ++ ++#endif /* PV_XAVP_H */