From a37140bef370cdfab88dbff87bedc2e2e6922e6b Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 28 Nov 2019 13:44:23 +0100 Subject: [PATCH] pv_headers: Initial commit 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. --- src/modules/pv_headers/Makefile | 12 + src/modules/pv_headers/README | 337 ++++++ 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 + .../pv_headers/doc/pv_headers_admin.xml | 116 ++ src/modules/pv_headers/pv_headers.c | 351 ++++++ src/modules/pv_headers/pv_headers.h | 51 + src/modules/pv_headers/pvh_func.c | 388 ++++++ src/modules/pv_headers/pvh_func.h | 38 + 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 + 19 files changed, 3394 insertions(+) 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_func.c create mode 100644 src/modules/pv_headers/pvh_func.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/modules/pv_headers/Makefile b/src/modules/pv_headers/Makefile new file mode 100644 index 00000000000..42b80dd612b --- /dev/null +++ b/src/modules/pv_headers/Makefile @@ -0,0 +1,12 @@ +# +# pv_headers module makefile +# +# +# WARNING: do not run this directly, it should be run by the master Makefile + +include ../../Makefile.defs +auto_gen= +NAME=pv_headers.so +LIBS= + +include ../../Makefile.modules diff --git a/src/modules/pv_headers/README b/src/modules/pv_headers/README new file mode 100644 index 00000000000..196752939b1 --- /dev/null +++ b/src/modules/pv_headers/README @@ -0,0 +1,337 @@ +pv_headers Module + +Kirill Solomko + + Sipwise GmbH + + +Edited by + +Victor Seva + + Sipwise GmbH + + + Copyright © 2018 Sipwise GmbH + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 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. Functions + + 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, hvalue, [idx]) + 4.7. pvh_remove_header(hname, [idx]) + + 5. Exported Variables + + List of Examples + + 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. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 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. Functions + + 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, hvalue, [idx]) + 4.7. pvh_remove_header(hname, [idx]) + + 5. Exported Variables + +1. 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. + +2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + +2.1. Kamailio Modules + + The following modules must be loaded before this module: + * uac. + * tm. + Needed only if "auto_msg" parameter is set to 1. + +2.2. External Libraries or Applications + + The following libraries or applications must be installed before + running Kamailio with this module loaded: + * None. + +3. Parameters + + 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) + +3.1. xavp_name (string) + + Name of the XAVP there the collected headers are stored. + + Default value is "headers". + + Example 1.1. Set xavp_name parameter +... +modparam("pv_headers", "xavp_name", "pvh") +... + + Result: $xavp(headers[0]=>From) $xavp(headers[0]=>To) + $xavp(headers[0]=>Call-ID) + +3.2. header_value_size (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. + + Example 1.2. Set header_value_size parameter +... +modparam("pv_headers", "header_value_size", 512) +... + +3.3. header_collect_flag (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. + + Example 1.3. Set header_collect_flag parameter +... +modparam("pv_headers", "header_collect_flag", 37) +... + +3.4. header_apply_flag (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. + + Example 1.4. Set header_apply_flag parameter +... +modparam("pv_headers", "header_apply_flag", 38) +... + +3.5. skip_headers (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". + + Example 1.5. Set skip_headers parameter +... +modparam("pv_headers", "skip_headers", "Record-Route,Via,Route") +... + +3.6. 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 set to an empty string then no headers are split. + + Default value is "". + + Example 1.6. Set split_headers parameter +... +modparam("pv_headers", "split_headers", "Diversion") +... + + Result: Received Diversion header: Diversion: + ,, After split: + Diversion: Diversion: Diversion: + + + Becomes handy if used together with pvh_modify_header() or + pvh_remove_header() to change or remove value 2 for instance. + +3.7. auto_msg (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). + + Example 1.7. Set auto_msg parameter +... +modparam("pvh", "auto_msg", 1) +... + +4. Functions + + 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, hvalue, [idx]) + 4.7. pvh_remove_header(hname, [idx]) + + \ + +4.1. pvh_collect_headers() + + 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. + +4.2. pvh_apply_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. + + 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. + +4.3. pvh_reset_headers() + + 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. + +4.4. pvh_check_header(hname) + + 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. + +4.5. pvh_append_header(hname, hvalue) + + 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. + +4.6. pvh_modify_header(hname, hvalue, [idx]) + + 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 "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. + + This function can be used from ANY_ROUTE but only after + pvh_collect_headers() or with "auto_msg" parameter enabled. + +4.7. pvh_remove_header(hname, [idx]) + + 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. + +5. 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 + 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 00000000000..b1431095741 --- /dev/null +++ b/src/modules/pv_headers/doc/Makefile @@ -0,0 +1,4 @@ +docs = pv_headers.xml + +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 00000000000..ef7f1337799 --- /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, hvalue, [idx])</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 00000000000..83e74d643eb --- /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 00000000000..a5211b92881 --- /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 00000000000..44e84f20c6d --- /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 00000000000..97308479ce3 --- /dev/null +++ b/src/modules/pv_headers/pv_headers.c @@ -0,0 +1,351 @@ +/* + * 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/sr_module.h" +#include "../../core/mod_fix.h" +#include "../../core/script_cb.h" +#include "../../modules/tm/tm_load.h" +#include "../../core/kemi.h" + +#include "pv_headers.h" +#include "pvh_func.h" +#include "pvh_xavp.h" +#include "pvh_hash.h" + +MODULE_VERSION + +#define MODULE_NAME "pv_headers" + +#define XAVP_NAME "headers" + +#define FL_NAME_PV_HDRS_COLLECTED "pv_headers_collected" +#define FL_NAME_PV_HDRS_APPLIED "pv_headers_applied" + +int FL_PV_HDRS_COLLECTED = 27; +int FL_PV_HDRS_APPLIED = 28; + +uac_api_t uac; +static tm_api_t tmb; + +str xavp_name = str_init(XAVP_NAME); + +str xavp_parsed_xname = str_init("parsed_pv_headers"); +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 str single_headers_param = str_init(""); + +str _hdr_from = {"From", 4}; +str _hdr_to = {"To", 2}; + +unsigned int header_name_size = 255; +unsigned int header_value_size = 1024; + + +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 w_pvh_collect_headers(struct sip_msg *msg, char *p1, char *p2) +{ + return pvh_collect_headers(msg, 0); +} + +static int ki_pvh_collect_headers(struct sip_msg *msg) +{ + return pvh_collect_headers(msg, 0); +} + +static int w_pvh_apply_headers(struct sip_msg *msg, char *p1, char *p2) +{ + return pvh_apply_headers(msg, 0); +} + +static int ki_pvh_apply_headers(struct sip_msg *msg) +{ + return pvh_apply_headers(msg, 0); +} + +static int w_pvh_reset_headers(struct sip_msg *msg, char *p1, char *p2) +{ + return pvh_reset_headers(msg); +} + +static int w_pvh_check_header(struct sip_msg *msg, char *p1, char *p2) +{ + str hname = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_p)p1, &hname) < 0) + return -1; + + return pvh_check_header(msg, &hname); +} + +static int w_pvh_append_header(struct sip_msg *msg, char *p1, char *p2) +{ + str hname = STR_NULL, hvalue = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_p)p1, &hname) < 0) + return -1; + + if(p2 && fixup_get_svalue(msg, (gparam_p)p2, &hvalue) < 0) + return -1; + + return pvh_append_header(msg, &hname, &hvalue); +} + +static int w_pvh_modify_header( + struct sip_msg *msg, char *p1, char *p2, char *p3) +{ + int indx = 0; + str hname = STR_NULL, hvalue = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_p)p1, &hname) < 0) + return -1; + + if(p2 && fixup_get_svalue(msg, (gparam_p)p2, &hvalue) < 0) + return -1; + + if(p3 && fixup_get_ivalue(msg, (gparam_p)p3, &indx) < 0) + return -1; + + return pvh_modify_header(msg, &hname, &hvalue, indx); +} + +static int w_pvh_remove_header( + struct sip_msg *msg, char *p1, char *p2, char *p3) +{ + int indx = -1; + str hname = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_p)p1, &hname) < 0) + return -1; + + if(p2 && fixup_get_ivalue(msg, (gparam_p)p2, &indx) < 0) + return -1; + + return pvh_remove_header(msg, &hname, indx); +} + +/* + * Exported functions + */ +static cmd_export_t cmds[] = { + {"pvh_collect_headers", (cmd_function)w_pvh_collect_headers, 0, 0, 0, + ANY_ROUTE}, + {"pvh_apply_headers", (cmd_function)w_pvh_apply_headers, 0, 0, 0, + ANY_ROUTE}, + {"pvh_reset_headers", (cmd_function)w_pvh_reset_headers, 0, 0, 0, + ANY_ROUTE}, + {"pvh_check_header", (cmd_function)w_pvh_check_header, 1, + fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, + {"pvh_append_header", (cmd_function)w_pvh_append_header, 2, + fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, + {"pvh_modify_header", (cmd_function)w_pvh_modify_header, 2, + fixup_spve_spve, fixup_free_spve_spve, ANY_ROUTE}, + {"pvh_modify_header", (cmd_function)w_pvh_modify_header, 3, + fixup_spve_all, fixup_free_spve_all, ANY_ROUTE}, + {"pvh_remove_header", (cmd_function)w_pvh_remove_header, 1, + fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, + {"pvh_remove_header", (cmd_function)w_pvh_remove_header, 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, 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 */ +}; + +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; + } + + 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"); + + return 0; +} + +void mod_destroy(void) +{ + 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); +} + +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) { + pvh_reset_headers(msg); + pvh_collect_headers(msg, 1); + } + } 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) + pvh_apply_headers(msg, 1); + + return; +} + +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; + } + pvh_collect_headers(msg, 1); + } else { + LM_ERR("unknown callback: %d\n", flags); + } + + return 1; +} + +static sr_kemi_t pvh_kemi_exports[] = { + {str_init("pv_headers"), str_init("pvh_collect_headers"), SR_KEMIP_INT, + ki_pvh_collect_headers, + {SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_apply_headers"), SR_KEMIP_INT, + ki_pvh_apply_headers, + {SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_reset_headers"), SR_KEMIP_INT, + pvh_reset_headers, + {SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_check_header"), SR_KEMIP_INT, + pvh_check_header, + {SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_append_header"), SR_KEMIP_INT, + pvh_check_header, + {SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_modify_header"), SR_KEMIP_INT, + pvh_modify_header, + {SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {str_init("pv_headers"), str_init("pvh_remove_header"), SR_KEMIP_INT, + pvh_remove_header, + {SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE}}, + {{0, 0}, {0, 0}, 0, NULL, {0, 0, 0, 0, 0, 0}}}; + +int mod_register(char *path, int *dlflags, void *p1, void *p2) +{ + sr_kemi_modules_add(pvh_kemi_exports); + return 0; +} diff --git a/src/modules/pv_headers/pv_headers.h b/src/modules/pv_headers/pv_headers.h new file mode 100644 index 00000000000..556b7bfcef1 --- /dev/null +++ b/src/modules/pv_headers/pv_headers.h @@ -0,0 +1,51 @@ +/* + * 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_HEADERS_H +#define PV_HEADERS_H + +#include "../../core/parser/parse_addr_spec.h" +#include "../../modules/uac/api.h" + +typedef struct _xavp_c_data +{ + struct to_body to_b; + struct to_param *to_params; + str value; +} xavp_c_data_t; + +extern uac_api_t uac; + +extern str xavp_name; +extern str xavp_parsed_xname; + +extern unsigned int header_name_size; +extern unsigned int header_value_size; + +extern str _hdr_from; +extern str _hdr_to; + +extern int FL_PV_HDRS_COLLECTED; +extern int FL_PV_HDRS_APPLIED; + +#endif /* PV_HEADERS_H */ diff --git a/src/modules/pv_headers/pvh_func.c b/src/modules/pv_headers/pvh_func.c new file mode 100644 index 00000000000..1340500cc4f --- /dev/null +++ b/src/modules/pv_headers/pvh_func.c @@ -0,0 +1,388 @@ +/* + * 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/dset.h" + +#include "pv_headers.h" +#include "pvh_str.h" +#include "pvh_hdr.h" +#include "pvh_hash.h" +#include "pvh_xavp.h" + +static str xavp_helper_xname = str_init("modparam_pv_headers"); +static str xavp_helper_name = str_init("xavp_name"); + +int pvh_collect_headers(struct sip_msg *msg, int is_auto) +{ + 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) { + if(msg->first_line.type == SIP_REPLY) { + if(isflagset(msg, FL_PV_HDRS_COLLECTED) == 1) { + LM_ERR("headers are already collected\n"); + return -1; + } + } else { + if(isbflagset(br_idx, FL_PV_HDRS_COLLECTED)) { + 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(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); + + switch(hf->type) { + case HDR_FROM_T: + pvh_str_copy(&name, &_hdr_from, header_name_size); + LM_DBG("force [From] as key\n"); + break; + case HDR_TO_T: + pvh_str_copy(&name, &_hdr_to, header_name_size); + LM_DBG("force [To] as key\n"); + break; + default: + pvh_str_copy(&name, &hf->name, header_name_size); + } + pvh_str_copy(&val, &hf->body, header_value_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; + } + + if(pvh_set_xavp(msg, &xavp_helper_xname, &xavp_helper_name, &xavp_name, + SR_XTYPE_STR, 0, 0) + < 0) + goto err; + + pvh_str_free(&name); + pvh_str_free(&val); + + msg->first_line.type == SIP_REPLY ? setflag(msg, FL_PV_HDRS_COLLECTED) + : setbflag(br_idx, FL_PV_HDRS_COLLECTED); + + return 1; + +err: + pvh_str_free(&name); + pvh_str_free(&val); + return -1; +} + +int pvh_apply_headers(struct sip_msg *msg, int is_auto) +{ + 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) { + if(msg->first_line.type == SIP_REPLY) { + if(isflagset(msg, FL_PV_HDRS_APPLIED) == 1) { + LM_ERR("headers are already applied\n"); + return -1; + } + } else { + if(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; + + pvh_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 = 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, _hdr_from.s, 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, _hdr_to.s, 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; + +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 pvh_reset_headers(struct sip_msg *msg) +{ + str br_xname = STR_NULL; + int br_idx; + + if(pvh_str_new(&br_xname, header_name_size) < 0) + return -1; + + pvh_get_branch_index(msg, &br_idx); + pvh_get_branch_xname(msg, &xavp_name, &br_xname); + + pvh_free_xavp(&br_xname); + pvh_get_branch_xname(msg, &xavp_parsed_xname, &br_xname); + pvh_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); + } + + pvh_str_free(&br_xname); + + return 1; +} + +int pvh_check_header(struct sip_msg *msg, str *hname) +{ + + if(pvh_xavp_get_child(msg, &xavp_name, hname) == NULL) + return -1; + + return 1; +} + +int pvh_append_header(struct sip_msg *msg, str *hname, str *hvalue) +{ + return pvh_set_xavp(msg, &xavp_name, hname, hvalue, SR_XTYPE_STR, 0, 1); +} + +int pvh_modify_header(struct sip_msg *msg, str *hname, str *hvalue, int indx) +{ + return pvh_set_xavp(msg, &xavp_name, hname, hvalue, SR_XTYPE_STR, indx, 0); +} + +int pvh_remove_header(struct sip_msg *msg, str *hname, int indx) +{ + sr_xavp_t *avp = NULL; + int count = 0; + + if((avp = pvh_xavp_get_child(msg, &xavp_name, hname)) == NULL) + return 1; + + if(indx < 0) { + count = xavp_count(hname, &avp); + do { + if(pvh_set_xavp( + msg, &xavp_name, hname, NULL, SR_XTYPE_STR, indx++, 0) + < 1) + return -1; + } while(indx < count); + } else { + if(pvh_set_xavp(msg, &xavp_name, hname, NULL, SR_XTYPE_STR, indx, 0) + < 1) + return -1; + } + + return 1; +} diff --git a/src/modules/pv_headers/pvh_func.h b/src/modules/pv_headers/pvh_func.h new file mode 100644 index 00000000000..0fe55242fc7 --- /dev/null +++ b/src/modules/pv_headers/pvh_func.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_FUNC_H +#define PV_FUNC_H + +#include "../../core/parser/msg_parser.h" + +int pvh_collect_headers(struct sip_msg *msg, int is_auto); +int pvh_apply_headers(struct sip_msg *msg, int is_auto); +int pvh_reset_headers(struct sip_msg *msg); + +int pvh_check_header(struct sip_msg *msg, str *hname); +int pvh_append_header(struct sip_msg *msg, str *hname, str *hvalue); +int pvh_modify_header(struct sip_msg *msg, str *hname, str *hvalue, int indx); +int pvh_remove_header(struct sip_msg *msg, str *hname, int indx); + +#endif /* PV_FUNC_H */ diff --git a/src/modules/pv_headers/pvh_hash.c b/src/modules/pv_headers/pvh_hash.c new file mode 100644 index 00000000000..f269868c46d --- /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 + * + */ + +#include "pv_headers.h" +#include "pvh_hash.h" +#include "pvh_str.h" + +struct str_hash_table skip_headers; +struct str_hash_table split_headers; +struct str_hash_table single_headers; + +int pvh_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(pvh_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) { + 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; + } + } + + return 1; +} + +int pvh_str_hash_add_key(struct str_hash_table *ht, str *key) +{ + struct str_hash_entry *e = NULL; + int e_size; + + if(ht->table == NULL || key == NULL || key->len == 0) + 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(pvh_str_new(&e->key, key->len + 1) < 0) + goto err; + pvh_str_copy(&e->key, key, key->len + 1); + + str_hash_add(ht, e); + return 1; + +err: + pvh_str_free(&e->key); + return -1; +} + +int pvh_str_hash_free(struct str_hash_table *ht) +{ + 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 1; +} + +int pvh_skip_header(str *hname) +{ + if(hname == NULL) + return 0; + + if(str_hash_get(&skip_headers, hname->s, hname->len)) + return 1; + + return 0; +} + +int pvh_single_header(str *hname) +{ + if(hname == NULL) + return 0; + + if(str_hash_get(&single_headers, hname->s, hname->len)) + return 1; + + return 0; +} diff --git a/src/modules/pv_headers/pvh_hash.h b/src/modules/pv_headers/pvh_hash.h new file mode 100644 index 00000000000..8828cc6ec2c --- /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 00000000000..70be9ac868f --- /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 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(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("append header: %.*s\n", new_h.len, new_h.s); + + 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: + if(reason) + pkg_free(reason); + return -1; +} + +int pvh_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) { + 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 00000000000..f05d7e43176 --- /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 00000000000..53e2c9c1522 --- /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 00000000000..95aa0b9f0be --- /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 00000000000..de296b4bca5 --- /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" + +#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) +{ + 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 pvh_xavp_append_value(str *name, sr_xval_t *val, sr_xavp_t **start) +{ + 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 pvh_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; +} + +sr_xval_t *pvh_xavp_get_value( + struct sip_msg *msg, str *xname, str *name, int idx) +{ + 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: + pvh_str_free(&br_xname); + return NULL; +} + +sr_xavp_t *pvh_xavp_get_child(struct sip_msg *msg, str *xname, str *name) +{ + 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: + 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 pvh_get_branch_index(struct sip_msg *msg, int *br_idx) +{ + 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: + pvh_str_free(&br_xname); + return -1; +} + +int pvh_get_header(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) +{ + 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 = 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); + } + + 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: + if(pv_format) + pv_elem_free_all(pv_format); + return -1; +} + +int pvh_get_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t *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, &_hdr_from, header_name_size); + else if(p_no >= 6 && p_no <= 10) + pvh_str_copy(&hname, &_hdr_to, 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 pvh_set_uri(struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) +{ + 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, &_hdr_from, header_name_size); + else if(p_no >= 6 && p_no <= 10) + pvh_str_copy(&hname, &_hdr_to, 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: + pvh_str_free(&hname); + if(pv_format) + pv_elem_free_all(pv_format); + return -1; +} + +int pvh_merge_uri(struct sip_msg *msg, enum action_type type, str *cur, + str *new, xavp_c_data_t *c_data) +{ + 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 pvh_get_reply_sr(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 = 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); +} + +int pvh_set_reply_sr( + 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(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: + if(pv_format) + pv_elem_free_all(pv_format); + return -1; +} diff --git a/src/modules/pv_headers/pvh_xavp.h b/src/modules/pv_headers/pvh_xavp.h new file mode 100644 index 00000000000..1245a1a1f47 --- /dev/null +++ b/src/modules/pv_headers/pvh_xavp.h @@ -0,0 +1,64 @@ +/* + * 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_XAVP_H +#define PV_XAVP_H + +#include "../../core/parser/parse_uri.h" +#include "../../core/xavp.h" +#include "../../core/pvar.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 */