provider.c 33.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
	belle-sip - SIP (RFC3261) library.
    Copyright (C) 2010  Belledonne Communications SARL

    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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include "belle_sip_internal.h"
jehan's avatar
jehan committed
20
#include "listeningpoint_internal.h"
Simon Morlat's avatar
Simon Morlat committed
21 22
#include "md5.h"

23
belle_sip_dialog_t *belle_sip_provider_find_dialog_from_msg(belle_sip_provider_t *prov, belle_sip_request_t *msg, int as_uas);
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
typedef struct authorization_context {
	belle_sip_header_call_id_t* callid;
	const char* scheme;
	const char* realm;
	const char* nonce;
	const char* qop;
	const char* opaque;
	int nonce_count;
	int is_proxy;
}authorization_context_t;

GET_SET_STRING(authorization_context,realm)
GET_SET_STRING(authorization_context,nonce)
GET_SET_STRING(authorization_context,qop)
GET_SET_STRING(authorization_context,scheme)
GET_SET_STRING(authorization_context,opaque)
GET_SET_INT(authorization_context,nonce_count,int)
static authorization_context_t* belle_sip_authorization_create(belle_sip_header_call_id_t* call_id) {
	authorization_context_t* result = malloc(sizeof(authorization_context_t));
	memset(result,0,sizeof(authorization_context_t));
	result->callid=call_id;
	belle_sip_object_ref(result->callid);
	return result;
}
static void belle_sip_authorization_destroy(authorization_context_t* object) {
	DESTROY_STRING(object,scheme);
	DESTROY_STRING(object,realm);
	DESTROY_STRING(object,nonce);
	DESTROY_STRING(object,qop);
jehan's avatar
jehan committed
54
	DESTROY_STRING(object,opaque);
55 56 57
	belle_sip_object_unref(object->callid);
	belle_sip_free(object);
}
58 59

static void belle_sip_provider_uninit(belle_sip_provider_t *p){
60 61 62 63 64 65 66
	p->listeners=belle_sip_list_free(p->listeners);
	p->listeners=belle_sip_list_free(p->internal_listeners);
	p->client_transactions=belle_sip_list_free_with_data(p->client_transactions,belle_sip_object_unref);
	p->server_transactions=belle_sip_list_free_with_data(p->server_transactions,belle_sip_object_unref);
	p->auth_contexts=belle_sip_list_free_with_data(p->auth_contexts,(void(*)(void*))belle_sip_authorization_destroy);
	p->dialogs=belle_sip_list_free_with_data(p->dialogs,belle_sip_object_unref);
	p->lps=belle_sip_list_free_with_data(p->lps,belle_sip_object_unref);
67 68
}

69
static void channel_state_changed(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_channel_state_t state){
70
	belle_sip_io_error_event_t ev;
71
	belle_sip_provider_t* prov=BELLE_SIP_PROVIDER(obj);
jehan's avatar
jehan committed
72
	if (state == BELLE_SIP_CHANNEL_ERROR || state == BELLE_SIP_CHANNEL_DISCONNECTED) {
73
		ev.transport=belle_sip_channel_get_transport_name(chan);
74 75
		ev.port=chan->peer_port;
		ev.host=chan->peer_name;
jehan's avatar
jehan committed
76
		ev.source=BELLE_SIP_OBJECT(prov);
77
		BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_io_error,&ev);
78 79
		/*IO error is also relevant for internal listener like refreshers*/
		BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->internal_listeners,process_io_error,&ev);
Simon Morlat's avatar
Simon Morlat committed
80
		if (!chan->force_close) belle_sip_provider_release_channel(prov,chan);
81
	}
82 83
}

Simon Morlat's avatar
Simon Morlat committed
84 85
static void belle_sip_provider_dispatch_request(belle_sip_provider_t* prov, belle_sip_request_t *req){
	belle_sip_server_transaction_t *t;
jehan's avatar
jehan committed
86
	belle_sip_request_event_t ev;
Simon Morlat's avatar
Simon Morlat committed
87 88 89 90 91 92
	t=belle_sip_provider_find_matching_server_transaction(prov,req);
	if (t){
		belle_sip_object_ref(t);
		belle_sip_server_transaction_on_request(t,req);
		belle_sip_object_unref(t);
	}else{
jehan's avatar
jehan committed
93
		ev.dialog=NULL;
jehan's avatar
jehan committed
94
		/* Should we limit to ACK ?  */
jehan's avatar
jehan committed
95 96
		/*Search for a dialog if exist */

97
		ev.dialog=belle_sip_provider_find_dialog_from_msg(prov,req,1/*request=uas*/);
jehan's avatar
jehan committed
98 99 100 101
		if (strcmp("ACK",belle_sip_request_get_method(req))==0){
			if (!ev.dialog) {
				belle_sip_warning("Provider [%p] received an unexpected stateless ACK",prov);
			} else 	if (belle_sip_dialog_handle_ack(ev.dialog,req)==-1){
102 103 104 105
				/*absorbed ACK retransmission, ignore */
				return;
			}
		}
Simon Morlat's avatar
Simon Morlat committed
106 107 108
		ev.source=prov;
		ev.server_transaction=NULL;
		ev.request=req;
jehan's avatar
jehan committed
109
		BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_request_event,&ev);
Simon Morlat's avatar
Simon Morlat committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	}
}

static void belle_sip_provider_dispatch_response(belle_sip_provider_t* prov, belle_sip_response_t *msg){
	belle_sip_client_transaction_t *t;
	t=belle_sip_provider_find_matching_client_transaction(prov,msg);
	/*
	 * If a transaction is found, pass it to the transaction and let it decide what to do.
	 * Else notifies directly.
	 */
	if (t){
		/*since the add_response may indirectly terminate the transaction, we need to guarantee the transaction is not freed
		 * until full completion*/
		belle_sip_object_ref(t);
		belle_sip_client_transaction_add_response(t,msg);
		belle_sip_object_unref(t);
	}else{
		belle_sip_response_event_t event;
Simon Morlat's avatar
Simon Morlat committed
128
		event.source=prov;
Simon Morlat's avatar
Simon Morlat committed
129
		event.client_transaction=NULL;
Simon Morlat's avatar
Simon Morlat committed
130
		event.dialog=NULL;
Simon Morlat's avatar
Simon Morlat committed
131
		event.response=msg;
jehan's avatar
jehan committed
132
		BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_response_event,&event);
Simon Morlat's avatar
Simon Morlat committed
133 134 135 136 137 138
	}
}

static void belle_sip_provider_dispatch_message(belle_sip_provider_t *prov, belle_sip_message_t *msg){
	if (belle_sip_message_is_request(msg)){
		belle_sip_provider_dispatch_request(prov,(belle_sip_request_t*)msg);
Simon Morlat's avatar
Simon Morlat committed
139
	}else{
Simon Morlat's avatar
Simon Morlat committed
140
		belle_sip_provider_dispatch_response(prov,(belle_sip_response_t*)msg);
Simon Morlat's avatar
Simon Morlat committed
141
	}
142
	belle_sip_object_unref(msg);
Simon Morlat's avatar
Simon Morlat committed
143 144
}

Simon Morlat's avatar
Simon Morlat committed
145 146 147
/*
 * takes example on 16.11 of RFC3261
 */
148
static void compute_hash_from_invariants(belle_sip_message_t *msg, char *branchid, size_t branchid_size, const char *initial){
Simon Morlat's avatar
Simon Morlat committed
149 150 151 152 153 154 155
	md5_state_t ctx;
	unsigned int cseq=belle_sip_header_cseq_get_seq_number(belle_sip_message_get_header_by_type(msg,belle_sip_header_cseq_t));
	char tmp[256]={0};
	uint8_t digest[16];
	const char*callid=belle_sip_header_call_id_get_call_id(belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t));
	const char *from_tag=belle_sip_header_from_get_tag(belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t));
	const char *to_tag=belle_sip_header_to_get_tag(belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t));
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	belle_sip_uri_t *requri=NULL;
	belle_sip_header_via_t *via=NULL;
	belle_sip_header_via_t *prev_via=NULL;
	const belle_sip_list_t *vias=belle_sip_message_get_headers(msg,"via");
	int is_request=belle_sip_message_is_request(msg);
	
	if (vias){
		via=(belle_sip_header_via_t*)vias->data;
		if (vias->next){
			prev_via=(belle_sip_header_via_t*)vias->next->data;
		}
	}
	
	if (is_request){
		requri=belle_sip_request_get_uri(BELLE_SIP_REQUEST(msg));
	}
Simon Morlat's avatar
Simon Morlat committed
172 173
	
	md5_init(&ctx);
174 175 176 177 178 179
	if (initial)
		md5_append(&ctx,(uint8_t*)initial,strlen(initial));
	if (requri){
		belle_sip_object_marshal((belle_sip_object_t*)requri,tmp,0,sizeof(tmp)-1);
		md5_append(&ctx,(uint8_t*)tmp,strlen(tmp));
	}
Simon Morlat's avatar
Simon Morlat committed
180 181 182 183 184 185
	if (from_tag)
		md5_append(&ctx,(uint8_t*)from_tag,strlen(from_tag));
	if (to_tag)
		md5_append(&ctx,(uint8_t*)to_tag,strlen(to_tag));
	md5_append(&ctx,(uint8_t*)callid,strlen(callid));
	md5_append(&ctx,(uint8_t*)&cseq,sizeof(cseq));
186 187 188 189 190 191 192 193 194 195
	if (is_request){
		if (prev_via){
			belle_sip_object_marshal((belle_sip_object_t*)prev_via,tmp,0,sizeof(tmp)-1);
			md5_append(&ctx,(uint8_t*)tmp,strlen(tmp));
		}
	}else{
		if (via){
			belle_sip_object_marshal((belle_sip_object_t*)via,tmp,0,sizeof(tmp)-1);
			md5_append(&ctx,(uint8_t*)tmp,strlen(tmp));
		}
Simon Morlat's avatar
Simon Morlat committed
196 197 198 199 200
	}
	md5_finish(&ctx,digest);
	belle_sip_octets_to_text(digest,sizeof(digest),branchid,branchid_size);
}

201 202 203
static void fix_outgoing_via(belle_sip_provider_t *p, belle_sip_channel_t *chan, belle_sip_message_t *msg){
	belle_sip_header_via_t *via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header(msg,"via"));
	belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(via),"rport",NULL);
Simon Morlat's avatar
Simon Morlat committed
204

205 206 207 208 209 210 211 212 213 214
	if (belle_sip_header_via_get_host(via)==NULL){
		const char *local_ip;
		int local_port;
		local_ip=belle_sip_channel_get_local_address(chan,&local_port);
		belle_sip_header_via_set_host(via,local_ip);
		belle_sip_header_via_set_port(via,local_port);
		belle_sip_header_via_set_protocol(via,"SIP/2.0");
		belle_sip_header_via_set_transport(via,belle_sip_channel_get_transport_name(chan));
	}
	if (belle_sip_header_via_get_branch(via)==NULL){
Simon Morlat's avatar
Simon Morlat committed
215 216 217
		/*branch id should not be set random here (stateless forwarding): but rather a hash of message invariants*/
		char branchid[24];
		char token[BELLE_SIP_BRANCH_ID_LENGTH];
218
		compute_hash_from_invariants(msg,token,sizeof(token),NULL);
Simon Morlat's avatar
Simon Morlat committed
219
		snprintf(branchid,sizeof(branchid)-1,BELLE_SIP_BRANCH_MAGIC_COOKIE ".%s",token);
220
		belle_sip_header_via_set_branch(via,branchid);
Simon Morlat's avatar
Simon Morlat committed
221
		belle_sip_message("Computing branch id %s for message sent statelessly", branchid);
222 223
	}
}
jehan's avatar
jehan committed
224

Simon Morlat's avatar
Simon Morlat committed
225 226
static int channel_on_event(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, unsigned int revents){
	if (revents & BELLE_SIP_EVENT_READ){
227
		belle_sip_message_t *msg=belle_sip_channel_pick_message(chan);
228
		belle_sip_provider_dispatch_message(BELLE_SIP_PROVIDER(obj),msg);
Simon Morlat's avatar
Simon Morlat committed
229 230 231 232
	}
	return 0;
}

233
static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){
jehan's avatar
jehan committed
234
	belle_sip_header_contact_t* contact = (belle_sip_header_contact_t*)belle_sip_message_get_header(msg,"Contact");
jehan's avatar
jehan committed
235
	belle_sip_header_content_length_t* content_lenght = (belle_sip_header_content_length_t*)belle_sip_message_get_header(msg,"Content-Length");
jehan's avatar
jehan committed
236
	belle_sip_uri_t* contact_uri;
Simon Morlat's avatar
Simon Morlat committed
237 238 239 240 241

	if (belle_sip_message_is_request(msg)){
		/*probably better to be in channel*/
		fix_outgoing_via((belle_sip_provider_t*)obj,chan,msg);
	}
Simon Morlat's avatar
Simon Morlat committed
242

Simon Morlat's avatar
Simon Morlat committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
	if (contact){
		/* fix the contact if empty*/
		if (!(contact_uri =belle_sip_header_address_get_uri((belle_sip_header_address_t*)contact))) {
			contact_uri = belle_sip_uri_new();
			belle_sip_header_address_set_uri((belle_sip_header_address_t*)contact,contact_uri);
		}
		if (!belle_sip_uri_get_host(contact_uri)) {
			belle_sip_uri_set_host(contact_uri,chan->local_ip);
		}
		if (belle_sip_uri_get_transport_param(contact_uri) == NULL && strcasecmp("udp",belle_sip_channel_get_transport_name(chan))!=0) {
			belle_sip_uri_set_transport_param(contact_uri,belle_sip_channel_get_transport_name_lower_case(chan));
		}
		if (belle_sip_uri_get_port(contact_uri) == 0 && chan->local_port!=5060) {
			belle_sip_uri_set_port(contact_uri,chan->local_port);
		}
jehan's avatar
jehan committed
258
	}
jehan's avatar
jehan committed
259 260 261 262
	if (!content_lenght && strcasecmp("udp",belle_sip_channel_get_transport_name(chan))!=0) {
		content_lenght = belle_sip_header_content_length_create(0);
		belle_sip_message_add_header(msg,(belle_sip_header_t*)content_lenght);
	}
263 264
}

265
BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_provider_t,belle_sip_channel_listener_t)
Simon Morlat's avatar
Simon Morlat committed
266
	channel_state_changed,
267 268
	channel_on_event,
	channel_on_sending
269 270 271 272
BELLE_SIP_IMPLEMENT_INTERFACE_END

BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_provider_t,belle_sip_channel_listener_t);
	
Simon Morlat's avatar
Simon Morlat committed
273
BELLE_SIP_INSTANCIATE_VPTR(belle_sip_provider_t,belle_sip_object_t,belle_sip_provider_uninit,NULL,NULL,FALSE);
Simon Morlat's avatar
Simon Morlat committed
274

275
belle_sip_provider_t *belle_sip_provider_new(belle_sip_stack_t *s, belle_sip_listening_point_t *lp){
Simon Morlat's avatar
Simon Morlat committed
276
	belle_sip_provider_t *p=belle_sip_object_new(belle_sip_provider_t);
277
	p->stack=s;
jehan's avatar
jehan committed
278
	if (lp) belle_sip_provider_add_listening_point(p,lp);
279 280 281 282
	return p;
}

int belle_sip_provider_add_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp){
jehan's avatar
jehan committed
283 284 285 286
	if (lp == NULL) {
		belle_sip_error("Cannot add NULL lp to provider [%p]",p);
		return -1;
	}
287
	belle_sip_listener_set_channel_listener(lp,BELLE_SIP_CHANNEL_LISTENER(p));
Simon Morlat's avatar
Simon Morlat committed
288
	p->lps=belle_sip_list_append(p->lps,belle_sip_object_ref(lp));
289 290
	return 0;
}
Simon Morlat's avatar
Simon Morlat committed
291

jehan's avatar
jehan committed
292 293 294 295 296
void belle_sip_provider_remove_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp) {
	p->lps=belle_sip_list_remove(p->lps,lp);
	belle_sip_object_unref(lp);
	return;
}
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

belle_sip_listening_point_t *belle_sip_provider_get_listening_point(belle_sip_provider_t *p, const char *transport){
	belle_sip_list_t *l;
	for(l=p->lps;l!=NULL;l=l->next){
		belle_sip_listening_point_t *lp=(belle_sip_listening_point_t*)l->data;
		if (strcasecmp(belle_sip_listening_point_get_transport(lp),transport)==0)
			return lp;
	}
	return NULL;
}

const belle_sip_list_t *belle_sip_provider_get_listening_points(belle_sip_provider_t *p){
	return p->lps;
}

jehan's avatar
jehan committed
312 313 314 315 316 317 318 319
void belle_sip_provider_add_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){
	p->internal_listeners=belle_sip_list_append(p->internal_listeners,l);
}

void belle_sip_provider_remove_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){
	p->internal_listeners=belle_sip_list_remove(p->internal_listeners,l);
}

320 321
void belle_sip_provider_add_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){
	p->listeners=belle_sip_list_append(p->listeners,l);
322 323
}

324 325
void belle_sip_provider_remove_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){
	p->listeners=belle_sip_list_remove(p->listeners,l);
326 327
}

328
belle_sip_header_call_id_t * belle_sip_provider_create_call_id(const belle_sip_provider_t *prov){
329
	belle_sip_header_call_id_t *cid=belle_sip_header_call_id_new();
330 331
	char tmp[11];
	belle_sip_header_call_id_set_call_id(cid,belle_sip_random_token(tmp,sizeof(tmp)));
332 333
	return cid;
}
334

jehan's avatar
jehan committed
335 336 337
belle_sip_dialog_t * belle_sip_provider_create_dialog(belle_sip_provider_t *prov, belle_sip_transaction_t *t) {
	return belle_sip_provider_create_dialog_internal(prov,t,TRUE);
}
338

jehan's avatar
jehan committed
339
belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provider_t *prov, belle_sip_transaction_t *t,unsigned int check_last_resp){
Simon Morlat's avatar
Simon Morlat committed
340
	belle_sip_dialog_t *dialog=NULL;
Simon Morlat's avatar
Simon Morlat committed
341
	
jehan's avatar
jehan committed
342
	if (check_last_resp && t->last_response){
Simon Morlat's avatar
Simon Morlat committed
343 344 345
		int code=belle_sip_response_get_status_code(t->last_response);
		if (code>=200 && code<300){
			belle_sip_fatal("You must not create dialog after sending the response that establish the dialog.");
jehan's avatar
jehan committed
346
			return NULL;
Simon Morlat's avatar
Simon Morlat committed
347 348
		}
	}
Simon Morlat's avatar
Simon Morlat committed
349
	dialog=belle_sip_dialog_new(t);
jehan's avatar
jehan committed
350
	if (dialog) {
Simon Morlat's avatar
Simon Morlat committed
351 352
		belle_sip_transaction_set_dialog(t,dialog);
		belle_sip_provider_add_dialog(prov,dialog);
jehan's avatar
jehan committed
353
	}
Simon Morlat's avatar
Simon Morlat committed
354 355 356
	return dialog;
}

Simon Morlat's avatar
Simon Morlat committed
357
/*finds an existing dialog for an outgoing or incoming request */
358
belle_sip_dialog_t *belle_sip_provider_find_dialog_from_msg(belle_sip_provider_t *prov, belle_sip_request_t *msg, int as_uas){
Simon Morlat's avatar
Simon Morlat committed
359 360
	belle_sip_list_t *elem;
	belle_sip_dialog_t *dialog;
jehan's avatar
jehan committed
361
	belle_sip_dialog_t *returned_dialog=NULL;
362 363
	belle_sip_header_call_id_t *call_id;
	belle_sip_header_from_t *from;
Simon Morlat's avatar
Simon Morlat committed
364 365 366 367 368
	belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t);
	const char *from_tag;
	const char *to_tag;
	const char *call_id_value;
	const char *local_tag,*remote_tag;
369 370 371 372 373 374 375 376
	
	if (to==NULL || (to_tag=belle_sip_header_to_get_tag(to))==NULL){
		/* a request without to tag cannot be part of a dialog */
		return NULL;
	}
	
	call_id=belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t);
	from=belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t);
Simon Morlat's avatar
Simon Morlat committed
377

378
	if (call_id==NULL || from==NULL) return NULL;
Simon Morlat's avatar
Simon Morlat committed
379 380 381

	call_id_value=belle_sip_header_call_id_get_call_id(call_id);
	from_tag=belle_sip_header_from_get_tag(from);
382
	
Simon Morlat's avatar
Simon Morlat committed
383 384 385 386 387
	local_tag=as_uas ? to_tag : from_tag;
	remote_tag=as_uas ? from_tag : to_tag;
	
	for (elem=prov->dialogs;elem!=NULL;elem=elem->next){
		dialog=(belle_sip_dialog_t*)elem->data;
jehan's avatar
jehan committed
388 389 390 391 392 393 394
		/*ignore dialog in state BELLE_SIP_DIALOG_NULL, is it really the correct things to do*/
		if (belle_sip_dialog_get_state(dialog) != BELLE_SIP_DIALOG_NULL && _belle_sip_dialog_match(dialog,call_id_value,local_tag,remote_tag)) {
			if (!returned_dialog)
				returned_dialog=dialog;
			else
				belle_sip_fatal("More than 1 dialog is matching, check your app");
		}
Simon Morlat's avatar
Simon Morlat committed
395
	}
jehan's avatar
jehan committed
396
	return returned_dialog;
Simon Morlat's avatar
Simon Morlat committed
397 398
}

Simon Morlat's avatar
Simon Morlat committed
399 400 401 402 403
void belle_sip_provider_add_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog){
	prov->dialogs=belle_sip_list_prepend(prov->dialogs,belle_sip_object_ref(dialog));
}

void belle_sip_provider_remove_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog){
404 405 406
	belle_sip_dialog_terminated_event_t ev;
	ev.source=prov;
	ev.dialog=dialog;
Simon Morlat's avatar
Simon Morlat committed
407
	prov->dialogs=belle_sip_list_remove(prov->dialogs,dialog);
jehan's avatar
jehan committed
408
	BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_dialog_terminated,&ev);
Simon Morlat's avatar
Simon Morlat committed
409 410 411
	belle_sip_object_unref(dialog);
}

Simon Morlat's avatar
Simon Morlat committed
412
belle_sip_client_transaction_t *belle_sip_provider_create_client_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){
413
	const char *method=belle_sip_request_get_method(req);
414
	belle_sip_client_transaction_t *t;
415
	belle_sip_client_transaction_t *inv_transaction;
416
	if (strcmp(method,"INVITE")==0)
417
		t=(belle_sip_client_transaction_t*)belle_sip_ict_new(prov,req);
418 419 420
	else if (strcmp(method,"ACK")==0){
		belle_sip_error("belle_sip_provider_create_client_transaction() cannot be used for ACK requests.");
		return NULL;
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
	} else {
		t=(belle_sip_client_transaction_t*)belle_sip_nict_new(prov,req);
		if (strcmp(method,"CANCEL")==0){
			/*force next hop*/
			inv_transaction=belle_sip_provider_find_matching_client_transaction_from_req(prov,req);
			if (inv_transaction && inv_transaction->next_hop) {
				/*found corresponding ict, taking next hop*/
				/*9.1 Client Behavior
				 * The destination address,
				   port, and transport for the CANCEL MUST be identical to those used to
				   send the original request.*/
				t->next_hop=belle_sip_hop_create(inv_transaction->next_hop->transport
												, inv_transaction->next_hop->host
												, inv_transaction->next_hop->port);
			} else {
				belle_sip_error (" No corresponding ict nor dest found for cancel request attached to transaction [%p]",t);
			}
		}
439
	}
440
	belle_sip_transaction_set_dialog((belle_sip_transaction_t*)t,belle_sip_provider_find_dialog_from_msg(prov,req,FALSE));
441
	return t;
442 443
}

Simon Morlat's avatar
Simon Morlat committed
444
belle_sip_server_transaction_t *belle_sip_provider_create_server_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){
Simon Morlat's avatar
Simon Morlat committed
445
	belle_sip_server_transaction_t* t;
446
	if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){
Simon Morlat's avatar
Simon Morlat committed
447
		t=(belle_sip_server_transaction_t*)belle_sip_ist_new(prov,req);
448 449 450 451
	}else if (strcmp(belle_sip_request_get_method(req),"ACK")==0){
		belle_sip_error("Creating a server transaction for an ACK is not a good idea, probably");
		return NULL;
	}else 
Simon Morlat's avatar
Simon Morlat committed
452
		t=(belle_sip_server_transaction_t*)belle_sip_nist_new(prov,req);
453
	belle_sip_transaction_set_dialog((belle_sip_transaction_t*)t,belle_sip_provider_find_dialog_from_msg(prov,req,TRUE));
Simon Morlat's avatar
Simon Morlat committed
454 455
	belle_sip_provider_add_server_transaction(prov,t);
	return t;
456 457 458 459 460 461
}

belle_sip_stack_t *belle_sip_provider_get_sip_stack(belle_sip_provider_t *p){
	return p->stack;
}

462 463 464 465
belle_sip_channel_t * belle_sip_provider_get_channel(belle_sip_provider_t *p, const char *name, int port, const char *transport){
	belle_sip_list_t *l;
	belle_sip_listening_point_t *candidate=NULL,*lp;
	belle_sip_channel_t *chan;
466 467 468

	if (transport==NULL) transport="UDP";
	
469 470 471 472 473 474 475 476 477 478
	for(l=p->lps;l!=NULL;l=l->next){
		lp=(belle_sip_listening_point_t*)l->data;
		if (strcasecmp(belle_sip_listening_point_get_transport(lp),transport)==0){
			chan=belle_sip_listening_point_get_channel(lp,name,port);
			if (chan) return chan;
			candidate=lp;
		}
	}
	if (candidate){
		chan=belle_sip_listening_point_create_channel(candidate,name,port);
479
		if (!chan) belle_sip_error("Could not create channel to %s:%s:%i",transport,name,port);
480 481 482
		return chan;
	}
	belle_sip_error("No listening point matching for transport %s",transport);
483
	return NULL;
Simon Morlat's avatar
wip  
Simon Morlat committed
484 485
}

Simon Morlat's avatar
wip  
Simon Morlat committed
486 487 488 489
void belle_sip_provider_release_channel(belle_sip_provider_t *p, belle_sip_channel_t *chan){
	belle_sip_listening_point_remove_channel(chan->lp,chan);
}

490 491 492
void belle_sip_provider_clean_channels(belle_sip_provider_t *p){
	belle_sip_list_t *l;
	belle_sip_listening_point_t *lp;
Simon Morlat's avatar
Simon Morlat committed
493
	
494 495 496 497 498 499
	for(l=p->lps;l!=NULL;l=l->next){
		lp=(belle_sip_listening_point_t*)l->data;
		belle_sip_listening_point_clean_channels(lp);
	}
}

500
void belle_sip_provider_send_request(belle_sip_provider_t *p, belle_sip_request_t *req){
501
	belle_sip_hop_t* hop;
502
	belle_sip_channel_t *chan;
503 504
	hop=belle_sip_stack_create_next_hop(p->stack,req);
	chan=belle_sip_provider_get_channel(p,hop->host, hop->port, hop->transport);
505 506 507
	if (chan) {
		belle_sip_channel_queue_message(chan,BELLE_SIP_MESSAGE(req));
	}
508
	belle_sip_hop_free(hop);
509 510
}

Simon Morlat's avatar
wip  
Simon Morlat committed
511
void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_response_t *resp){
512 513
	belle_sip_hop_t hop;
	belle_sip_channel_t *chan;
514 515 516
	belle_sip_header_to_t *to=(belle_sip_header_to_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"to");

	if (belle_sip_response_get_status_code(resp)!=100 && belle_sip_header_to_get_tag(to)==NULL){
517 518 519
		char token[BELLE_SIP_TAG_LENGTH];
		compute_hash_from_invariants((belle_sip_message_t*)resp,token,sizeof(token),"tag");
		belle_sip_header_to_set_tag(to,token);
520
	}
521 522 523
	belle_sip_response_get_return_hop(resp,&hop);
	chan=belle_sip_provider_get_channel(p,hop.host, hop.port, hop.transport);
	if (chan) belle_sip_channel_queue_message(chan,BELLE_SIP_MESSAGE(resp));
524
	belle_sip_hop_free(&hop);
525 526
}

527

528 529 530
/*private provider API*/

void belle_sip_provider_set_transaction_terminated(belle_sip_provider_t *p, belle_sip_transaction_t *t){
531 532 533 534 535 536
	belle_sip_transaction_terminated_event_t ev;
	
	BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t);
	ev.source=t->provider;
	ev.transaction=t;
	ev.is_server_transaction=BELLE_SIP_IS_INSTANCE_OF(t,belle_sip_server_transaction_t);
jehan's avatar
jehan committed
537
	BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(t,process_transaction_terminated,&ev);
538
	if (!ev.is_server_transaction){
Simon Morlat's avatar
Simon Morlat committed
539
		belle_sip_provider_remove_client_transaction(p,(belle_sip_client_transaction_t*)t);
Simon Morlat's avatar
Simon Morlat committed
540 541
	}else{
		belle_sip_provider_remove_server_transaction(p,(belle_sip_server_transaction_t*)t);
Simon Morlat's avatar
Simon Morlat committed
542 543 544 545 546
	}
}

void belle_sip_provider_add_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){
	prov->client_transactions=belle_sip_list_prepend(prov->client_transactions,belle_sip_object_ref(t));
Simon Morlat's avatar
wip  
Simon Morlat committed
547
}
548

549 550 551 552 553 554 555 556 557 558 559 560 561
struct client_transaction_matcher{
	const char *branchid;
	const char *method;
};

static int client_transaction_match(const void *p_tr, const void *p_matcher){
	belle_sip_client_transaction_t *tr=(belle_sip_client_transaction_t*)p_tr;
	struct client_transaction_matcher *matcher=(struct client_transaction_matcher*)p_matcher;
	const char *req_method=belle_sip_request_get_method(tr->base.request);
	if (strcmp(matcher->branchid,tr->base.branch_id)==0 && strcmp(matcher->method,req_method)==0) return 0;
	return -1;
}

Simon Morlat's avatar
Simon Morlat committed
562 563
belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction(belle_sip_provider_t *prov, 
                                                                                   belle_sip_response_t *resp){
564 565 566 567 568 569 570 571 572
	struct client_transaction_matcher matcher;
	belle_sip_header_via_t *via=(belle_sip_header_via_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"via");
	belle_sip_header_cseq_t *cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"cseq");
	belle_sip_client_transaction_t *ret=NULL;
	belle_sip_list_t *elem;
	if (via==NULL){
		belle_sip_warning("Response has no via.");
		return NULL;
	}
Simon Morlat's avatar
Simon Morlat committed
573
	if (cseq==NULL){
574 575 576 577 578 579 580 581 582 583 584
		belle_sip_warning("Response has no cseq.");
		return NULL;
	}
	matcher.branchid=belle_sip_header_via_get_branch(via);
	matcher.method=belle_sip_header_cseq_get_method(cseq);
	elem=belle_sip_list_find_custom(prov->client_transactions,client_transaction_match,&matcher);
	if (elem){
		ret=(belle_sip_client_transaction_t*)elem->data;
		belle_sip_message("Found transaction matching response.");
	}
	return ret;
Simon Morlat's avatar
Simon Morlat committed
585 586
}

587
void belle_sip_provider_remove_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){	
jehan's avatar
jehan committed
588 589 590 591 592 593 594 595
	belle_sip_list_t* elem=belle_sip_list_find(prov->client_transactions,t);
	if (elem) {
		prov->client_transactions=belle_sip_list_delete_link(prov->client_transactions,elem);
		belle_sip_object_unref(t);
	} else {
		belle_sip_error("trying to remove transaction [%p] not part of provider [%p]",t,prov);
	}

Simon Morlat's avatar
Simon Morlat committed
596
}
Simon Morlat's avatar
Simon Morlat committed
597 598 599 600 601

void belle_sip_provider_add_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){
	prov->server_transactions=belle_sip_list_prepend(prov->server_transactions,belle_sip_object_ref(t));
}

602
struct transaction_matcher{
Simon Morlat's avatar
Simon Morlat committed
603 604 605
	const char *branchid;
	const char *method;
	const char *sentby;
606
	int is_ack_or_cancel;
Simon Morlat's avatar
Simon Morlat committed
607 608
};

609 610 611 612 613
static int rfc3261_transaction_match(const void *p_tr, const void *p_matcher){
	belle_sip_transaction_t *tr=(belle_sip_transaction_t*)p_tr;
	struct transaction_matcher *matcher=(struct transaction_matcher*)p_matcher;
	const char *req_method=belle_sip_request_get_method(tr->request);
	if (strcmp(matcher->branchid,tr->branch_id)==0){
Simon Morlat's avatar
Simon Morlat committed
614
		if (strcmp(matcher->method,req_method)==0) return 0;
615
		if (matcher->is_ack_or_cancel && strcmp(req_method,"INVITE")==0) return 0;
Simon Morlat's avatar
Simon Morlat committed
616 617 618 619
	}
	return -1;
}

620 621
belle_sip_transaction_t * belle_sip_provider_find_matching_transaction(belle_sip_list_t *transactions, belle_sip_request_t *req){
	struct transaction_matcher matcher;
Simon Morlat's avatar
Simon Morlat committed
622
	belle_sip_header_via_t *via=(belle_sip_header_via_t*)belle_sip_message_get_header((belle_sip_message_t*)req,"via");
623
	belle_sip_transaction_t *ret=NULL;
624
	belle_sip_list_t *elem=NULL;
Simon Morlat's avatar
Simon Morlat committed
625 626 627 628 629 630
	if (via==NULL){
		belle_sip_warning("Request has no via.");
		return NULL;
	}
	matcher.branchid=belle_sip_header_via_get_branch(via);
	matcher.method=belle_sip_request_get_method(req);
631
	matcher.is_ack_or_cancel=(strcmp(matcher.method,"ACK")==0 || strcmp(matcher.method,"CANCEL")==0);
Simon Morlat's avatar
Simon Morlat committed
632 633
	if (strncmp(matcher.branchid,BELLE_SIP_BRANCH_MAGIC_COOKIE,strlen(BELLE_SIP_BRANCH_MAGIC_COOKIE))==0){
		/*compliant to RFC3261*/
634
		elem=belle_sip_list_find_custom(transactions,rfc3261_transaction_match,&matcher);
Simon Morlat's avatar
Simon Morlat committed
635 636 637 638 639
	}else{
		//FIXME
	}
	
	if (elem){
640
		ret=(belle_sip_transaction_t*)elem->data;
jehan's avatar
jehan committed
641
		belle_sip_message("Found transaction [%p] matching request.",ret);
Simon Morlat's avatar
Simon Morlat committed
642 643 644
	}
	return ret;
}
645 646 647 648 649 650 651 652
belle_sip_server_transaction_t * belle_sip_provider_find_matching_server_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req) {
	belle_sip_transaction_t *ret=belle_sip_provider_find_matching_transaction(prov->server_transactions,req);
	return ret?BELLE_SIP_SERVER_TRANSACTION(ret):NULL;
}
belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction_from_req(belle_sip_provider_t *prov, belle_sip_request_t *req) {
	belle_sip_transaction_t *ret=belle_sip_provider_find_matching_transaction(prov->client_transactions,req);
	return ret?BELLE_SIP_CLIENT_TRANSACTION(ret):NULL;
}
Simon Morlat's avatar
Simon Morlat committed
653 654 655 656 657

void belle_sip_provider_remove_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){	
	prov->server_transactions=belle_sip_list_remove(prov->server_transactions,t);
	belle_sip_object_unref(t);
}
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673


static void authorization_context_fill_from_auth(authorization_context_t* auth_context,belle_sip_header_www_authenticate_t* authenticate) {
	authorization_context_set_realm(auth_context,belle_sip_header_www_authenticate_get_realm(authenticate));
	if (auth_context->nonce && strcmp(belle_sip_header_www_authenticate_get_nonce(authenticate),auth_context->nonce)!=0) {
		/*new nonce, resetting nounce_count*/
		auth_context->nonce_count=0;
	}
	authorization_context_set_nonce(auth_context,belle_sip_header_www_authenticate_get_nonce(authenticate));
	authorization_context_set_qop(auth_context,belle_sip_header_www_authenticate_get_qop_first(authenticate));
	authorization_context_set_scheme(auth_context,belle_sip_header_www_authenticate_get_scheme(authenticate));
	authorization_context_set_opaque(auth_context,belle_sip_header_www_authenticate_get_opaque(authenticate));
	if (belle_sip_object_is_instance_of(BELLE_SIP_OBJECT(authenticate),BELLE_SIP_TYPE_ID(belle_sip_header_proxy_authenticate_t))) {
		auth_context->is_proxy=1;
	}
}
674

675 676 677 678 679 680 681 682 683 684 685 686
static belle_sip_list_t*  belle_sip_provider_get_auth_context_by_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id) {
	belle_sip_list_t* auth_context_lst=NULL;
	belle_sip_list_t* result=NULL;
	authorization_context_t* auth_context;
	for (auth_context_lst=p->auth_contexts;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
		auth_context=(authorization_context_t*)auth_context_lst->data;
		if (belle_sip_header_call_id_equals(auth_context->callid,call_id) ) {
			result=belle_sip_list_append(result,auth_context_lst->data);
		}
	}
	return result;
}
687

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
static void  belle_sip_provider_update_or_create_auth_context(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_header_www_authenticate_t* authenticate) {
	 belle_sip_list_t* auth_context_lst =  belle_sip_provider_get_auth_context_by_call_id(p,call_id);
	 authorization_context_t* auth_context;
	 for (;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
		 auth_context= (authorization_context_t*)auth_context_lst->data;
		 if (strcmp(auth_context->realm,belle_sip_header_www_authenticate_get_realm(authenticate))==0) {
			 authorization_context_fill_from_auth(auth_context,authenticate);
			 if (auth_context_lst) belle_sip_free(auth_context_lst);
			 return; /*only one realm is supposed to be found for now*/
		 }
	 }
	 /*no auth context found, creating one*/
	 auth_context=belle_sip_authorization_create(call_id);
	 authorization_context_fill_from_auth(auth_context,authenticate);
	 p->auth_contexts=belle_sip_list_append(p->auth_contexts,auth_context);
	 if (auth_context_lst) belle_sip_free(auth_context_lst);
	 return;
}
706

707
int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request,belle_sip_response_t *resp,belle_sip_list_t** auth_infos) {
708 709 710
	belle_sip_header_call_id_t* call_id;
	belle_sip_list_t* auth_context_lst;
	belle_sip_list_t* authenticate_lst;
jehan's avatar
jehan committed
711
	belle_sip_list_t* head;
712 713 714 715 716 717 718 719 720 721
	belle_sip_header_www_authenticate_t* authenticate;
	belle_sip_header_authorization_t* authorization;

	belle_sip_header_address_t* from;
	belle_sip_auth_event_t* auth_event;
	authorization_context_t* auth_context;
	belle_sip_uri_t* from_uri;
	const char* ha1;
	char computed_ha1[33];
	int result=0;
722
	const char* request_method;
723 724 725 726 727
	/*check params*/
	if (!p || !request) {
		belle_sip_error("belle_sip_provider_add_authorization bad parameters");
		return-1;
	}
728 729 730 731 732 733
	request_method=belle_sip_request_get_method(request);

	if (strcmp("CANCEL",request_method)==0 || strcmp("ACK",request_method)==0) {
		belle_sip_debug("no authorization header needed for method [%s]",request_method);
		return 0;
	}
734 735
	/*get authenticates value from response*/
	if (resp) {
736
		belle_sip_list_t *it;
737 738
		call_id = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(resp),belle_sip_header_call_id_t);
		/*searching for authentication headers*/
739
		authenticate_lst = belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(resp),BELLE_SIP_WWW_AUTHENTICATE));
740
		/*search for proxy authenticate*/
741
		authenticate_lst=belle_sip_list_concat(authenticate_lst,belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(resp),BELLE_SIP_PROXY_AUTHENTICATE)));
742
		/*update auth contexts with authenticate headers from response*/
743 744
		for (it=authenticate_lst;it!=NULL;it=it->next) {
			authenticate=BELLE_SIP_HEADER_WWW_AUTHENTICATE(it->data);
745 746
			belle_sip_provider_update_or_create_auth_context(p,call_id,authenticate);
		}
747
		belle_sip_list_free(authenticate_lst);
748 749 750 751 752 753
	}

	/*put authorization header if passwd found*/
	call_id = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_call_id_t);
	from = BELLE_SIP_HEADER_ADDRESS(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_FROM));
	from_uri = belle_sip_header_address_get_uri(from);
jehan's avatar
jehan committed
754
	if ((head=auth_context_lst = belle_sip_provider_get_auth_context_by_call_id(p,call_id))) {
755 756 757 758 759 760 761
		/*we assume there no existing auth headers*/
		for (;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) {
			/*clear auth info*/
			auth_context=(authorization_context_t*)auth_context_lst->data;
			auth_event = belle_sip_auth_event_create(auth_context->realm,belle_sip_uri_get_user(from_uri));
			/*put data*/
			/*call listener*/
jehan's avatar
jehan committed
762
			BELLE_SIP_PROVIDER_INVOKE_LISTENERS(p->listeners,process_auth_requested,auth_event);
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
			if (auth_event->passwd || auth_event->ha1) {
				if (!auth_event->userid) {
					/*if no userid, username = userid*/

					belle_sip_auth_event_set_userid(auth_event,(const char*)auth_event->username);
				}
				belle_sip_message("Auth info found for [%s] realm [%s]",auth_event->userid,auth_event->realm);
				if (auth_context->is_proxy) {
					authorization=BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_header_proxy_authorization_new());
				} else {
					authorization=belle_sip_header_authorization_new();
				}
				belle_sip_header_authorization_set_scheme(authorization,auth_context->scheme);
				belle_sip_header_authorization_set_realm(authorization,auth_context->realm);
				belle_sip_header_authorization_set_username(authorization,auth_event->userid);
				belle_sip_header_authorization_set_nonce(authorization,auth_context->nonce);
				belle_sip_header_authorization_set_qop(authorization,auth_context->qop);
				belle_sip_header_authorization_set_opaque(authorization,auth_context->opaque);
Simon Morlat's avatar
Simon Morlat committed
781
				belle_sip_header_authorization_set_uri(authorization,(belle_sip_uri_t*)belle_sip_request_get_uri(request));
jehan's avatar
jehan committed
782 783
				if (auth_context->qop)
					belle_sip_header_authorization_set_nonce_count(authorization,++auth_context->nonce_count);
784 785 786 787 788 789 790 791 792 793 794 795 796
				if (auth_event->ha1) {
					ha1=auth_event->ha1;
				} else {
					belle_sip_auth_helper_compute_ha1(auth_event->userid,auth_context->realm,auth_event->passwd, computed_ha1);
					ha1=computed_ha1;
				}
				if (belle_sip_auth_helper_fill_authorization(authorization
															,belle_sip_request_get_method(request)
															,ha1)) {
					belle_sip_object_unref(authorization);
				} else
					belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(authorization));
				result=1;
797
				belle_sip_auth_event_destroy(auth_event);
798 799
		} else {
			belle_sip_message("No auth info found for call id [%s]",belle_sip_header_call_id_get_call_id(call_id));
800 801 802 803 804 805
			if (auth_infos) {
				/*stored to give user information on realm/username which requires authentications*/
				*auth_infos=belle_sip_list_append(*auth_infos,auth_event);
			} else {
				belle_sip_auth_event_destroy(auth_event);
			}
806
		}
807

808
		}
jehan's avatar
jehan committed
809
		belle_sip_list_free(head);
810 811 812 813 814
	} else {
		/*nothing to do*/
	}
	return result;
}
jehan's avatar
jehan committed
815 816 817 818 819 820 821 822 823 824
void belle_sip_provider_set_recv_error(belle_sip_provider_t *prov, int recv_error) {
	belle_sip_list_t *lps;
	belle_sip_list_t *channels;
	for(lps=prov->lps;lps!=NULL;lps=lps->next){
		for(channels=((belle_sip_listening_point_t*)lps->data)->channels;channels!=NULL;channels=channels->next){
			((belle_sip_channel_t*)channels->data)->recv_error=recv_error;
			((belle_sip_source_t*)channels->data)->notify_required=(recv_error<=0);
		}
	}
}