diff --git a/modules/siprec/doc/siprec_admin.xml b/modules/siprec/doc/siprec_admin.xml index 409716e181f..dc5b249fa5c 100644 --- a/modules/siprec/doc/siprec_admin.xml +++ b/modules/siprec/doc/siprec_admin.xml @@ -273,8 +273,7 @@ modparam("siprec", "skip_failover_codes", "[34][0-9][0-9]") Exported Functions
- <function moreinfo="none">siprec_start_recording(srs, [group], [caller], - [callee], [rtpproxy_sock], [media_ip], [headers])</function> + <function moreinfo="none">siprec_start_recording(srs[, rtpproxy_sock])</function> Calling this function on an initial @@ -299,21 +298,6 @@ modparam("siprec", "skip_failover_codes", "[34][0-9][0-9]") for more information. - - group (string, optional) - an opaque values - used by the SIPREC protocol to group calls in certain - profiles. - - - caller (string, optional) - an XML block - containing information about the caller. If absent, the - From header is used to build the value from. - - - callee (string, optional) - an XML block - containing information about the callee. If absent, the - To header is used to build the value from. - rtpproxy_sock (string, optional) - the RTPProxy socket used for this call. If absent, the rtpproxy @@ -321,17 +305,6 @@ modparam("siprec", "skip_failover_codes", "[34][0-9][0-9]") based on the default set provisioned in the rtpproxy module. - - media_ip (string, optional) - the IP that - RTPProxy will be streaming media from. If absent - 127.0.0.1 will be used. - - - headers (string, optional) - extra headers - that are to be added in the initial request towards the SRS. - NOTE: headers must be separated by - \r\n and must end with \r\n. - @@ -437,5 +410,57 @@ modparam("siprec", "skip_failover_codes", "[34][0-9][0-9]")
+
+ Exported Pseudo-Variables +
+ <varname>$siprec</varname> + + Used to modify/describe different siprec sessions + parameters that should be taken into account by the + function. + + + The context of this variable is only limited to the current + message processed - it is not available at the transaction + or dialog level. + + + Any of this setting is optional. + + + Settings that can be provisioned: + + + + group - an apaque value that will be inserted + in the SIPREC body and represents the name of the group that can be + used to clasify calls in certain profiles. If missing, no group is added. + + + caller - an XML block containing information + about the caller. If absent, the From header + is used to build the value. + + + callee - an XML block containing information + about the callee. If absent, the To header + is used to build the value. + + + media_ip - the IP that + RTPProxy will be streaming media from. If absent + 127.0.0.1 will be used. + + + headers - extra headers + that are to be added in the initial request towards the SRS. + NOTE: headers must be separated by + \r\n and must end with \r\n. + + +
+ +
+ diff --git a/modules/siprec/siprec.c b/modules/siprec/siprec.c index 7e5badf6555..6780dd74b4f 100644 --- a/modules/siprec/siprec.c +++ b/modules/siprec/siprec.c @@ -31,14 +31,14 @@ #include "siprec_sess.h" #include "siprec_logic.h" +#include "siprec_var.h" static int mod_preinit(void); static int mod_init(void); static int child_init(int); static void mod_destroy(void); -static int siprec_start_rec(struct sip_msg *msg, str *srs, str *group, - str *_cA, str *_cB, str *rtp, str *m_ip, str *_hdrs); +static int siprec_start_rec(struct sip_msg *msg, str *srs, str *rtp); static int siprec_pause_rec(struct sip_msg *msg); static int siprec_resume_rec(struct sip_msg *msg); @@ -61,11 +61,6 @@ static dep_export_t deps = { static cmd_export_t cmds[] = { {"siprec_start_recording",(cmd_function)siprec_start_rec, { {CMD_PARAM_STR,0,0}, - {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, - {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, - {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, - {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, - {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {0,0,0}}, REQUEST_ROUTE|ONREPLY_ROUTE}, {"siprec_pause_recording",(cmd_function)siprec_pause_rec, @@ -83,6 +78,13 @@ static param_export_t params[] = { {0, 0, 0} }; +static pv_export_t vars[] = { + { {"siprec", sizeof("siprec")-1}, 1000, + pv_get_siprec, pv_set_siprec, pv_parse_siprec, + 0, 0, 0 }, +}; + + /* module exports */ struct module_exports exports = { "siprec", /* module name */ @@ -96,7 +98,7 @@ struct module_exports exports = { params, /* exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ - 0, /* exported pseudo-variables */ + vars, /* exported pseudo-variables */ 0, /* extra processes */ 0, /* extra transformations */ mod_preinit, /* module pre-initialization function */ @@ -134,6 +136,11 @@ static int mod_preinit(void) srec_dlg_idx = srec_dlg.dlg_ctx_register_ptr(NULL); + if (init_srec_var() < 0) { + LM_ERR("cannot initialize siprec variable!\n"); + return -1; + } + return 0; } @@ -188,13 +195,13 @@ static void tm_src_unref_session(void *p) /* * function that simply prints the parameters passed */ -static int siprec_start_rec(struct sip_msg *msg, str *srs, str *group, - str *_cA, str *_cB, str *rtp, str *m_ip, str *_hdrs) +static int siprec_start_rec(struct sip_msg *msg, str *srs, str *rtp) { int ret; str *aor, *display, *xml_val; struct src_sess *ss; struct dlg_cell *dlg; + struct srec_var *var; /* create the dialog, if does not exist yet */ dlg = srec_dlg.get_dlg(); @@ -206,11 +213,17 @@ static int siprec_start_rec(struct sip_msg *msg, str *srs, str *group, dlg = srec_dlg.get_dlg(); } + var = get_srec_var(); + /* XXX: if there is a forced send socket in the message, use it * this is the only way to provide a different socket for SRS, but * we might need to take a different approach */ /* check if the current dialog has a siprec session ongoing */ - if (!(ss = src_new_session(srs, rtp, m_ip, group, _hdrs, msg->force_send_socket))) { + if (!(ss = src_new_session(srs, rtp, + (var && var->media.len?&var->media:NULL), + (var && var->group.len?&var->group:NULL), + (var && var->headers.len?&var->headers:NULL), + msg->force_send_socket))) { LM_ERR("cannot create siprec session!\n"); return -2; } @@ -224,8 +237,8 @@ static int siprec_start_rec(struct sip_msg *msg, str *srs, str *group, ret = -2; /* caller info */ - if (_cA) { - xml_val = _cA; + if (var && var->caller.len) { + xml_val = &var->caller; display = aor = NULL; } else { if (parse_from_header(msg) < 0) { @@ -246,8 +259,8 @@ static int siprec_start_rec(struct sip_msg *msg, str *srs, str *group, goto session_cleanup; } /* caller info */ - if (_cB) { - xml_val = _cB; + if (var && var->callee.len) { + xml_val = &var->callee; } else { if ((!msg->to && parse_headers(msg, HDR_TO_F, 0) < 0) || !msg->to) { LM_ERR("inexisting or invalid to header!\n"); diff --git a/modules/siprec/siprec_var.c b/modules/siprec/siprec_var.c new file mode 100644 index 00000000000..84475921228 --- /dev/null +++ b/modules/siprec/siprec_var.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2021 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "siprec_var.h" +#include "../../ut.h" +#include "../../context.h" + +#define SIPREC_VAR_INVAID_ID (-1) +#define SIPREC_VAR_GROUP_ID (1 << 0) +#define SIPREC_VAR_CALLER_ID (1 << 1) +#define SIPREC_VAR_CALLEE_ID (1 << 2) +#define SIPREC_VAR_HEADERS_ID (1 << 3) +#define SIPREC_VAR_MEDIA_ID (1 << 4) + +struct { + const char *name; + const int id; +} siprec_var_names[] = { + {"group", SIPREC_VAR_GROUP_ID}, + {"caller", SIPREC_VAR_CALLER_ID}, + {"callee", SIPREC_VAR_CALLEE_ID}, + {"media_ip", SIPREC_VAR_MEDIA_ID}, + {"headers", SIPREC_VAR_HEADERS_ID}, +}; + +static int srec_msg_idx; + +#define SIPREC_GET_VAR() (struct srec_var *)\ + (context_get_ptr(CONTEXT_GLOBAL, current_processing_ctx, srec_msg_idx)) + +struct srec_var *get_srec_var(void) +{ + return SIPREC_GET_VAR(); +} + +static struct srec_var *get_srec_var_new(void) +{ + struct srec_var *var = SIPREC_GET_VAR(); + if (var) + return var; + /* not found - create a new one */ + var = pkg_malloc(sizeof *var); + if (!var) { + LM_ERR("oom for siprec var!\n"); + return NULL; + } + memset(var, 0, sizeof *var); + context_put_ptr(CONTEXT_GLOBAL, current_processing_ctx, srec_msg_idx, var); + return var; +} + +static void free_srec_var(void *v) +{ + struct srec_var *sv = (struct srec_var *)v; + if (sv->group.s) + pkg_free(sv->group.s); + if (sv->caller.s) + pkg_free(sv->caller.s); + if (sv->callee.s) + pkg_free(sv->callee.s); + if (sv->media.s) + pkg_free(sv->media.s); + if (sv->headers.s) + pkg_free(sv->headers.s); + pkg_free(sv); +} + + +static int pv_parse_siprec_name(const str *name) +{ + int s; + for (s = 0; s < (sizeof(siprec_var_names)/sizeof(siprec_var_names[0])); s++) { + if (str_match_nt(name, siprec_var_names[s].name)) + return siprec_var_names[s].id; + } + LM_ERR("unknwon siprec variable %.*s\n", name->len, name->s); + return SIPREC_VAR_INVAID_ID; +} + +static int pv_parse_siprec_get_name(struct sip_msg *msg, pv_param_t *p) +{ + pv_value_t tv; + + if (p->pvn.type == PV_NAME_INTSTR) + return p->pvn.u.isname.type; + + if(pv_get_spec_value(msg, (const pv_spec_p)(p->pvn.u.dname), &tv)!=0) + { + LM_ERR("cannot get siprec value\n"); + return SIPREC_VAR_INVAID_ID; + } + + if(tv.flags&PV_VAL_NULL || tv.flags&PV_VAL_EMPTY) + { + LM_ERR("null or empty name\n"); + return -1; + } + + if(!(tv.flags&PV_VAL_STR)) + tv.rs.s = int2str(tv.ri, &tv.rs.len); + + return pv_parse_siprec_name(&tv.rs); +} + + +int pv_parse_siprec(pv_spec_p sp, const str *in) +{ + char *p; + char *s; + pv_spec_p nsp = 0; + + if(in==NULL || in->s==NULL || sp==NULL) + return -1; + p = in->s; + if(*p==PV_MARKER) + { + nsp = (pv_spec_p)pkg_malloc(sizeof(pv_spec_t)); + if(nsp==NULL) + { + LM_ERR("no more memory\n"); + return -1; + } + s = pv_parse_spec(in, nsp); + if(s==NULL) + { + LM_ERR("invalid name [%.*s]\n", in->len, in->s); + pv_spec_free(nsp); + return -1; + } + sp->pvp.pvn.type = PV_NAME_PVAR; + sp->pvp.pvn.u.dname = (void*)nsp; + return 0; + } + sp->pvp.pvn.type = PV_NAME_INTSTR; + sp->pvp.pvn.u.isname.type = pv_parse_siprec_name(in); + return (sp->pvp.pvn.u.isname.type == SIPREC_VAR_INVAID_ID)?-1:0; +} + +int pv_get_siprec(struct sip_msg *msg, pv_param_t *param, + pv_value_t *val) +{ + str *field = NULL; + struct srec_var *sv = SIPREC_GET_VAR(); + if (!sv) + return pv_get_null(msg, param, val); + + switch (pv_parse_siprec_get_name(msg, param)) { + case SIPREC_VAR_GROUP_ID: + field = &sv->group; + break; + case SIPREC_VAR_CALLER_ID: + field = &sv->caller; + break; + case SIPREC_VAR_CALLEE_ID: + field = &sv->callee; + break; + case SIPREC_VAR_MEDIA_ID: + field = &sv->media; + break; + case SIPREC_VAR_HEADERS_ID: + field = &sv->headers; + break; + default: + LM_BUG("unknown field!\n"); + case SIPREC_VAR_INVAID_ID: + return -1; + } + if (!field) { + LM_BUG("unknown field!\n"); + return -1; + } + + if (field->len < 0) + return pv_get_null(msg, param, val); + + val->rs = *field; + val->flags = PV_VAL_STR; + + return 0; +} + +int pv_set_siprec(struct sip_msg* msg, pv_param_t *param, + int op, pv_value_t *val) +{ + int rc; + str *field = NULL, tmp; + struct srec_var *sv = get_srec_var_new(); + if (!sv) + return -1; + + switch (pv_parse_siprec_get_name(msg, param)) { + case SIPREC_VAR_GROUP_ID: + field = &sv->group; + break; + case SIPREC_VAR_CALLER_ID: + field = &sv->caller; + break; + case SIPREC_VAR_CALLEE_ID: + field = &sv->callee; + break; + case SIPREC_VAR_MEDIA_ID: + field = &sv->media; + break; + case SIPREC_VAR_HEADERS_ID: + field = &sv->headers; + break; + default: + LM_BUG("unknown field %d!\n", pv_parse_siprec_get_name(msg, param)); + case SIPREC_VAR_INVAID_ID: + return -1; + } + if (!field) { + LM_BUG("unknown field!\n"); + return -1; + } + if (!(val->flags & PV_VAL_STR)) { + tmp.s = int2str(val->ri, &tmp.len); + rc = pkg_str_dup(field, &tmp); + } else { + rc = pkg_str_dup(field, &val->rs); + } + + return rc; +} + +int init_srec_var(void) +{ + srec_msg_idx = context_register_ptr(CONTEXT_GLOBAL, free_srec_var); + return 0; +} diff --git a/modules/siprec/siprec_var.h b/modules/siprec/siprec_var.h new file mode 100644 index 00000000000..bc29ff8ebf0 --- /dev/null +++ b/modules/siprec/siprec_var.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 OpenSIPS Project + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _SIPREC_VAR_H_ +#define _SIPREC_VAR_H_ + +#include "../../str.h" +#include "../../pvar.h" + +struct srec_var { + str group, caller, callee, media, headers; +}; + +int init_srec_var(void); +int pv_parse_siprec(pv_spec_p sp, const str *in); +int pv_get_siprec(struct sip_msg *msg, pv_param_t *param, + pv_value_t *val); +int pv_set_siprec(struct sip_msg* msg, pv_param_t *param, + int op, pv_value_t *val); + +struct srec_var *get_srec_var(void); + +#endif /* _SIPREC_VAR_H_ */