sal.c 30.2 KB
Newer Older
Simon Morlat's avatar
Simon Morlat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)

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 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
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Simon Morlat's avatar
Simon Morlat committed
18 19
*/

20
/**
21
This file contains SAL API functions that do not depend on the underlying implementation (like belle-sip).
Simon Morlat's avatar
Simon Morlat committed
22
**/
23 24 25
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
26
#include "sal/sal.h"
27

28 29 30

#include <ctype.h>

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

const char *sal_multicast_role_to_string(SalMulticastRole role){
	switch(role){
		case SalMulticastInactive:
			return "inactive";
		case SalMulticastReceiver:
			return "receiver";
		case SalMulticastSender:
			return "sender";
		case SalMulticastSenderReceiver:
			return "sender-receiver";
	}
	return "INVALID";
}

46
const char* sal_transport_to_string(SalTransport transport) {
47 48 49 50 51 52 53 54
	switch (transport) {
		case SalTransportUDP:return "udp";
		case SalTransportTCP: return "tcp";
		case SalTransportTLS:return "tls";
		case SalTransportDTLS:return "dtls";
		default: {
			ms_fatal("Unexpected transport [%i]",transport);
			return NULL;
55
		}
56
	}
57
}
58

59
SalTransport sal_transport_parse(const char* param) {
jehan's avatar
jehan committed
60
	if (!param) return SalTransportUDP;
61 62 63 64 65 66
	if (strcasecmp("udp",param)==0) return SalTransportUDP;
	if (strcasecmp("tcp",param)==0) return SalTransportTCP;
	if (strcasecmp("tls",param)==0) return SalTransportTLS;
	if (strcasecmp("dtls",param)==0) return SalTransportDTLS;
	ms_error("Unknown transport type[%s], returning UDP", param);
	return SalTransportUDP;
67
}
68

Simon Morlat's avatar
Simon Morlat committed
69
SalMediaDescription *sal_media_description_new(){
70
	SalMediaDescription *md=ms_new0(SalMediaDescription,1);
71
	int i;
72
	md->refcount=1;
73
	for(i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
74
		md->streams[i].dir=SalStreamInactive;
75 76
		md->streams[i].rtp_port = 0;
		md->streams[i].rtcp_port = 0;
johan's avatar
johan committed
77
		md->streams[i].haveZrtpHash = 0;
78
	}
Simon Morlat's avatar
Simon Morlat committed
79
	return md;
Simon Morlat's avatar
Simon Morlat committed
80 81
}

82
static void sal_media_description_destroy(SalMediaDescription *md){
Simon Morlat's avatar
Simon Morlat committed
83 84
	int i;
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;i++){
85 86
		bctbx_list_free_with_data(md->streams[i].payloads,(void (*)(void *))payload_type_destroy);
		bctbx_list_free_with_data(md->streams[i].already_assigned_payloads,(void (*)(void *))payload_type_destroy);
87
		md->streams[i].payloads=NULL;
88
		md->streams[i].already_assigned_payloads=NULL;
89
		sal_custom_sdp_attribute_free(md->streams[i].custom_sdp_attributes);
Simon Morlat's avatar
Simon Morlat committed
90
	}
91
	sal_custom_sdp_attribute_free(md->custom_sdp_attributes);
Simon Morlat's avatar
Simon Morlat committed
92 93 94
	ms_free(md);
}

95
SalMediaDescription * sal_media_description_ref(SalMediaDescription *md){
96
	md->refcount++;
97
	return md;
98 99 100 101 102 103 104 105 106
}

void sal_media_description_unref(SalMediaDescription *md){
	md->refcount--;
	if (md->refcount==0){
		sal_media_description_destroy (md);
	}
}

107
SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, SalMediaProto proto, SalStreamType type){
108
	int i;
109
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
110
		SalStreamDescription *ss=&md->streams[i];
111
		if (!sal_stream_description_active(ss)) continue;
112 113 114 115 116
		if (ss->proto==proto && ss->type==type) return ss;
	}
	return NULL;
}

117 118 119
unsigned int sal_media_description_nb_active_streams_of_type(SalMediaDescription *md, SalStreamType type) {
	unsigned int i;
	unsigned int nb = 0;
120
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i) {
121
		if (!sal_stream_description_active(&md->streams[i])) continue;
122 123 124 125 126 127 128
		if (md->streams[i].type == type) nb++;
	}
	return nb;
}

SalStreamDescription * sal_media_description_get_active_stream_of_type(SalMediaDescription *md, SalStreamType type, unsigned int idx) {
	unsigned int i;
129
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i) {
130
		if (!sal_stream_description_active(&md->streams[i])) continue;
131 132 133 134 135 136 137 138 139 140 141 142 143 144
		if (md->streams[i].type == type) {
			if (idx-- == 0) return &md->streams[i];
		}
	}
	return NULL;
}

SalStreamDescription * sal_media_description_find_secure_stream_of_type(SalMediaDescription *md, SalStreamType type) {
	SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type);
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type);
	return desc;
}

SalStreamDescription * sal_media_description_find_best_stream(SalMediaDescription *md, SalStreamType type) {
145 146 147
	SalStreamDescription *desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavpf, type);
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoUdpTlsRtpSavp, type);
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavpf, type);
148 149 150 151 152 153
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpSavp, type);
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvpf, type);
	if (desc == NULL) desc = sal_media_description_find_stream(md, SalProtoRtpAvp, type);
	return desc;
}

Simon Morlat's avatar
Simon Morlat committed
154
bool_t sal_media_description_empty(const SalMediaDescription *md){
155
	if (sal_media_description_get_nb_active_streams(md) > 0) return FALSE;
Simon Morlat's avatar
Simon Morlat committed
156 157 158 159 160
	return TRUE;
}

void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir){
	int i;
161
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
Simon Morlat's avatar
Simon Morlat committed
162
		SalStreamDescription *ss=&md->streams[i];
163
		if (!sal_stream_description_active(ss)) continue;
Simon Morlat's avatar
Simon Morlat committed
164 165 166 167
		ss->dir=stream_dir;
	}
}

168 169 170
int sal_media_description_get_nb_active_streams(const SalMediaDescription *md) {
	int i;
	int nb = 0;
171
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
172 173 174 175 176
		if (sal_stream_description_active(&md->streams[i])) nb++;
	}
	return nb;
}

177 178 179 180 181 182
static bool_t is_null_address(const char *addr){
	return strcmp(addr,"0.0.0.0")==0 || strcmp(addr,"::0")==0;
}

/*check for the presence of at least one stream with requested direction */
static bool_t has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){
Simon Morlat's avatar
Simon Morlat committed
183
	int i;
184

Simon Morlat's avatar
Simon Morlat committed
185
	/* we are looking for at least one stream with requested direction, inactive streams are ignored*/
186
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
Simon Morlat's avatar
Simon Morlat committed
187
		const SalStreamDescription *ss=&md->streams[i];
188
		if (!sal_stream_description_active(ss)) continue;
189 190 191
		if (ss->dir==stream_dir) {
			return TRUE;
		}
192
		/*compatibility check for phones that only used the null address and no attributes */
193
		if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->rtp_addr))){
194
			return TRUE;
195
		}
196 197 198 199 200 201
	}
	return FALSE;
}

bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){
	if (stream_dir==SalStreamRecvOnly){
202
		return has_dir(md, SalStreamRecvOnly) && !(has_dir(md,SalStreamSendOnly) || has_dir(md,SalStreamSendRecv));
203
	}else if (stream_dir==SalStreamSendOnly){
204
		return has_dir(md, SalStreamSendOnly) && !(has_dir(md,SalStreamRecvOnly) || has_dir(md,SalStreamSendRecv));
205 206 207 208 209 210 211
	}else if (stream_dir==SalStreamSendRecv){
		return has_dir(md,SalStreamSendRecv);
	}else{
		/*SalStreamInactive*/
		if (has_dir(md,SalStreamSendOnly) || has_dir(md,SalStreamSendRecv)  || has_dir(md,SalStreamRecvOnly))
			return FALSE;
		else return TRUE;
Simon Morlat's avatar
Simon Morlat committed
212
	}
213
	return FALSE;
Simon Morlat's avatar
Simon Morlat committed
214
}
215

216 217 218 219
bool_t sal_stream_description_active(const SalStreamDescription *sd) {
	return (sd->rtp_port > 0);
}

220
/*these are switch case, so that when a new proto is added we can't forget to modify this function*/
221
bool_t sal_stream_description_has_avpf(const SalStreamDescription *sd) {
222 223 224 225 226 227 228 229 230 231 232 233
	switch (sd->proto){
		case SalProtoRtpAvpf:
		case SalProtoRtpSavpf:
		case SalProtoUdpTlsRtpSavpf:
			return TRUE;
		case SalProtoRtpAvp:
		case SalProtoRtpSavp:
		case SalProtoUdpTlsRtpSavp:
		case SalProtoOther:
			return FALSE;
	}
	return FALSE;
234 235
}

236 237 238 239
bool_t sal_stream_description_has_ipv6(const SalStreamDescription *sd){
	return strchr(sd->rtp_addr,':') != NULL;
}

240
bool_t sal_stream_description_has_implicit_avpf(const SalStreamDescription *sd){
241
	return sd->implicit_rtcp_fb;
242
}
243
/*these are switch case, so that when a new proto is added we can't forget to modify this function*/
244
bool_t sal_stream_description_has_srtp(const SalStreamDescription *sd) {
245 246 247 248 249 250 251 252 253 254 255 256
	switch (sd->proto){
		case SalProtoRtpSavp:
		case SalProtoRtpSavpf:
			return TRUE;
		case SalProtoRtpAvp:
		case SalProtoRtpAvpf:
		case SalProtoUdpTlsRtpSavpf:
		case SalProtoUdpTlsRtpSavp:
		case SalProtoOther:
			return FALSE;
	}
	return FALSE;
257 258
}

johan's avatar
johan committed
259
bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd) {
260 261 262 263 264 265 266 267 268 269 270 271
	switch (sd->proto){
		case SalProtoUdpTlsRtpSavpf:
		case SalProtoUdpTlsRtpSavp:
			return TRUE;
		case SalProtoRtpSavp:
		case SalProtoRtpSavpf:
		case SalProtoRtpAvp:
		case SalProtoRtpAvpf:
		case SalProtoOther:
			return FALSE;
	}
	return FALSE;
johan's avatar
johan committed
272 273
}

johan's avatar
johan committed
274 275 276 277 278
bool_t sal_stream_description_has_zrtp(const SalStreamDescription *sd) {
	if (sd->haveZrtpHash==1) return TRUE;
	return FALSE;
}

279 280 281
bool_t sal_media_description_has_avpf(const SalMediaDescription *md) {
	int i;
	if (md->nb_streams == 0) return FALSE;
282
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
283 284 285 286 287 288
		if (!sal_stream_description_active(&md->streams[i])) continue;
		if (sal_stream_description_has_avpf(&md->streams[i]) != TRUE) return FALSE;
	}
	return TRUE;
}

289 290 291 292 293 294 295 296 297 298
bool_t sal_media_description_has_implicit_avpf(const SalMediaDescription *md) {
    int i;
    if (md->nb_streams == 0) return FALSE;
    for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
        if (!sal_stream_description_active(&md->streams[i])) continue;
        if (sal_stream_description_has_implicit_avpf(&md->streams[i]) != TRUE) return FALSE;
    }
    return TRUE;
}

299 300 301
bool_t sal_media_description_has_srtp(const SalMediaDescription *md) {
	int i;
	if (md->nb_streams == 0) return FALSE;
302
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
303 304 305 306 307 308
		if (!sal_stream_description_active(&md->streams[i])) continue;
		if (sal_stream_description_has_srtp(&md->streams[i]) != TRUE) return FALSE;
	}
	return TRUE;
}

johan's avatar
johan committed
309 310 311
bool_t sal_media_description_has_dtls(const SalMediaDescription *md) {
	int i;
	if (md->nb_streams == 0) return FALSE;
312
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
johan's avatar
johan committed
313 314 315 316 317 318
		if (!sal_stream_description_active(&md->streams[i])) continue;
		if (sal_stream_description_has_dtls(&md->streams[i]) != TRUE) return FALSE;
	}
	return TRUE;
}

johan's avatar
johan committed
319 320 321 322 323 324 325 326 327 328
bool_t sal_media_description_has_zrtp(const SalMediaDescription *md) {
	int i;
	if (md->nb_streams == 0) return FALSE;
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
		if (!sal_stream_description_active(&md->streams[i])) continue;
		if (sal_stream_description_has_zrtp(&md->streams[i]) != TRUE) return FALSE;
	}
	return TRUE;
}

329 330 331 332 333 334 335 336 337 338 339 340 341 342
bool_t sal_media_description_has_ipv6(const SalMediaDescription *md){
	int i;
	if (md->nb_streams == 0) return FALSE;
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
		if (!sal_stream_description_active(&md->streams[i])) continue;
		if (md->streams[i].rtp_addr[0] != '\0'){
			if (!sal_stream_description_has_ipv6(&md->streams[i])) return FALSE;
		}else{
			if (strchr(md->addr,':') == NULL) return FALSE;
		}
	}
	return TRUE;
}

343 344 345 346 347 348 349 350 351 352 353 354 355
/*
static bool_t fmtp_equals(const char *p1, const char *p2){
	if (p1 && p2 && strcmp(p1,p2)==0) return TRUE;
	if (p1==NULL && p2==NULL) return TRUE;
	return FALSE;
}
*/

static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){
	if (p1->type!=p2->type) return FALSE;
	if (strcmp(p1->mime_type,p2->mime_type)!=0) return FALSE;
	if (p1->clock_rate!=p2->clock_rate) return FALSE;
	if (p1->channels!=p2->channels) return FALSE;
356
	if (payload_type_get_number(p1) != payload_type_get_number(p2)) return FALSE;
357 358 359 360 361
	/*
	 Do not compare fmtp right now: they are modified internally when the call is started
	*/
	/*
	if (!fmtp_equals(p1->recv_fmtp,p2->recv_fmtp) ||
362
		!fmtp_equals(p1->send_fmtp,p2->send_fmtp))
363 364 365 366 367
		return FALSE;
	*/
	return TRUE;
}

Simon Morlat's avatar
Simon Morlat committed
368 369 370 371
static bool_t is_recv_only(PayloadType *p){
	return (p->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && ! (p->flags & PAYLOAD_TYPE_FLAG_CAN_SEND);
}

372 373
static bool_t payload_list_equals(const bctbx_list_t *l1, const bctbx_list_t *l2){
	const bctbx_list_t *e1,*e2;
374 375 376 377 378 379
	for(e1=l1,e2=l2;e1!=NULL && e2!=NULL; e1=e1->next,e2=e2->next){
		PayloadType *p1=(PayloadType*)e1->data;
		PayloadType *p2=(PayloadType*)e2->data;
		if (!payload_type_equals(p1,p2))
			return FALSE;
	}
Simon Morlat's avatar
Simon Morlat committed
380 381 382 383 384 385
	if (e1!=NULL){
		/*skip possible recv-only payloads*/
		for(;e1!=NULL && is_recv_only((PayloadType*)e1->data);e1=e1->next){
			ms_message("Skipping recv-only payload type...");
		}
	}
386 387 388 389 390 391 392
	if (e1!=NULL || e2!=NULL){
		/*means one list is longer than the other*/
		return FALSE;
	}
	return TRUE;
}

393 394
int sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2) {
	int result = SAL_MEDIA_DESCRIPTION_UNCHANGED;
395
	int i;
396 397 398 399

	/* A different proto should result in SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED but the encryption change
	   needs a stream restart for now, so use SAL_MEDIA_DESCRIPTION_CODEC_CHANGED */
	if (sd1->proto != sd2->proto) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
400 401
	for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
		if ((sd1->crypto[i].tag != sd2->crypto[i].tag)
402 403 404 405 406
			|| (sd1->crypto[i].algo != sd2->crypto[i].algo)){
			result|=SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED;
		}
		if ((strncmp(sd1->crypto[i].master_key, sd2->crypto[i].master_key, sizeof(sd1->crypto[i].master_key) - 1))) {
			result |= SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED;
407 408
		}
	}
409 410 411

	if (sd1->type != sd2->type) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
	if (strcmp(sd1->rtp_addr, sd2->rtp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
412 413
	if (sd1->rtp_addr[0]!='\0' && sd2->rtp_addr[0]!='\0' && ms_is_multicast(sd1->rtp_addr) != ms_is_multicast(sd2->rtp_addr))
			result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED;
414 415 416 417 418 419 420 421 422 423 424
	if (sd1->rtp_port != sd2->rtp_port) {
		if ((sd1->rtp_port == 0) || (sd2->rtp_port == 0)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
		else result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
	}
	if (strcmp(sd1->rtcp_addr, sd2->rtcp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
	if (sd1->rtcp_port != sd2->rtcp_port) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
	if (!payload_list_equals(sd1->payloads, sd2->payloads)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
	if (sd1->bandwidth != sd2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
	if (sd1->ptime != sd2->ptime) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
	if (sd1->dir != sd2->dir) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;

425
	/* ICE */
426 427
	if (strcmp(sd1->ice_ufrag, sd2->ice_ufrag) != 0) result |= SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED;
	if (strcmp(sd1->ice_pwd, sd2->ice_pwd) != 0) result |= SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED;
428

jehan's avatar
jehan committed
429 430 431 432
	/*DTLS*/
	if (sd1->dtls_role != sd2->dtls_role) result |= SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED;
	if (strcmp(sd1->dtls_fingerprint, sd2->dtls_fingerprint) != 0) result |= SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED;

433
	return result;
434 435
}

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
char * sal_media_description_print_differences(int result){
	char *out = NULL;
	if (result & SAL_MEDIA_DESCRIPTION_CODEC_CHANGED){
		out = ms_strcat_printf(out, "%s ", "CODEC_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED){
		out = ms_strcat_printf(out, "%s ", "NETWORK_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED){
		out = ms_strcat_printf(out, "%s ", "ICE_RESTART_DETECTED");
		result &= ~SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED){
		out = ms_strcat_printf(out, "%s ", "CRYPTO_KEYS_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED){
		out = ms_strcat_printf(out, "%s ", "NETWORK_XXXCAST_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED){
		out = ms_strcat_printf(out, "%s ", "STREAMS_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED){
		out = ms_strcat_printf(out, "%s ", "CRYPTO_POLICY_CHANGED");
		result &= ~SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED;
	}
	if (result & SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION){
		out = ms_strcat_printf(out, "%s ", "FORCE_STREAM_RECONSTRUCTION");
		result &= ~SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION;
	}
	if (result){
		ms_fatal("There are unhandled result bitmasks in sal_media_description_print_differences(), fix it");
	}
	if (!out) out = ms_strdup("NONE");
	return out;
}

477 478
int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2) {
	int result = SAL_MEDIA_DESCRIPTION_UNCHANGED;
479
	int i;
480 481

	if (strcmp(md1->addr, md2->addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
482 483
	if (md1->addr[0]!='\0' && md2->addr[0]!='\0' && ms_is_multicast(md1->addr) != ms_is_multicast(md2->addr))
		result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED;
484
	if (md1->nb_streams != md2->nb_streams) result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED;
485
	if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
486 487

	/* ICE */
488 489
	if (strcmp(md1->ice_ufrag, md2->ice_ufrag) != 0) result |= SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED;
	if (strcmp(md1->ice_pwd, md2->ice_pwd) != 0) result |= SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED;
490

491
	for(i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i){
492
		if (!sal_stream_description_active(&md1->streams[i]) && !sal_stream_description_active(&md2->streams[i])) continue;
493
		result |= sal_stream_description_equals(&md1->streams[i], &md2->streams[i]);
494
	}
495
	return result;
496
}
497

498 499 500 501 502 503 504 505
static void assign_address(SalAddress** address, const char *value){
	if (*address){
		sal_address_destroy(*address);
		*address=NULL;
	}
	if (value)
		*address=sal_address_new(value);
}
506

Simon Morlat's avatar
Simon Morlat committed
507 508 509 510 511 512 513 514
static void assign_string(char **str, const char *arg){
	if (*str){
		ms_free(*str);
		*str=NULL;
	}
	if (arg)
		*str=ms_strdup(arg);
}
jehan's avatar
jehan committed
515

516
void sal_op_set_contact_address(SalOp *op, const SalAddress *address){
517 518
	if (((SalOpBase*)op)->contact_address) sal_address_destroy(((SalOpBase*)op)->contact_address);
	((SalOpBase*)op)->contact_address=address?sal_address_clone(address):NULL;
519 520 521 522
}
const SalAddress* sal_op_get_contact_address(const SalOp *op) {
	return ((SalOpBase*)op)->contact_address;
}
523 524 525 526 527 528

const SalAddress*sal_op_get_remote_contact_address(const SalOp* op)
{
	return ((SalOpBase*)op)->remote_contact_address;
}

jehan's avatar
jehan committed
529 530 531 532 533 534 535 536 537
#define SET_PARAM(op,name) \
		char* name##_string=NULL; \
		assign_address(&((SalOpBase*)op)->name##_address,name); \
		if (((SalOpBase*)op)->name##_address) { \
			name##_string=sal_address_as_string(((SalOpBase*)op)->name##_address); \
		}\
		assign_string(&((SalOpBase*)op)->name,name##_string); \
		if(name##_string) ms_free(name##_string);

538

Simon Morlat's avatar
Simon Morlat committed
539
void sal_op_set_route(SalOp *op, const char *route){
540 541 542
	char* route_string=(void *)0;
	SalOpBase* op_base = (SalOpBase*)op;
	if (op_base->route_addresses) {
543 544
		bctbx_list_for_each(op_base->route_addresses,(void (*)(void *))sal_address_destroy);
		op_base->route_addresses=bctbx_list_free(op_base->route_addresses);
545 546
	}
	if (route) {
547
		op_base->route_addresses=bctbx_list_append(NULL,NULL);
548 549 550 551
		assign_address((SalAddress**)&(op_base->route_addresses->data),route);
		route_string=sal_address_as_string((SalAddress*)op_base->route_addresses->data); \
	}
	assign_string(&op_base->route,route_string); \
552
	if(route_string) ms_free(route_string);
jehan's avatar
jehan committed
553
}
554
const bctbx_list_t* sal_op_get_route_addresses(const SalOp *op) {
555
	return ((SalOpBase*)op)->route_addresses;
jehan's avatar
jehan committed
556 557 558 559 560
}
void sal_op_set_route_address(SalOp *op, const SalAddress *address){
	char* address_string=sal_address_as_string(address); /*can probably be optimized*/
	sal_op_set_route(op,address_string);
	ms_free(address_string);
Simon Morlat's avatar
Simon Morlat committed
561
}
562 563 564
void sal_op_add_route_address(SalOp *op, const SalAddress *address){
	SalOpBase* op_base = (SalOpBase*)op;
	if (op_base->route_addresses) {
565
		op_base->route_addresses=bctbx_list_append(op_base->route_addresses,(void*)sal_address_clone(address));
566 567
	} else {
		sal_op_set_route_address(op,address);
568 569
	}
}
570 571 572 573 574 575 576
void sal_op_set_realm(SalOp *op, const char *realm){
	SalOpBase* op_base = (SalOpBase*)op;
	if (op_base->realm != NULL){
		ms_free(op_base->realm);
	}
	op_base->realm = ms_strdup(realm);
}
Simon Morlat's avatar
Simon Morlat committed
577
void sal_op_set_from(SalOp *op, const char *from){
578 579 580 581 582 583
	SET_PARAM(op,from);
}
void sal_op_set_from_address(SalOp *op, const SalAddress *from){
	char* address_string=sal_address_as_string(from); /*can probably be optimized*/
	sal_op_set_from(op,address_string);
	ms_free(address_string);
Simon Morlat's avatar
Simon Morlat committed
584 585
}
void sal_op_set_to(SalOp *op, const char *to){
586 587 588 589 590 591
	SET_PARAM(op,to);
}
void sal_op_set_to_address(SalOp *op, const SalAddress *to){
	char* address_string=sal_address_as_string(to); /*can probably be optimized*/
	sal_op_set_to(op,address_string);
	ms_free(address_string);
Simon Morlat's avatar
Simon Morlat committed
592
}
593 594 595 596
void sal_op_set_diversion_address(SalOp *op, const SalAddress *diversion){
	if (((SalOpBase*)op)->diversion_address) sal_address_destroy(((SalOpBase*)op)->diversion_address);
	((SalOpBase*)op)->diversion_address=diversion?sal_address_clone(diversion):NULL;
}
Simon Morlat's avatar
Simon Morlat committed
597 598 599 600
void sal_op_set_user_pointer(SalOp *op, void *up){
	((SalOpBase*)op)->user_pointer=up;
}

601 602 603 604
Sal *sal_op_get_sal(const SalOp *op){
	return ((SalOpBase*)op)->root;
}

Simon Morlat's avatar
Simon Morlat committed
605 606 607
const char *sal_op_get_from(const SalOp *op){
	return ((SalOpBase*)op)->from;
}
608 609 610
const SalAddress *sal_op_get_from_address(const SalOp *op){
	return ((SalOpBase*)op)->from_address;
}
Simon Morlat's avatar
Simon Morlat committed
611 612 613 614 615

const char *sal_op_get_to(const SalOp *op){
	return ((SalOpBase*)op)->to;
}

616 617 618
const SalAddress *sal_op_get_to_address(const SalOp *op){
	return ((SalOpBase*)op)->to_address;
}
619

620 621 622 623
const SalAddress *sal_op_get_diversion_address(const SalOp *op){
	return ((SalOpBase*)op)->diversion_address;
}

624 625 626 627
const char *sal_op_get_remote_ua(const SalOp *op){
	return ((SalOpBase*)op)->remote_ua;
}

Simon Morlat's avatar
Simon Morlat committed
628 629 630 631
void *sal_op_get_user_pointer(const SalOp *op){
	return ((SalOpBase*)op)->user_pointer;
}

632 633 634 635
const char *sal_op_get_proxy(const SalOp *op){
	return ((SalOpBase*)op)->route;
}

636 637 638
const char *sal_op_get_network_origin(const SalOp *op){
	return ((SalOpBase*)op)->origin;
}
jehan's avatar
jehan committed
639 640 641
const char* sal_op_get_call_id(const SalOp *op) {
	return  ((SalOpBase*)op)->call_id;
}
642

Simon Morlat's avatar
Simon Morlat committed
643 644 645 646 647
void __sal_op_init(SalOp *b, Sal *sal){
	memset(b,0,sizeof(SalOpBase));
	((SalOpBase*)b)->root=sal;
}

648
void __sal_op_set_network_origin(SalOp *op, const char *origin){
jehan's avatar
jehan committed
649
	SET_PARAM(op,origin);
650 651
}

652
void __sal_op_set_remote_contact(SalOp *op, const char* remote_contact){
653 654 655
	assign_address(&((SalOpBase*)op)->remote_contact_address,remote_contact);\
	/*to preserve header params*/
	assign_string(&((SalOpBase*)op)->remote_contact,remote_contact); \
656
}
657

jehan's avatar
jehan committed
658 659 660 661 662
void __sal_op_set_network_origin_address(SalOp *op, SalAddress *origin){
	char* address_string=sal_address_as_string(origin); /*can probably be optimized*/
	__sal_op_set_network_origin(op,address_string);
	ms_free(address_string);
}
663

Simon Morlat's avatar
Simon Morlat committed
664 665
void __sal_op_free(SalOp *op){
	SalOpBase *b=(SalOpBase *)op;
666 667 668 669 670 671 672 673
	if (b->from_address){
		sal_address_destroy(b->from_address);
		b->from_address=NULL;
	}
	if (b->to_address){
		sal_address_destroy(b->to_address);
		b->to_address=NULL;
	}
674

675 676 677 678
	if (b->service_route){
		sal_address_destroy(b->service_route);
		b->service_route=NULL;
	}
679

680 681 682 683
	if (b->origin_address){
		sal_address_destroy(b->origin_address);
		b->origin_address=NULL;
	}
684

Simon Morlat's avatar
Simon Morlat committed
685 686 687 688 689 690 691 692 693 694 695 696
	if (b->from) {
		ms_free(b->from);
		b->from=NULL;
	}
	if (b->to) {
		ms_free(b->to);
		b->to=NULL;
	}
	if (b->route) {
		ms_free(b->route);
		b->route=NULL;
	}
697 698 699 700
	if (b->realm) {
		ms_free(b->realm);
		b->realm=NULL;
	}
701 702 703
	if (b->contact_address) {
		sal_address_destroy(b->contact_address);
	}
704 705 706 707
	if (b->origin){
		ms_free(b->origin);
		b->origin=NULL;
	}
708 709 710 711
	if (b->remote_ua){
		ms_free(b->remote_ua);
		b->remote_ua=NULL;
	}
712 713 714 715
	if (b->remote_contact){
		ms_free(b->remote_contact);
		b->remote_contact=NULL;
	}
716 717 718
	if (b->remote_contact_address){
		sal_address_destroy(b->remote_contact_address);
	}
Simon Morlat's avatar
Simon Morlat committed
719
	if (b->local_media)
720
		sal_media_description_unref(b->local_media);
Simon Morlat's avatar
Simon Morlat committed
721
	if (b->remote_media)
722
		sal_media_description_unref(b->remote_media);
jehan's avatar
jehan committed
723 724
	if (b->call_id)
		ms_free((void*)b->call_id);
725 726 727
	if (b->service_route) {
		sal_address_destroy(b->service_route);
	}
728
	if (b->route_addresses){
729 730
		bctbx_list_for_each(b->route_addresses,(void (*)(void*)) sal_address_destroy);
		b->route_addresses=bctbx_list_free(b->route_addresses);
731
	}
Simon Morlat's avatar
Simon Morlat committed
732 733 734 735
	if (b->recv_custom_headers)
		sal_custom_header_free(b->recv_custom_headers);
	if (b->sent_custom_headers)
		sal_custom_header_free(b->sent_custom_headers);
736 737 738 739 740
	
	if (b->entity_tag != NULL){
		ms_free(b->entity_tag);
		b->entity_tag = NULL;
	}
Simon Morlat's avatar
Simon Morlat committed
741 742
	ms_free(op);
}
743

744 745 746
SalAuthInfo* sal_auth_info_new() {
	return ms_new0(SalAuthInfo,1);
}
747

748 749 750 751 752
SalAuthInfo* sal_auth_info_clone(const SalAuthInfo* auth_info) {
	SalAuthInfo* new_auth_info=sal_auth_info_new();
	new_auth_info->username=auth_info->username?ms_strdup(auth_info->username):NULL;
	new_auth_info->userid=auth_info->userid?ms_strdup(auth_info->userid):NULL;
	new_auth_info->realm=auth_info->realm?ms_strdup(auth_info->realm):NULL;
753
	new_auth_info->domain=auth_info->realm?ms_strdup(auth_info->domain):NULL;
754 755 756
	new_auth_info->password=auth_info->password?ms_strdup(auth_info->password):NULL;
	return new_auth_info;
}
757

758
void sal_auth_info_delete(SalAuthInfo* auth_info) {
759 760 761
	if (auth_info->username) ms_free(auth_info->username);
	if (auth_info->userid) ms_free(auth_info->userid);
	if (auth_info->realm) ms_free(auth_info->realm);
762
	if (auth_info->domain) ms_free(auth_info->domain);
763
	if (auth_info->password) ms_free(auth_info->password);
Guillaume Beraudo's avatar
Guillaume Beraudo committed
764
	if (auth_info->ha1) ms_free(auth_info->ha1);
765 766
	if (auth_info->certificates) sal_certificates_chain_delete(auth_info->certificates);
	if (auth_info->key) sal_signing_key_delete(auth_info->key);
767
	ms_free(auth_info);
768
}
769

770 771


jehan's avatar
jehan committed
772 773
const char* sal_stream_type_to_string(SalStreamType type) {
	switch (type) {
774 775 776
	case SalAudio: return "audio";
	case SalVideo: return "video";
	case SalText: return "text";
jehan's avatar
jehan committed
777 778 779 780
	default: return "other";
	}
}

781 782 783 784 785
const char *sal_stream_description_get_type_as_string(const SalStreamDescription *desc){
	if (desc->type==SalOther) return desc->typeother;
	else return sal_stream_type_to_string(desc->type);
}

jehan's avatar
jehan committed
786 787 788 789
const char* sal_media_proto_to_string(SalMediaProto type) {
	switch (type) {
	case SalProtoRtpAvp:return "RTP/AVP";
	case SalProtoRtpSavp:return "RTP/SAVP";
johan's avatar
johan committed
790
	case SalProtoUdpTlsRtpSavp:return "UDP/TLS/RTP/SAVP";
791 792
	case SalProtoRtpAvpf:return "RTP/AVPF";
	case SalProtoRtpSavpf:return "RTP/SAVPF";
johan's avatar
johan committed
793
	case SalProtoUdpTlsRtpSavpf:return "UDP/TLS/RTP/SAVPF";
jehan's avatar
jehan committed
794 795 796 797
	default: return "unknown";
	}
}

798 799 800 801 802
const char *sal_stream_description_get_proto_as_string(const SalStreamDescription *desc){
	if (desc->proto==SalProtoOther) return desc->proto_other;
	else return sal_media_proto_to_string(desc->proto);
}

jehan's avatar
jehan committed
803 804 805 806 807 808 809 810 811

const char* sal_stream_dir_to_string(SalStreamDir type) {
	switch (type) {
	case SalStreamSendRecv:return "sendrecv";
	case SalStreamSendOnly:return "sendonly";
	case SalStreamRecvOnly:return "recvonly";
	case SalStreamInactive:return "inative";
	default: return "unknown";
	}
812

jehan's avatar
jehan committed
813
}
jehan's avatar
jehan committed
814 815 816 817 818 819 820 821 822

const char* sal_reason_to_string(const SalReason reason) {
	switch (reason) {
	case SalReasonDeclined : return "SalReasonDeclined";
	case SalReasonBusy: return "SalReasonBusy";
	case SalReasonRedirect: return "SalReasonRedirect";
	case SalReasonTemporarilyUnavailable: return "SalReasonTemporarilyUnavailable";
	case SalReasonNotFound: return "SalReasonNotFound";
	case SalReasonDoNotDisturb: return "SalReasonDoNotDisturb";
823
	case SalReasonUnsupportedContent: return "SalReasonUnsupportedContent";
jehan's avatar
jehan committed
824 825
	case SalReasonForbidden: return "SalReasonForbidden";
	case SalReasonUnknown: return "SalReasonUnknown";
826
	case SalReasonServiceUnavailable: return "SalReasonServiceUnavailable";
827
	case SalReasonNotAcceptable: return "SalReasonNotAcceptable";
jehan's avatar
jehan committed
828 829 830
	default: return "Unkown reason";
	}
}
831 832 833 834 835 836 837 838 839
const SalAddress* sal_op_get_service_route(const SalOp *op) {
	return ((SalOpBase*)op)->service_route;
}
void sal_op_set_service_route(SalOp *op,const SalAddress* service_route) {
	if (((SalOpBase*)op)->service_route)
		sal_address_destroy(((SalOpBase*)op)->service_route);

	((SalOpBase*)op)->service_route=service_route?sal_address_clone(service_route):NULL;
}
jehan's avatar
jehan committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856

const char* sal_presence_status_to_string(const SalPresenceStatus status) {
	switch (status) {
	case SalPresenceOffline: return "SalPresenceOffline";
	case SalPresenceOnline: return "SalPresenceOnline";
	case SalPresenceBusy: return "SalPresenceBusy";
	case SalPresenceBerightback: return "SalPresenceBerightback";
	case SalPresenceAway: return "SalPresenceAway";
	case SalPresenceOnthephone: return "SalPresenceOnthephone";
	case SalPresenceOuttolunch: return "SalPresenceOuttolunch";
	case SalPresenceDonotdisturb: return "SalPresenceDonotdisturb";
	case SalPresenceMoved: return "SalPresenceMoved";
	case SalPresenceAltService: return "SalPresenceAltService";
	default : return "unknown";
	}

}
857 858 859 860 861 862 863 864 865 866 867
const char* sal_privacy_to_string(SalPrivacy privacy) {
	switch(privacy) {
	case SalPrivacyUser: return "user";
	case SalPrivacyHeader: return "header";
	case SalPrivacySession: return "session";
	case SalPrivacyId: return "id";
	case SalPrivacyNone: return "none";
	case SalPrivacyCritical: return "critical";
	default: return NULL;
	}
}
868

869 870 871 872 873
static void remove_trailing_spaces(char *line) {
	size_t size = size = strlen(line);
	char *end = line + size - 1;
	while (end >= line && isspace(*end)) {
		end--;
874
	}
875
    *(end + 1) = '\0';
876 877
}

878
static int line_get_value(const char *input, const char *key, char *value, size_t value_size, size_t *read){
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
	const char *end=strchr(input,'\n');
	char line[256]={0};
	char key_candidate[256];
	char *equal;
	size_t len;
	if (!end) len=strlen(input);
	else len=end +1 -input;
	*read=len;
	strncpy(line,input,MIN(len,sizeof(line)));
	equal=strchr(line,'=');
	if (!equal) return FALSE;
	*equal='\0';
	if (sscanf(line,"%s",key_candidate)!=1) return FALSE;
	if (strcasecmp(key,key_candidate)==0){
		equal++;
		remove_trailing_spaces(equal);
		strncpy(value,equal,value_size-1);
		value[value_size-1]='\0';
		return TRUE;
	}
	return FALSE;
}

int sal_lines_get_value(const char *data, const char *key, char *value, size_t value_size){
903
	size_t read=0;
904

905 906 907 908 909 910 911 912
	do{
		if (line_get_value(data,key,value,value_size,&read))
			return TRUE;
		data+=read;
	}while(read!=0);
	return FALSE;
}

913 914 915 916
const char *sal_op_get_entity_tag(const SalOp* op) {
	SalOpBase* op_base = (SalOpBase*)op;
	return op_base->entity_tag;
}
917 918


919 920 921 922 923 924 925 926 927 928
void sal_op_set_entity_tag(SalOp *op, const char* entity_tag) {
	SalOpBase* op_base = (SalOpBase*)op;
	if (op_base->entity_tag != NULL){
		ms_free(op_base->entity_tag);
	}
	if (entity_tag)
		op_base->entity_tag = ms_strdup(entity_tag);
	else
		op_base->entity_tag = NULL;
}
929 930 931 932

#ifdef BELLE_SIP_H
#error "You included belle-sip header other than just belle-sip/object.h in sal.c. This breaks design rules !"
#endif