diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 3f72016cb..5a3e6d2b1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2549,87 +2549,8 @@ void linphone_core_iterate(LinphoneCore *lc){ } } -static LinphoneAddress* _linphone_core_destroy_addr_if_not_sip( LinphoneAddress* addr ){ - if( linphone_address_is_sip(addr) ) { - return addr; - } else { - linphone_address_destroy(addr); - return NULL; - } -} - -/** - * Interpret a call destination as supplied by the user, and returns a fully qualified - * LinphoneAddress. - * - * @ingroup call_control - * - * A sip address should look like DisplayName \ . - * Basically this function performs the following tasks - * - if a phone number is entered, prepend country prefix of the default proxy - * configuration, eventually escape the '+' by 00. - * - if no domain part is supplied, append the domain name of the default proxy - * - if no sip: is present, prepend it - * - * The result is a syntaxically correct SIP address. -**/ - LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url){ - enum_lookup_res_t *enumres=NULL; - char *enum_domain=NULL; - LinphoneProxyConfig *proxy=lc->default_proxy; - char *tmpurl; - LinphoneAddress *uri; - - if (*url=='\0') return NULL; - - if (is_enum(url,&enum_domain)){ - linphone_core_notify_display_status(lc,_("Looking for telephone number destination...")); - if (enum_lookup(enum_domain,&enumres)<0){ - linphone_core_notify_display_status(lc,_("Could not resolve this number.")); - ms_free(enum_domain); - return NULL; - } - ms_free(enum_domain); - tmpurl=enumres->sip_address[0]; - uri=linphone_address_new(tmpurl); - enum_lookup_res_free(enumres); - return _linphone_core_destroy_addr_if_not_sip(uri); - } - /* check if we have a "sip:" or a "sips:" */ - if ( (strstr(url,"sip:")==NULL) && (strstr(url,"sips:")==NULL) ){ - /* this doesn't look like a true sip uri */ - if (strchr(url,'@')!=NULL){ - /* seems like sip: is missing !*/ - tmpurl=ms_strdup_printf("sip:%s",url); - uri=linphone_address_new(tmpurl); - ms_free(tmpurl); - if (uri){ - return _linphone_core_destroy_addr_if_not_sip(uri); - } - } - - if (proxy!=NULL){ - /* append the proxy domain suffix */ - const char *identity=linphone_proxy_config_get_identity(proxy); - char normalized_username[128]; - uri=linphone_address_new(identity); - if (uri==NULL){ - return NULL; - } - linphone_address_set_display_name(uri,NULL); - linphone_proxy_config_normalize_number(proxy,url,normalized_username, - sizeof(normalized_username)); - linphone_address_set_username(uri,normalized_username); - return _linphone_core_destroy_addr_if_not_sip(uri); - }else return NULL; - } - uri=linphone_address_new(url); - if (uri!=NULL){ - return _linphone_core_destroy_addr_if_not_sip(uri); - } - - return NULL; + return linphone_proxy_config_normalize_sip_uri(NULL, url); } /** diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index cdbf1a18e..54f3c2424 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1097,6 +1097,29 @@ LINPHONE_PUBLIC bool_t linphone_proxy_config_is_phone_number(LinphoneProxyConfig LINPHONE_PUBLIC bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len); +/** + * Same objective as linphone_proxy_config_normalize_number but allocates a new string + * @param proxy #LinphoneProxyConfig object containing country code and/or escape symbol. If NULL passed, will use default configuration. + * @param username the string to parse + * @return NULL if invalid phone number, normalized phone number from username input otherwise. +*/ +LINPHONE_PUBLIC char* linphone_proxy_config_normalize_phone_number(LinphoneProxyConfig *proxy, const char *username); + +/** + * Normalize a human readable sip uri into a fully qualified LinphoneAddress. + * A sip address should look like DisplayName \ . + * Basically this function performs the following tasks + * - if a phone number is entered, prepend country prefix and eventually escape the '+' by 00 of the proxy config. + * - if no domain part is supplied, append the domain name of the proxy config. + * - if no sip: is present, prepend it. + * + * The result is a syntactically correct SIP address. + * @param proxy #LinphoneProxyConfig object containing country code, escape symbol and/or domain name. If NULL passed, will use default configuration. + * @param username the string to parse + * @return NULL if invalid phone number, normalized phone number from username input otherwise. +*/ +LINPHONE_PUBLIC LinphoneAddress* linphone_proxy_config_normalize_sip_uri(LinphoneProxyConfig *proxy, const char *username); + /** * Set default privacy policy for all calls routed through this proxy. * @param cfg #LinphoneProxyConfig object to be modified @@ -2504,6 +2527,7 @@ LINPHONE_PUBLIC void linphone_core_remove_listener(LinphoneCore *lc, const Linph /*sets the user-agent string in sip messages, ideally called just after linphone_core_new() or linphone_core_init() */ LINPHONE_PUBLIC void linphone_core_set_user_agent(LinphoneCore *lc, const char *ua_name, const char *version); +/** @deprecated Use linphone_proxy_config_normalize_sip_uri instead. */ LINPHONE_PUBLIC LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url); LINPHONE_PUBLIC LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 2cfa26b20..ecbeb1533 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -23,6 +23,7 @@ Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org) #include "lpconfig.h" #include "private.h" #include "mediastreamer2/mediastream.h" +#include "enum.h" #include @@ -938,33 +939,40 @@ static void replace_icp_with_plus(const char *src, char *dest, size_t destlen, c strncpy(dest+i, src+strlen(icp), destlen-i-1); } +static char* replace_plus_with_icp_new(char *phone, const char* icp){ + return (icp && phone[0]=='+') ? ms_strdup_printf("%s%s", icp, phone+1) : ms_strdup(phone); +} -bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *inproxy, const char *username, char *result, size_t result_len){ +static char* replace_icp_with_plus_new(char *phone, const char *icp){ + return (strstr(phone, icp) == phone) ? ms_strdup_printf("+%s", phone+strlen(icp)) : ms_strdup(phone); +} + +bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len){ bool_t ret; - LinphoneProxyConfig *proxy = inproxy ? inproxy : linphone_proxy_config_new(); + LinphoneProxyConfig *tmpproxy = proxy ? proxy : linphone_proxy_config_new(); memset(result, 0, result_len); - if (linphone_proxy_config_is_phone_number(proxy, username)){ + if (linphone_proxy_config_is_phone_number(tmpproxy, username)){ dial_plan_t dialplan = {0}; char *flatten=flatten_number(username); ms_debug("Flattened number is '%s'",flatten); - /*username does not contain a dial prefix nor the proxy, nothing else to do*/ - if (proxy->dial_prefix==NULL || proxy->dial_prefix[0]=='\0'){ + /*username does not contain a dial prefix nor the tmpproxy, nothing else to do*/ + if (tmpproxy->dial_prefix==NULL || tmpproxy->dial_prefix[0]=='\0'){ strncpy(result,flatten,result_len-1); } else { - lookup_dial_plan_by_ccc(proxy->dial_prefix,&dialplan); + lookup_dial_plan_by_ccc(tmpproxy->dial_prefix,&dialplan); ms_debug("Using dial plan '%s'",dialplan.country); /* the number has international prefix or +, so nothing to do*/ if (flatten[0]=='+'){ ms_debug("Prefix already present."); /*eventually replace the plus by the international calling prefix of the country*/ - if (proxy->dial_escape_plus) { + if (tmpproxy->dial_escape_plus) { replace_plus_with_icp(flatten,result,result_len,dialplan.icp); }else{ strncpy(result, flatten, result_len-1); } }else if (strstr(flatten,dialplan.icp)==flatten){ - if (proxy->dial_escape_plus){ + if (tmpproxy->dial_escape_plus){ strncpy(result, flatten, result_len-1); }else{ replace_icp_with_plus(flatten, result, result_len, dialplan.icp); @@ -978,7 +986,7 @@ bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *inproxy, cons skip=numlen-dialplan.nnl; if (skip<0) skip=0; /*first prepend international calling prefix or +*/ - if (proxy->dial_escape_plus){ + if (tmpproxy->dial_escape_plus){ strncpy(result,dialplan.icp,result_len); i+=strlen(dialplan.icp); }else{ @@ -994,17 +1002,129 @@ bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *inproxy, cons strncpy(result+i,flatten+skip,result_len-i-1); } } - ms_free(flatten); ret = TRUE; } else { strncpy(result,username,result_len-1); ret = FALSE; } - if (inproxy==NULL) ms_free(proxy); + if (proxy==NULL) ms_free(tmpproxy); return ret; } +char* linphone_proxy_config_normalize_phone_number(LinphoneProxyConfig *proxy, const char *username) { + LinphoneProxyConfig *tmpproxy = proxy ? proxy : linphone_proxy_config_new(); + char* result = NULL; + if (linphone_proxy_config_is_phone_number(tmpproxy, username)){ + dial_plan_t dialplan = {0}; + char * flatten=flatten_number(username); + ms_debug("Flattened number is '%s'",flatten); + + /*if proxy has a dial prefix, modify phonenumber accordingly*/ + if (tmpproxy->dial_prefix!=NULL && tmpproxy->dial_prefix[0]!='\0'){ + lookup_dial_plan_by_ccc(tmpproxy->dial_prefix,&dialplan); + ms_debug("Using dial plan '%s'",dialplan.country); + /* the number already starts with + or international prefix*/ + if (flatten[0]=='+'||strstr(flatten,dialplan.icp)==flatten){ + ms_debug("Prefix already present."); + if (tmpproxy->dial_escape_plus) { + result = replace_plus_with_icp_new(flatten,dialplan.icp); + } else { + result = replace_icp_with_plus_new(flatten,dialplan.icp); + } + }else{ + /*0. keep at most national number significant digits */ + char* flatten_start = flatten + MAX(0, strlen(flatten) - dialplan.nnl); + /*1. First prepend international calling prefix or +*/ + /*2. Second add prefix*/ + /*3. Finally add user digits */ + + result = ms_strdup_printf("%s%s%s" + , tmpproxy->dial_escape_plus ? dialplan.icp : "+" + , dialplan.ccc + , flatten_start); + } + } + if (result==NULL) { + result = flatten; + } else { + ms_free(flatten); + } + } + if (proxy==NULL) ms_free(tmpproxy); + return result; +} + +static LinphoneAddress* _linphone_core_destroy_addr_if_not_sip( LinphoneAddress* addr ){ + if( linphone_address_is_sip(addr) ) { + return addr; + } else { + linphone_address_destroy(addr); + return NULL; + } +} + +LinphoneAddress* linphone_proxy_config_normalize_sip_uri(LinphoneProxyConfig *proxy, const char *username) { + enum_lookup_res_t *enumres=NULL; + char *enum_domain=NULL; + char *tmpurl; + LinphoneAddress *uri; + + if (*username=='\0') return NULL; + + if (is_enum(username,&enum_domain)){ + if (proxy) { + linphone_core_notify_display_status(proxy->lc,_("Looking for telephone number destination...")); + } + if (enum_lookup(enum_domain,&enumres)<0){ + if (proxy) { + linphone_core_notify_display_status(proxy->lc,_("Could not resolve this number.")); + } + ms_free(enum_domain); + return NULL; + } + ms_free(enum_domain); + tmpurl=enumres->sip_address[0]; + uri=linphone_address_new(tmpurl); + enum_lookup_res_free(enumres); + return _linphone_core_destroy_addr_if_not_sip(uri); + } + /* check if we have a "sip:" or a "sips:" */ + if ( (strstr(username,"sip:")==NULL) && (strstr(username,"sips:")==NULL) ){ + /* this doesn't look like a true sip uri */ + if (strchr(username,'@')!=NULL){ + /* seems like sip: is missing !*/ + tmpurl=ms_strdup_printf("sip:%s",username); + uri=linphone_address_new(tmpurl); + ms_free(tmpurl); + if (uri){ + return _linphone_core_destroy_addr_if_not_sip(uri); + } + } + + if (proxy!=NULL){ + /* append the proxy domain suffix */ + const char *identity=linphone_proxy_config_get_identity(proxy); + char normalized_username[128]; + uri=linphone_address_new(identity); + if (uri==NULL){ + return NULL; + } + linphone_address_set_display_name(uri,NULL); + linphone_proxy_config_normalize_number(proxy,username,normalized_username, + sizeof(normalized_username)); + linphone_address_set_username(uri,normalized_username); + return _linphone_core_destroy_addr_if_not_sip(uri); + }else return NULL; + } + uri=linphone_address_new(username); + if (uri!=NULL){ + return _linphone_core_destroy_addr_if_not_sip(uri); + } + + return NULL; +} + /** * Commits modification made to the proxy configuration. **/ diff --git a/tester/proxy_config_tester.c b/tester/proxy_config_tester.c index fe8b3cef8..29a169557 100644 --- a/tester/proxy_config_tester.c +++ b/tester/proxy_config_tester.c @@ -78,10 +78,26 @@ static void phone_normalization_with_dial_escape_plus(void){ linphone_proxy_config_destroy(proxy); } +#define SIP_URI_CHECK(actual, expected) { \ + LinphoneAddress* res = linphone_proxy_config_normalize_sip_uri(NULL, actual); \ + char* actual_str = linphone_address_as_string_uri_only(res); \ + BC_ASSERT_STRING_EQUAL(actual_str, expected); \ + ms_free(actual_str); \ + linphone_address_destroy(res); \ + } + + +static void sip_uri_normalization(void) { + BC_ASSERT_PTR_NULL(linphone_proxy_config_normalize_sip_uri(NULL, "test")); + SIP_URI_CHECK("test@linphone.org", "sip:test@linphone.org"); + SIP_URI_CHECK("test@linphone.org;transport=tls", "sip:test@linphone.org;transport=tls"); +} + test_t proxy_config_tests[] = { { "Phone normalization without proxy", phone_normalization_without_proxy }, { "Phone normalization with proxy", phone_normalization_with_proxy }, { "Phone normalization with dial escape plus", phone_normalization_with_dial_escape_plus }, + { "SIP URI normalization", sip_uri_normalization }, }; test_suite_t proxy_config_test_suite = {