Skip to content

Commit

Permalink
siputils: added tel2sip2() function
Browse files Browse the repository at this point in the history
- based on proposal presented at:
  #1173 (comment)
- alternative to tel2sip() which has a simpler approach, but it does not
  follow all the RFC requirements. This is still kept as it proved to be
  useful over the years

Co-authored-by: Donald Carr <doncarr@gmail.com>
Co-authored-by: Supreeth Herle <herlesupreeth@gmail.com>
  • Loading branch information
3 people committed Dec 29, 2023
1 parent 453a8f1 commit 366e286
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 0 deletions.
225 changes: 225 additions & 0 deletions src/modules/siputils/checks.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,231 @@ int tel2sip(struct sip_msg *_msg, char *_uri, char *_hostpart, char *_res)
}


/*
* Compare function to sort tel: uri options acording to standard
* before inserting into sip: uri
*
* See "RFC 3261 SIP: Session Initiation Protocol June 2002"
* 19.1.6 Relating SIP URIs and tel URLs
*/
typedef struct
{
char *name;
char *value;
} tel_param_t;

#define MAX_TEL_PARAMS 10

int compare_tel_options(const void *v1, const void *v2)
{
tel_param_t *p1 = (tel_param_t *)v1;
tel_param_t *p2 = (tel_param_t *)v2;

if(0 == strcasecmp(p1->name, "isdn-subaddress")) {
return -1;
} else if(0 == strcasecmp(p2->name, "isdn-subaddress")) {
return 1;
} else if(0 == strcasecmp(p1->name, "post-dial")) {
return -1;
} else if(0 == strcasecmp(p2->name, "post-dial")) {
return 1;
} else {
return strcasecmp(p1->name, p2->name);
}
}

/*
* Remove visual separators from the phone number
* Assume it has been validated as a number containing
* ONLY leading '+', digits, and visual separators.
*/
static void remove_visual_separators_from_phone(char *p)
{
char *p2;
p2 = p;
while(*p != '\0') {
/* Skip all visual separators */
while((*p != '\0')
&& ((*p == '.') || (*p == '-') || (*p == '(') || (*p == ')'))) {
p++;
}
*p2 = *p; /* Until the first visual separator, these both point to the same place. */
/* but, more efficient to just do it than an if statement each time. */
/* If we arrived at a terminator in the inner loop, time to exit */
if(*p == '\0')
return;
/* Now we increment both pointers. */
p++;
p2++;
}
*p2 = '\0'; /* Make sure that the string is terminated after the last valid digit. */
}

/*
* Check if this is a phone number.
* Assume possible leading '+'
* Assume separators '.', '-', '(', or ')' could be present.
*/
static int is_telnumber_format(const char *p)
{
if(*p == '+')
p++;
while(*p != '\0') {
if((!isdigit(*p)) && (*p != '.') && (*p != '-') && (*p != '(')
&& (*p != ')'))
return 0;
p++;
}
return 1;
}


/*
* Converts URI, if it is tel URI, to SIP URI. Returns 1, if
* conversion succeeded or if no conversion was needed, i.e., URI was not
* tel URI. Returns -1, if conversion failed. Takes SIP URI hostpart from
* second parameter and (if needed) writes the result to third parameter.
* This one attempts to be standards compliant and sort tel: uri parameters
* copied to the sip: uri in the manner defined in the standard. It also
* deletes the "phone-context" parameter if it is a domain, and, takes visual
* separators from the "phone-context" parameter if it is a telephone number.
*/
int tel2sip2(struct sip_msg *_msg, char *_uri, char *_hostpart, char *_res)
{
str uri, hostpart, tel_uri, sip_uri;
char *at;
int i, j, in_tel_parameters = 0;
int n_tel_params = 0;
pv_spec_t *res;
pv_value_t res_val;
char *tmp_ptr = NULL;
tel_param_t params[MAX_TEL_PARAMS];

/* get parameters */
if(get_str_fparam(&uri, _msg, (fparam_t *)_uri) < 0) {
LM_ERR("failed to get uri value\n");
return -1;
}
if(get_str_fparam(&hostpart, _msg, (fparam_t *)_hostpart) < 0) {
LM_ERR("failed to get hostpart value\n");
return -1;
}
res = (pv_spec_t *)_res;

/* check if anything needs to be done */
if(uri.len < 4)
return 1;
if(strncasecmp(uri.s, "tel:", 4) != 0)
return 1;

/* reserve memory for clean tel uri */
tel_uri.s = pkg_malloc(uri.len + 1);
if(tel_uri.s == 0) {
LM_ERR("no more pkg memory\n");
return -1;
}

/* Remove visual separators before converting to SIP URI. Don't remove
* visual separators in TEL URI parameters (after the first ";") */
for(i = 0, j = 0; i < uri.len; i++) {
if(in_tel_parameters == 0) {
if(uri.s[i] == ';')
in_tel_parameters = 1;
}
if(in_tel_parameters == 0) {
if((uri.s[i] != '-') && (uri.s[i] != '.') && (uri.s[i] != '(')
&& (uri.s[i] != ')'))
tel_uri.s[j++] = tolower(uri.s[i]);
} else {
tel_uri.s[j++] = tolower(uri.s[i]);
}
}
tel_uri.s[j] = '\0';
tel_uri.len = strlen(tel_uri.s);

/*** Start Code to sort tel: params ***/
tmp_ptr = tel_uri.s + 4; /* skip tel: */

for(i = 0; i < MAX_TEL_PARAMS; i++) {
tmp_ptr = strchr(tmp_ptr, ';');
if(tmp_ptr == NULL) {
break;
}
*tmp_ptr = '\0';
tmp_ptr++;
n_tel_params++;
params[i].name = tmp_ptr;
}
for(i = 0; i < n_tel_params; i++) {
tmp_ptr = strchr(params[i].name, '=');
if(tmp_ptr == NULL) {
params[i].value = "";
} else {
*tmp_ptr = '\0';
tmp_ptr++;
params[i].value = tmp_ptr;
}
if((0 == strcasecmp(params[i].name, "phone-context"))
&& (is_telnumber_format(params[i].value))) {
remove_visual_separators_from_phone(params[i].value);
}
}
if(n_tel_params > 1) {
qsort(&params[0], n_tel_params, sizeof(tel_param_t),
compare_tel_options);
}
/*** End Code to sort tel: params ***/

/* reserve memory for resulting sip uri */
sip_uri.len = 4 + tel_uri.len - 4 + 1 + hostpart.len + 1 + 10;
sip_uri.s = pkg_malloc(sip_uri.len + 1);
if(sip_uri.s == 0) {
LM_ERR("no more pkg memory\n");
pkg_free(tel_uri.s);
return -1;
}

/* create resulting sip uri */
at = sip_uri.s;
append_str(at, "sip:", 4);
/***** Start Code for sorted tel: parameters ****/
/* This string was terminated after the number */
append_str(at, tel_uri.s + 4, strlen(tel_uri.s + 4));
/** Now we need to insert sorted tel: parameters **/
for(i = 0; i < n_tel_params; i++) {
/* If the phone context is a domain,
* it has already been extracted and is in the "host part" */
if((0 != strcasecmp(params[i].name, "phone-context"))
|| (is_telnumber_format(params[i].value))) {
append_chr(at, ';');
append_str(at, params[i].name, strlen(params[i].name));
append_chr(at, '=');
append_str(at, params[i].value, strlen(params[i].value));
}
}
/***** End Code for sort tel: parameters ****/
append_chr(at, '@');
append_str(at, hostpart.s, hostpart.len);
append_chr(at, ';');
append_str(at, "user=phone", 10);

/* tel_uri is not needed anymore */
pkg_free(tel_uri.s);

/* set result pv value and write sip uri to result pv */
res_val.rs = sip_uri;
res_val.flags = PV_VAL_STR;
if(res->setf(_msg, &res->pvp, (int)EQ_T, &res_val) != 0) {
LM_ERR("failed to set result pvar\n");
pkg_free(sip_uri.s);
return -1;
}

/* free allocated pkg memory and return */
pkg_free(sip_uri.s);
return 1;
}

/*
* Check if parameter is an e164 number.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/modules/siputils/checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ int ki_add_uri_param(struct sip_msg *_msg, str *param);
*/
int tel2sip(struct sip_msg *_msg, char *_uri, char *_hostpart, char *_res);

int tel2sip2(struct sip_msg *_msg, char *_uri, char *_hostpart, char *_res);

/*
* Check if pseudo variable contains a valid uri
*/
Expand Down
2 changes: 2 additions & 0 deletions src/modules/siputils/siputils.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ static cmd_export_t cmds[] = {
REQUEST_ROUTE | BRANCH_ROUTE | FAILURE_ROUTE},
{"tel2sip", (cmd_function)tel2sip, 3, fixup_tel2sip, 0,
REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONREPLY_ROUTE},
{"tel2sip2", (cmd_function)tel2sip2, 3, fixup_tel2sip, 0,
REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONREPLY_ROUTE},
{"is_uri", (cmd_function)is_uri, 1, fixup_spve_null,
fixup_free_spve_null, ANY_ROUTE},
{"is_e164", (cmd_function)w_is_e164, 1, fixup_pvar_null,
Expand Down

0 comments on commit 366e286

Please sign in to comment.