proxy.c 53 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
linphone
Copyright (C) 2000  Simon MORLAT (simon.morlat@linphone.org)
*/
/*
 *  This program 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.
 *
 *  This program 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
20

21
#include "linphone/core.h"
22
#include "linphone/core_utils.h"
23
#include "linphone/sipsetup.h"
24 25
#include "linphone/lpconfig.h"
#include "private.h"
26
#include "mediastreamer2/mediastream.h"
27
#include "enum.h"
Erwan Croze's avatar
Erwan Croze committed
28
#include <bctoolbox/defs.h>
29
#include <ctype.h>
30

31
/*store current config related to server location*/
32
static void linphone_proxy_config_store_server_config(LinphoneProxyConfig* cfg) {
Simon Morlat's avatar
Simon Morlat committed
33
	if (cfg->saved_identity) linphone_address_unref(cfg->saved_identity);
34 35
	if (cfg->identity_address)
		cfg->saved_identity = linphone_address_clone(cfg->identity_address);
36
	else
37
		cfg->saved_identity = NULL;
38

Simon Morlat's avatar
Simon Morlat committed
39
	if (cfg->saved_proxy) linphone_address_unref(cfg->saved_proxy);
40 41
	if (cfg->reg_proxy)
		cfg->saved_proxy = linphone_address_new(cfg->reg_proxy);
42
	else
43
		cfg->saved_proxy = NULL;
44 45
}

46
LinphoneProxyConfigAddressComparisonResult linphone_proxy_config_address_equal(const LinphoneAddress *a, const LinphoneAddress *b) {
47
	if (a == NULL && b == NULL)
48
		return LinphoneProxyConfigAddressEqual;
49
	else if (!a || !b)
50
		return LinphoneProxyConfigAddressDifferent;
51

52 53
	if (linphone_address_equal(a,b))
		return LinphoneProxyConfigAddressEqual;
54 55
	if (linphone_address_weak_equal(a,b)) {
		/*also check both transport and uri */
Simon Morlat's avatar
Simon Morlat committed
56
		if (linphone_address_get_secure(a) == linphone_address_get_secure(b) && linphone_address_get_transport(a) == linphone_address_get_transport(b))
57 58 59 60 61
			return LinphoneProxyConfigAddressWeakEqual;
		else
			return LinphoneProxyConfigAddressDifferent;
	}
	return LinphoneProxyConfigAddressDifferent; /*either username, domain or port ar not equals*/
62 63
}

64 65
LinphoneProxyConfigAddressComparisonResult linphone_proxy_config_is_server_config_changed(const LinphoneProxyConfig* cfg) {
	LinphoneAddress *current_proxy=cfg->reg_proxy?linphone_address_new(cfg->reg_proxy):NULL;
66 67
	LinphoneProxyConfigAddressComparisonResult result_identity;
	LinphoneProxyConfigAddressComparisonResult result;
68

69
	result = linphone_proxy_config_address_equal(cfg->saved_identity,cfg->identity_address);
70
	if (result == LinphoneProxyConfigAddressDifferent) goto end;
71
	result_identity = result;
72

73
	result = linphone_proxy_config_address_equal(cfg->saved_proxy,current_proxy);
74
	if (result == LinphoneProxyConfigAddressDifferent) goto end;
75 76 77 78 79
	/** If the proxies are equal use the result of the difference between the identities,
	  * otherwise the result is weak-equal and so weak-equal must be returned even if the
	  * identities were equal.
	  */
	if (result == LinphoneProxyConfigAddressEqual) result = result_identity;
80

Simon Morlat's avatar
Simon Morlat committed
81
	end:
Simon Morlat's avatar
Simon Morlat committed
82
	if (current_proxy) linphone_address_unref(current_proxy);
83
	ms_message("linphone_proxy_config_is_server_config_changed : %i", result);
Simon Morlat's avatar
Simon Morlat committed
84
	return result;
85
}
86

smorlat's avatar
smorlat committed
87
void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){
88
	bctbx_list_t *elem;
smorlat's avatar
smorlat committed
89
	int i;
90
	if (!linphone_core_ready(lc)) return;
91

92
	for(elem=lc->sip_conf.proxies,i=0;elem!=NULL;elem=bctbx_list_next(elem),i++){
smorlat's avatar
smorlat committed
93 94 95
		LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
		linphone_proxy_config_write_to_config_file(lc->config,cfg,i);
	}
96 97
	/*to ensure removed configs are erased:*/
	linphone_proxy_config_write_to_config_file(lc->config,NULL,i);
98
	lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy_config_index(lc));
smorlat's avatar
smorlat committed
99
}
aymeric's avatar
aymeric committed
100

101
static void linphone_proxy_config_init(LinphoneCore* lc, LinphoneProxyConfig *cfg) {
102 103 104 105
	const char *dial_prefix = lc ? lp_config_get_default_string(lc->config,"proxy","dial_prefix",NULL) : NULL;
	const char *identity = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_identity", NULL) : NULL;
	const char *proxy = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_proxy", NULL) : NULL;
	const char *route = lc ? lp_config_get_default_string(lc->config, "proxy", "reg_route", NULL) : NULL;
106
	const char *realm = lc ? lp_config_get_default_string(lc->config, "proxy", "realm", NULL) : NULL;
107
	const char *quality_reporting_collector = lc ? lp_config_get_default_string(lc->config, "proxy", "quality_reporting_collector", NULL) : NULL;
108 109
	const char *contact_params = lc ? lp_config_get_default_string(lc->config, "proxy", "contact_parameters", NULL) : NULL;
	const char *contact_uri_params = lc ? lp_config_get_default_string(lc->config, "proxy", "contact_uri_parameters", NULL) : NULL;
110
	const char *refkey = lc ? lp_config_get_default_string(lc->config, "proxy", "refkey", NULL) : NULL;
111
	const char *nat_policy_ref = lc ? lp_config_get_default_string(lc->config, "proxy", "nat_policy_ref", NULL):NULL;
112
	cfg->lc = lc;
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	cfg->expires = lc ? lp_config_get_default_int(lc->config, "proxy", "reg_expires", 3600) : 3600;
	cfg->reg_sendregister = lc ? lp_config_get_default_int(lc->config, "proxy", "reg_sendregister", 1) : 1;
	cfg->dial_prefix = dial_prefix ? ms_strdup(dial_prefix) : NULL;
	cfg->dial_escape_plus = lc ? lp_config_get_default_int(lc->config, "proxy", "dial_escape_plus", 0) : 0;
	cfg->privacy = lc ? lp_config_get_default_int(lc->config, "proxy", "privacy", LinphonePrivacyDefault) : LinphonePrivacyDefault;
	cfg->identity_address = identity ? linphone_address_new(identity) : NULL;
	cfg->reg_identity = cfg->identity_address ? linphone_address_as_string(cfg->identity_address) : NULL;
	cfg->reg_proxy = proxy ? ms_strdup(proxy) : NULL;
	cfg->reg_route = route ? ms_strdup(route) : NULL;
	cfg->realm = realm ? ms_strdup(realm) : NULL;
	cfg->quality_reporting_enabled = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_enabled", 0) : 0;
	cfg->quality_reporting_collector = quality_reporting_collector ? ms_strdup(quality_reporting_collector) : NULL;
	cfg->quality_reporting_interval = lc ? lp_config_get_default_int(lc->config, "proxy", "quality_reporting_interval", 0) : 0;
	cfg->contact_params = contact_params ? ms_strdup(contact_params) : NULL;
	cfg->contact_uri_params = contact_uri_params ? ms_strdup(contact_uri_params) : NULL;
Ghislain MARY's avatar
Ghislain MARY committed
128
	cfg->avpf_mode = lc ? static_cast<LinphoneAVPFMode>(lp_config_get_default_int(lc->config, "proxy", "avpf", LinphoneAVPFDefault)) : LinphoneAVPFDefault;
129
	cfg->avpf_rr_interval = lc ? lp_config_get_default_int(lc->config, "proxy", "avpf_rr_interval", 5) : 5;
130
	cfg->publish_expires= lc ? lp_config_get_default_int(lc->config, "proxy", "publish_expires", -1) : -1;
131
	cfg->publish = lc ? lp_config_get_default_int(lc->config, "proxy", "publish", FALSE) : FALSE;
132
	cfg->refkey = refkey ? ms_strdup(refkey) : NULL;
133 134 135 136 137 138 139 140 141
	if (nat_policy_ref) {
		LinphoneNatPolicy *policy = linphone_config_create_nat_policy_from_section(lc->config,nat_policy_ref);
		linphone_proxy_config_set_nat_policy(cfg, policy);
		if (policy) {
			linphone_nat_policy_unref(policy);
		} else {
			ms_error("Cannot create default nat policy with ref [%s] for proxy config [%p]",nat_policy_ref,cfg);
		}
	}
aymeric's avatar
aymeric committed
142 143
}

jehan's avatar
jehan committed
144 145 146
LinphoneProxyConfig *linphone_proxy_config_new() {
	return linphone_core_create_proxy_config(NULL);
}
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
static char * append_linphone_address(LinphoneAddress *addr,char *out) {
	char *res = out;
	if (addr) {
		char *tmp;
		tmp = linphone_address_as_string(addr);
		res = ms_strcat_printf(out, "%s",tmp);
		ms_free(tmp);
	}
	return res;
};
static char * append_string(const char * string,char *out) {
	char *res = out;
	if (string) {
		res = ms_strcat_printf(out, "%s",string);
	}
	return res;
}
/*
 * return true if computed value has changed
 */
bool_t linphone_proxy_config_compute_publish_params_hash(LinphoneProxyConfig * cfg) {
	char * source = NULL;
	char hash[33];
	char saved;
	unsigned long long previous_hash[2];
	previous_hash[0] = cfg->previous_publish_config_hash[0];
	previous_hash[1] = cfg->previous_publish_config_hash[1];
175

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	source = ms_strcat_printf(source, "%i",cfg->privacy);
	source=append_linphone_address(cfg->identity_address, source);
	source=append_string(cfg->reg_proxy,source);
	source=append_string(cfg->reg_route,source);
	source=append_string(cfg->realm,source);
	source = ms_strcat_printf(source, "%i",cfg->publish_expires);
	source = ms_strcat_printf(source, "%i",cfg->publish);
	belle_sip_auth_helper_compute_ha1(source, "dummy", "dummy", hash);
	ms_free(source);
	saved = hash[16];
	hash[16] = '\0';
	cfg->previous_publish_config_hash[0] = strtoull(hash, (char **)NULL, 16);
	hash[16] = saved;
	cfg->previous_publish_config_hash[1] = strtoull(&hash[16], (char **)NULL, 16);
	return previous_hash[0] != cfg->previous_publish_config_hash[0] || previous_hash[1] != cfg->previous_publish_config_hash[1];
}
192
static void _linphone_proxy_config_destroy(LinphoneProxyConfig *cfg);
193 194 195 196 197 198 199 200 201 202

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneProxyConfig);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneProxyConfig, belle_sip_object_t,
	(belle_sip_object_destroy_t)_linphone_proxy_config_destroy,
	NULL, // clone
	NULL, // marshal
	FALSE
);

jehan's avatar
jehan committed
203
LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc) {
204 205 206
	LinphoneProxyConfig *cfg = belle_sip_object_new(LinphoneProxyConfig);
	linphone_proxy_config_init(lc,cfg);
	return cfg;
207 208
}

209 210 211 212 213
void _linphone_proxy_config_release_ops(LinphoneProxyConfig *cfg){
	if (cfg->op) {
		sal_op_release(cfg->op);
		cfg->op=NULL;
	}
214 215 216 217
	if (cfg->presence_publish_event){
		linphone_event_terminate(cfg->presence_publish_event);
		linphone_event_unref(cfg->presence_publish_event);
		cfg->presence_publish_event=NULL;
218 219 220 221 222 223
	}
}

void _linphone_proxy_config_destroy(LinphoneProxyConfig *cfg){
	if (cfg->reg_proxy!=NULL) ms_free(cfg->reg_proxy);
	if (cfg->reg_identity!=NULL) ms_free(cfg->reg_identity);
Simon Morlat's avatar
Simon Morlat committed
224
	if (cfg->identity_address!=NULL) linphone_address_unref(cfg->identity_address);
225 226 227 228 229 230 231 232
	if (cfg->reg_route!=NULL) ms_free(cfg->reg_route);
	if (cfg->quality_reporting_collector!=NULL) ms_free(cfg->quality_reporting_collector);
	if (cfg->ssctx!=NULL) sip_setup_context_free(cfg->ssctx);
	if (cfg->realm!=NULL) ms_free(cfg->realm);
	if (cfg->type!=NULL) ms_free(cfg->type);
	if (cfg->dial_prefix!=NULL) ms_free(cfg->dial_prefix);
	if (cfg->contact_params) ms_free(cfg->contact_params);
	if (cfg->contact_uri_params) ms_free(cfg->contact_uri_params);
Simon Morlat's avatar
Simon Morlat committed
233 234
	if (cfg->saved_proxy!=NULL) linphone_address_unref(cfg->saved_proxy);
	if (cfg->saved_identity!=NULL) linphone_address_unref(cfg->saved_identity);
235
	if (cfg->sent_headers!=NULL) sal_custom_header_free(cfg->sent_headers);
236
	if (cfg->pending_contact) linphone_address_unref(cfg->pending_contact);
237
	if (cfg->refkey) ms_free(cfg->refkey);
Ghislain MARY's avatar
Ghislain MARY committed
238 239 240
	if (cfg->nat_policy != NULL) {
		linphone_nat_policy_unref(cfg->nat_policy);
	}
241 242 243
	if (cfg->ei){
		linphone_error_info_unref(cfg->ei);
	}
244
	_linphone_proxy_config_release_ops(cfg);
245 246 247 248 249 250
}

void linphone_proxy_config_destroy(LinphoneProxyConfig *cfg) {
	belle_sip_object_unref(cfg);
}

251 252 253 254 255
void _linphone_proxy_config_release(LinphoneProxyConfig *cfg) {
	_linphone_proxy_config_release_ops(cfg);
	belle_sip_object_unref(cfg);
}

256 257 258 259 260 261 262
LinphoneProxyConfig *linphone_proxy_config_ref(LinphoneProxyConfig *cfg) {
	belle_sip_object_ref(cfg);
	return cfg;
}

void linphone_proxy_config_unref(LinphoneProxyConfig *cfg) {
	belle_sip_object_unref(cfg);
aymeric's avatar
aymeric committed
263 264
}

265 266
bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *cfg){
	return cfg->state == LinphoneRegistrationOk;
smorlat's avatar
smorlat committed
267 268
}

269
LinphoneStatus linphone_proxy_config_set_server_addr(LinphoneProxyConfig *cfg, const char *server_addr){
270 271
	LinphoneAddress *addr=NULL;
	char *modified=NULL;
272

273 274
	if (cfg->reg_proxy!=NULL) ms_free(cfg->reg_proxy);
	cfg->reg_proxy=NULL;
275

aymeric's avatar
aymeric committed
276
	if (server_addr!=NULL && strlen(server_addr)>0){
277
		if (strstr(server_addr,"sip:")==NULL && strstr(server_addr,"sips:")==NULL){
278 279 280
			modified=ms_strdup_printf("sip:%s",server_addr);
			addr=linphone_address_new(modified);
			ms_free(modified);
281
		}
282 283
		if (addr==NULL)
			addr=linphone_address_new(server_addr);
284
		if (addr){
285
			cfg->reg_proxy=linphone_address_as_string(addr);
Simon Morlat's avatar
Simon Morlat committed
286
			linphone_address_unref(addr);
aymeric's avatar
aymeric committed
287 288
		}else{
			ms_warning("Could not parse %s",server_addr);
289
			return -1;
aymeric's avatar
aymeric committed
290 291 292 293 294
		}
	}
	return 0;
}

295

296
LinphoneStatus linphone_proxy_config_set_identity_address(LinphoneProxyConfig *cfg, const LinphoneAddress *addr){
297
	if (!addr || linphone_address_get_username(addr)==NULL){
298 299
		char* as_string = addr ? linphone_address_as_string(addr) : ms_strdup("NULL");
		ms_warning("Invalid sip identity: %s", as_string);
300 301 302 303
		ms_free(as_string);
		return -1;
	}
	if (cfg->identity_address != NULL) {
Simon Morlat's avatar
Simon Morlat committed
304
		linphone_address_unref(cfg->identity_address);
305 306 307 308 309 310 311 312 313 314
	}
	cfg->identity_address=linphone_address_clone(addr);

	if (cfg->reg_identity!=NULL) {
		ms_free(cfg->reg_identity);
	}
	cfg->reg_identity= linphone_address_as_string(cfg->identity_address);
	return 0;
}

315
LinphoneStatus linphone_proxy_config_set_identity(LinphoneProxyConfig *cfg, const char *identity){
aymeric's avatar
aymeric committed
316
	if (identity!=NULL && strlen(identity)>0){
317 318
		LinphoneAddress *addr=linphone_address_new(identity);
		int ret=linphone_proxy_config_set_identity_address(cfg, addr);
Simon Morlat's avatar
Simon Morlat committed
319
		if (addr) linphone_address_unref(addr);
320
		return ret;
aymeric's avatar
aymeric committed
321
	}
322
	return -1;
smorlat's avatar
smorlat committed
323 324 325
}

const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg){
326
	return cfg->identity_address ? linphone_address_get_domain(cfg->identity_address) : NULL;
aymeric's avatar
aymeric committed
327 328
}

329
LinphoneStatus linphone_proxy_config_set_route(LinphoneProxyConfig *cfg, const char *route)
aymeric's avatar
aymeric committed
330
{
331 332 333
	if (cfg->reg_route!=NULL){
		ms_free(cfg->reg_route);
		cfg->reg_route=NULL;
aymeric's avatar
aymeric committed
334
	}
335
	if (route!=NULL && route[0] !='\0'){
336 337
		SalAddress *addr;
		char *tmp;
Simon Morlat's avatar
Simon Morlat committed
338
		/*try to prepend 'sip:' */
339
		if (strstr(route,"sip:")==NULL && strstr(route,"sips:")==NULL){
340 341 342 343 344
			tmp=ms_strdup_printf("sip:%s",route);
		}else tmp=ms_strdup(route);
		addr=sal_address_new(tmp);
		if (addr!=NULL){
			sal_address_destroy(addr);
345 346
			cfg->reg_route=tmp;
			return 0;
347 348
		}else{
			ms_free(tmp);
349
			return -1;
350
		}
351 352
	} else {
		return 0;
Simon Morlat's avatar
Simon Morlat committed
353
	}
aymeric's avatar
aymeric committed
354 355
}

356 357
bool_t linphone_proxy_config_check(LinphoneCore *lc, LinphoneProxyConfig *cfg){
	if (cfg->reg_proxy==NULL){
358 359
		if (lc)
			linphone_core_notify_display_warning(lc,_("The sip proxy address you entered is invalid, it must start with \"sip:\""
aymeric's avatar
aymeric committed
360 361 362
						" followed by a hostname."));
		return FALSE;
	}
363
	if (cfg->identity_address==NULL){
364 365
		if (lc)
			linphone_core_notify_display_warning(lc,_("The sip identity you entered is invalid.\nIt should look like "
aymeric's avatar
aymeric committed
366 367 368 369 370 371
					"sip:username@proxydomain, such as sip:alice@example.net"));
		return FALSE;
	}
	return TRUE;
}

372
void linphone_proxy_config_enableregister(LinphoneProxyConfig *cfg, bool_t val){
373
	if (val != cfg->reg_sendregister) cfg->register_changed = TRUE;
374
	cfg->reg_sendregister=val;
aymeric's avatar
aymeric committed
375 376
}

377
void linphone_proxy_config_set_expires(LinphoneProxyConfig *cfg, int val){
378
	if (val<0) val=600;
379
	if (val != cfg->expires) cfg->register_changed = TRUE;
380
	cfg->expires=val;
aymeric's avatar
aymeric committed
381 382
}

383 384
void linphone_proxy_config_enable_publish(LinphoneProxyConfig *cfg, bool_t val){
	cfg->publish=val;
aymeric's avatar
aymeric committed
385
}
386

387 388
void linphone_proxy_config_pause_register(LinphoneProxyConfig *cfg){
	if (cfg->op) sal_op_stop_refreshing(cfg->op);
389 390
}

391
void linphone_proxy_config_edit(LinphoneProxyConfig *cfg){
392
	/*store current config related to server location*/
393
	linphone_proxy_config_store_server_config(cfg);
394
	linphone_proxy_config_compute_publish_params_hash(cfg);
395

396 397
	if (cfg->publish && cfg->presence_publish_event){
		linphone_event_pause_publish(cfg->presence_publish_event);
398
	}
399
	/*Don't stop refresher*/
aymeric's avatar
aymeric committed
400 401
}

402 403 404
void linphone_proxy_config_apply(LinphoneProxyConfig *cfg,LinphoneCore *lc){
	cfg->lc=lc;
	linphone_proxy_config_done(cfg);
aymeric's avatar
aymeric committed
405
}
Simon Morlat's avatar
Simon Morlat committed
406

407
void linphone_proxy_config_stop_refreshing(LinphoneProxyConfig * cfg){
408
	LinphoneAddress *contact_addr=NULL;
409
	if (cfg->op
410 411
			&& cfg->state == LinphoneRegistrationOk
			&& (contact_addr = (LinphoneAddress*)sal_op_get_contact_address(cfg->op))
412 413 414
			&& linphone_address_get_transport(contact_addr) != LinphoneTransportUdp /*with udp, there is a risk of port reuse, so I prefer to not do anything for now*/
			&& lp_config_get_int(cfg->lc->config, "sip", "unregister_previous_contact", 0)) {
		/*need to save current contact in order to reset it later*/
415 416 417 418
		linphone_address_ref(contact_addr);
		if (cfg->pending_contact)
			linphone_address_unref(cfg->pending_contact);
		cfg->pending_contact=contact_addr;
419 420

	}
421
	if (cfg->presence_publish_event){ /*might probably do better*/
422 423 424
		linphone_event_set_publish_state(cfg->presence_publish_event,LinphonePublishNone);
		linphone_event_unref(cfg->presence_publish_event); /*probably useless as cfg->long_term_event is already unref in linphone_proxy_config_notify_publish_state_changed. To be check with Ghislain*/
		cfg->presence_publish_event=NULL;
425
	}
426 427 428
	if (cfg->op){
		sal_op_release(cfg->op);
		cfg->op=NULL;
429 430 431
	}
}

432
LinphoneAddress *guess_contact_for_register(LinphoneProxyConfig *cfg){
433
	LinphoneAddress *ret=NULL;
434
	LinphoneAddress *proxy=linphone_address_new(cfg->reg_proxy);
435
	const char *host;
436

437
	if (proxy==NULL) return NULL;
438
	host=linphone_address_get_domain(proxy);
439
	if (host!=NULL){
440 441
		int localport = -1;
		const char *localip = NULL;
442
		LinphoneAddress *contact=linphone_address_clone(cfg->identity_address);
443

Simon Morlat's avatar
Simon Morlat committed
444
		linphone_address_clean(contact);
445

446
		if (cfg->contact_params) {
447
			// We want to add a list of contacts params to the linphone address
448
			sal_address_set_params(contact,cfg->contact_params);
449
		}
450
		if (cfg->contact_uri_params){
451
			sal_address_set_uri_params(contact,cfg->contact_uri_params);
452
		}
453
#ifdef BUILD_UPNP
454 455 456 457
		if (cfg->lc->upnp != NULL && linphone_core_get_firewall_policy(cfg->lc)==LinphonePolicyUseUpnp &&
			linphone_upnp_context_get_state(cfg->lc->upnp) == LinphoneUpnpStateOk) {
			localip = linphone_upnp_context_get_external_ipaddress(cfg->lc->upnp);
			localport = linphone_upnp_context_get_external_port(cfg->lc->upnp);
458
		}
459
#endif //BUILD_UPNP
460
		linphone_address_set_port(contact,localport);
461 462 463 464
		linphone_address_set_domain(contact,localip);
		linphone_address_set_display_name(contact,NULL);

		ret=contact;
465
	}
Simon Morlat's avatar
Simon Morlat committed
466
	linphone_address_unref(proxy);
467 468
	return ret;
}
469

470 471 472 473
void _linphone_proxy_config_unregister(LinphoneProxyConfig *obj) {
	if (obj->op && (obj->state == LinphoneRegistrationOk ||
					(obj->state == LinphoneRegistrationProgress && obj->expires != 0))) {
		sal_unregister(obj->op);
474 475
	}
}
476

477 478 479
static void linphone_proxy_config_register(LinphoneProxyConfig *cfg){
	if (cfg->reg_sendregister){
		LinphoneAddress* proxy=linphone_address_new(cfg->reg_proxy);
Simon Morlat's avatar
Simon Morlat committed
480
		char* proxy_string;
481
		char * from = linphone_address_as_string(cfg->identity_address);
482
		LinphoneAddress *contact;
483
		ms_message("LinphoneProxyConfig [%p] about to register (LinphoneCore version: %s)",cfg,linphone_core_get_version());
jehan's avatar
jehan committed
484
		proxy_string=linphone_address_as_string_uri_only(proxy);
Simon Morlat's avatar
Simon Morlat committed
485
		linphone_address_unref(proxy);
486 487 488
		if (cfg->op)
			sal_op_release(cfg->op);
		cfg->op=sal_op_new(cfg->lc->sal);
489

490
		linphone_configure_op(cfg->lc, cfg->op, cfg->identity_address, cfg->sent_headers, FALSE);
491

492
		if ((contact=guess_contact_for_register(cfg))) {
493
			sal_op_set_contact_address(cfg->op,contact);
Simon Morlat's avatar
Simon Morlat committed
494
			linphone_address_unref(contact);
jehan's avatar
jehan committed
495
		}
496

497
		sal_op_set_user_pointer(cfg->op,cfg);
498

499 500

		if (sal_register(cfg->op,proxy_string, cfg->reg_identity, cfg->expires, cfg->pending_contact)==0) {
501 502 503 504
			if (cfg->pending_contact) {
				linphone_address_unref(cfg->pending_contact);
				cfg->pending_contact=NULL;
			}
505
			linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,"Registration in progress");
506
		} else {
507
			linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,"Registration failed");
508
		}
jehan's avatar
jehan committed
509
		ms_free(proxy_string);
510
		ms_free(from);
511
	} else {
512
		/* unregister if registered*/
513 514
		if (cfg->state == LinphoneRegistrationProgress) {
			linphone_proxy_config_set_state(cfg,LinphoneRegistrationCleared,"Registration cleared");
515
		}
516
		_linphone_proxy_config_unregister(cfg);
aymeric's avatar
aymeric committed
517
	}
smorlat's avatar
smorlat committed
518 519
}

520 521 522 523
void linphone_proxy_config_refresh_register(LinphoneProxyConfig *cfg){
	if (cfg->reg_sendregister && cfg->op && cfg->state!=LinphoneRegistrationProgress){
		if (sal_register_refresh(cfg->op,cfg->expires) == 0) {
			linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress, "Refresh registration");
524
		}
525 526 527
	}
}

528 529 530 531 532 533

void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char *prefix){
	if (cfg->dial_prefix!=NULL){
		ms_free(cfg->dial_prefix);
		cfg->dial_prefix=NULL;
	}
534
	if (prefix && prefix[0]!='\0') cfg->dial_prefix=ms_strdup(prefix);
535 536 537 538 539 540 541 542 543 544 545 546 547
}

const char *linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg){
	return cfg->dial_prefix;
}

void linphone_proxy_config_set_dial_escape_plus(LinphoneProxyConfig *cfg, bool_t val){
	cfg->dial_escape_plus=val;
}

bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg){
	return cfg->dial_escape_plus;
}
548

549 550
void linphone_proxy_config_enable_quality_reporting(LinphoneProxyConfig *cfg, bool_t val){
	cfg->quality_reporting_enabled = val;
551 552
}

553
bool_t linphone_proxy_config_quality_reporting_enabled(LinphoneProxyConfig *cfg){
554
	return cfg->quality_reporting_enabled;
555 556
}

557
void linphone_proxy_config_set_quality_reporting_interval(LinphoneProxyConfig *cfg, int interval) {
558
	cfg->quality_reporting_interval = interval;
559 560 561 562 563 564
}

int linphone_proxy_config_get_quality_reporting_interval(LinphoneProxyConfig *cfg) {
	return cfg->quality_reporting_interval;
}

565
void linphone_proxy_config_set_quality_reporting_collector(LinphoneProxyConfig *cfg, const char *collector){
566 567
	if (collector!=NULL && strlen(collector)>0){
		LinphoneAddress *addr=linphone_address_new(collector);
568
		if (!addr){
569
			ms_error("Invalid SIP collector URI: %s. Quality reporting will be DISABLED.",collector);
570
		} else {
571
			if (cfg->quality_reporting_collector != NULL){
572
				ms_free(cfg->quality_reporting_collector);
573
			}
574
			cfg->quality_reporting_collector = ms_strdup(collector);
575 576 577
		}

		if (addr){
Simon Morlat's avatar
Simon Morlat committed
578
			linphone_address_unref(addr);
579 580 581 582
		}
	}
}

583 584
const char *linphone_proxy_config_get_quality_reporting_collector(const LinphoneProxyConfig *cfg){
	return cfg->quality_reporting_collector;
585 586 587
}


588
bool_t linphone_proxy_config_is_phone_number(LinphoneProxyConfig *proxy, const char *username){
589
	const char *p;
590
	if (!username) return FALSE;
591
	for(p=username;*p!='\0';++p){
592
		if (isdigit(*p) ||
593 594 595 596
			*p==' ' ||
			*p=='.' ||
			*p=='-' ||
			*p==')' ||
597 598
			*p=='(' ||
			*p=='/' ||
599
			*p=='+' ||
600 601
			(unsigned char)*p==0xca || (unsigned char)*p==0xc2 || (unsigned char)*p==0xa0 // non-breakable space (iOS uses it to format contacts phone number)
			) {
602
			continue;
603
		}
604
		return FALSE;
605 606 607 608
	}
	return TRUE;
}

609
//remove anything but [0-9] and +
610
static char *flatten_number(const char *number){
Ghislain MARY's avatar
Ghislain MARY committed
611
	char *result=reinterpret_cast<char *>(ms_malloc0(strlen(number)+1));
612 613 614
	char *w=result;
	const char *r;
	for(r=number;*r!='\0';++r){
615
		if (*r=='+' || isdigit(*r)){
616 617 618 619 620 621 622
			*w++=*r;
		}
	}
	*w++='\0';
	return result;
}

jehan's avatar
jehan committed
623
/*static char* replace_plus_with_icp(char *phone, const char* icp){
624
	return (icp && phone[0]=='+') ? ms_strdup_printf("%s%s", icp, phone+1) : ms_strdup(phone);
jehan's avatar
jehan committed
625
}*/
626

627
static char* replace_icp_with_plus(char *phone, const char *icp){
628 629
	return (strstr(phone, icp) == phone) ?  ms_strdup_printf("+%s", phone+strlen(icp)) : ms_strdup(phone);
}
630

631
bool_t linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len){
632 633
	char * normalized_phone = linphone_proxy_config_normalize_phone_number(proxy, username);
	const char * output = normalized_phone ? normalized_phone : username;
634
	memset(result, 0, result_len);
635 636 637
	memcpy(result, output, MIN(strlen(output) + 1, result_len));
	ms_free(normalized_phone);
	return output != username;
638 639
}

640 641 642
char* linphone_proxy_config_normalize_phone_number(LinphoneProxyConfig *proxy, const char *username) {
	LinphoneProxyConfig *tmpproxy = proxy ? proxy : linphone_proxy_config_new();
	char* result = NULL;
643 644 645
	LinphoneDialPlan dialplan = {0};
	char * nationnal_significant_number = NULL;
	int ccc = -1;
646

647 648
	if (linphone_proxy_config_is_phone_number(tmpproxy, username)){
		char * flatten=flatten_number(username);
649
		ms_debug("Flattened number is '%s' for '%s'",flatten, username);
650

651 652
		ccc = linphone_dial_plan_lookup_ccc_from_e164(flatten);
		if (ccc>-1) { /*e164 like phone number*/
653
			dialplan = *linphone_dial_plan_by_ccc_as_int(ccc);
654
			nationnal_significant_number = strstr(flatten, dialplan.ccc);
655 656 657 658 659 660 661 662 663 664 665 666 667
			if (nationnal_significant_number) {
				nationnal_significant_number +=strlen(dialplan.ccc);
			}
		} else if (flatten[0] =='+') {
			ms_message ("Unknown ccc for e164 like number [%s]", flatten);
			goto end;
		} else {
			dialplan = *linphone_dial_plan_by_ccc(tmpproxy->dial_prefix); //copy dial plan;
			if (tmpproxy->dial_prefix){
				if (strcmp(tmpproxy->dial_prefix,dialplan.ccc) != 0){
					//probably generic dialplan, preserving proxy dial prefix
					strncpy(dialplan.ccc,tmpproxy->dial_prefix,sizeof(dialplan.ccc));
				}
668 669 670 671 672 673 674 675
				/*it does not make sens to try replace icp with + if we are not sure from the country we are (I.E tmpproxy->dial_prefix==NULL)*/
				if (strstr(flatten,dialplan.icp)==flatten) {
					char *e164 = replace_icp_with_plus(flatten,dialplan.icp);
					result = linphone_proxy_config_normalize_phone_number(tmpproxy,e164);
					ms_free(e164);
					goto end;
				}

676 677
			}
			nationnal_significant_number=flatten;
678
		}
jehan's avatar
jehan committed
679
		ms_debug("Using dial plan '%s'",dialplan.country);
680

681
		/*if proxy has a dial prefix, modify phonenumber accordingly*/
682
		if (dialplan.ccc[0]!='\0') {
683
			/* the number already starts with + or international prefix*/
684 685 686 687 688 689 690 691 692 693 694 695 696 697
			/*0. keep at most national number significant digits */
			char* nationnal_significant_number_start = nationnal_significant_number
														+ MAX(0, (int)strlen(nationnal_significant_number)
														- (int)dialplan.nnl);
			ms_debug("Prefix not present. Keeping at most %d digits: %s", dialplan.nnl, nationnal_significant_number_start);

			/*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
										, nationnal_significant_number_start);
			ms_debug("Prepended prefix resulted in %s", result);
698
		}
699 700

	end:
701 702 703 704 705 706
		if (result==NULL) {
			result = flatten;
		} else {
			ms_free(flatten);
		}
	}
707
	if (proxy==NULL) linphone_proxy_config_unref(tmpproxy);
708 709 710 711 712 713 714
	return result;
}

static LinphoneAddress* _linphone_core_destroy_addr_if_not_sip( LinphoneAddress* addr ){
	if( linphone_address_is_sip(addr) ) {
		return addr;
	} else {
Simon Morlat's avatar
Simon Morlat committed
715
		linphone_address_unref(addr);
716 717 718 719 720 721 722 723 724 725
		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;

726
	if (!username || *username=='\0') return NULL;
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

	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);
			}
		}

758
		if (proxy!=NULL && linphone_proxy_config_get_identity_address(proxy)!=NULL){
759
			/* append the proxy domain suffix but remove any custom parameters/headers */
760
			LinphoneAddress *uri=linphone_address_clone(linphone_proxy_config_get_identity_address(proxy));
761 762
			if (uri==NULL){
				return NULL;
763
			} else {
764
				linphone_address_clean(uri);
765
				linphone_address_set_display_name(uri,NULL);
766
				linphone_address_set_username(uri,username);
767
				return _linphone_core_destroy_addr_if_not_sip(uri);
768
			}
769 770 771
		} else {
			return NULL;
		}
772 773 774 775 776 777 778 779 780
	}
	uri=linphone_address_new(username);
	if (uri!=NULL){
		return _linphone_core_destroy_addr_if_not_sip(uri);
	}

	return NULL;
}

781 782 783 784 785 786 787
void linphone_proxy_config_set_etag(LinphoneProxyConfig *cfg,const char* sip_etag) {
	if (cfg->sip_etag) ms_free(cfg->sip_etag);
	if (sip_etag)
		cfg->sip_etag = ms_strdup(sip_etag);
	else
		cfg->sip_etag = NULL;
}
smorlat's avatar
smorlat committed
788 789 790
/**
 * Commits modification made to the proxy configuration.
**/
791
LinphoneStatus linphone_proxy_config_done(LinphoneProxyConfig *cfg)
smorlat's avatar
smorlat committed
792
{
793 794
	LinphoneProxyConfigAddressComparisonResult res;

795
	if (!linphone_proxy_config_check(cfg->lc,cfg))
796 797
		return -1;

798
	/*check if server address has changed*/
799
	res = linphone_proxy_config_is_server_config_changed(cfg);
800
	if (res != LinphoneProxyConfigAddressEqual) {
801
		/* server config has changed, need to unregister from previous first*/
802
		if (cfg->op