sal_op_presence.c 13.6 KB
Newer Older
jehan's avatar
jehan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
linphone
Copyright (C) 2012  Belledonne Communications, Grenoble, France

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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "sal_impl.h"


22
void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence) {
jehan's avatar
jehan committed
23
	char *contact_info;
24
	char *content = NULL;
jehan's avatar
jehan committed
25 26 27 28 29
	size_t content_length;

	belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(notify,belle_sip_header_from_t);

	contact_info=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)));
30 31 32 33 34
	op->base.root->callbacks.convert_presence_to_xml_requested(op, presence, contact_info, &content);
	if (content == NULL) {
		ms_free(contact_info);
		return;
	}
jehan's avatar
jehan committed
35

jehan's avatar
jehan committed
36
	belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_TYPE);
jehan's avatar
jehan committed
37
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
38
								,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","pidf+xml")));
jehan's avatar
jehan committed
39
	belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_LENGTH);
jehan's avatar
jehan committed
40
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
41 42
								,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(content))));
	belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),content,content_length);
jehan's avatar
jehan committed
43
	ms_free(contact_info);
Ghislain MARY's avatar
Ghislain MARY committed
44
	ms_free(content);
jehan's avatar
jehan committed
45 46 47 48 49
}

static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
	ms_error("presence_process_io_error not implemented yet");
}
50

51 52 53 54 55 56
static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
	SalOp* op= (SalOp*)ctx;
	if (op->dialog) {
		sal_op_unref(op);
		op->dialog=NULL;
	}
jehan's avatar
jehan committed
57 58
}

Simon Morlat's avatar
Simon Morlat committed
59 60 61
static void presence_refresher_listener( const belle_sip_refresher_t* refresher, void* user_pointer, unsigned int status_code, const char* reason_phrase){
	SalOp* op = (SalOp*)user_pointer;
	switch(status_code){
62 63
		case 481: {

Simon Morlat's avatar
Simon Morlat committed
64 65
			ms_message("The server or remote ua lost the SUBSCRIBE dialog context. Let's restart a new one.");
			belle_sip_refresher_stop(op->refresher);
66 67 68 69 70
			if (op->dialog) { /*delete previous dialog if any*/
				belle_sip_dialog_set_application_data(op->dialog,NULL);
				belle_sip_object_unref(op->dialog);
				op->dialog=NULL;
			}
71 72 73 74 75 76 77 78 79 80

			if (sal_op_get_contact_address(op)) {
				/*contact is also probably not good*/
				SalAddress* contact=sal_address_clone(sal_op_get_contact_address(op));
				sal_address_set_port_int(contact,-1);
				sal_address_set_domain(contact,NULL);
				sal_op_set_contact_address(op,contact);
				sal_address_destroy(contact);
			}

Simon Morlat's avatar
Simon Morlat committed
81 82
			sal_subscribe_presence(op,NULL,NULL,-1);
		break;
83
		}
Simon Morlat's avatar
Simon Morlat committed
84 85 86 87
	}
}


jehan's avatar
jehan committed
88 89 90 91 92 93 94 95 96 97 98
static void presence_response_event(void *op_base, const belle_sip_response_event_t *event){
	SalOp* op = (SalOp*)op_base;
	belle_sip_dialog_state_t dialog_state;
	belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
	belle_sip_response_t* response=belle_sip_response_event_get_response(event);
	belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
	int code = belle_sip_response_get_status_code(response);
	char reason[256]={0};
	SalError error=SalErrorUnknown;
	SalReason sr=SalReasonUnknown;
	belle_sip_header_expires_t* expires;
99

jehan's avatar
jehan committed
100
	if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) {
101 102
		ms_error("subscription to [%s] rejected reason  [%s]",sal_op_get_to(op),reason[0]!=0?reason:sal_reason_to_string(sr));
		op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
jehan's avatar
jehan committed
103 104
		return;
	}
105
	set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event));
106 107 108 109
	if (!op->dialog) {
		ms_message("presence op [%p] receive out of dialog answer [%i]",op,code);
		return;
	}
jehan's avatar
jehan committed
110 111
	dialog_state=belle_sip_dialog_get_state(op->dialog);

112
	switch(dialog_state) {
jehan's avatar
jehan committed
113 114
		case BELLE_SIP_DIALOG_NULL:
		case BELLE_SIP_DIALOG_EARLY: {
115
			ms_error("presence op [%p] receive an unexpected answer [%i]",op,code);
jehan's avatar
jehan committed
116 117 118 119 120 121 122 123
			break;
		}
		case BELLE_SIP_DIALOG_CONFIRMED: {
			if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0) {
				expires=belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t);
				if(op->refresher) {
					belle_sip_refresher_stop(op->refresher);
					belle_sip_object_unref(op->refresher);
124
					op->refresher=NULL;
jehan's avatar
jehan committed
125 126 127
				}
				if (expires>0){
					op->refresher=belle_sip_client_transaction_create_refresher(client_transaction);
128
					belle_sip_refresher_enable_nat_helper(op->refresher,op->base.root->nat_helper_enabled);
Simon Morlat's avatar
Simon Morlat committed
129
					belle_sip_refresher_set_listener(op->refresher,presence_refresher_listener,op);
jehan's avatar
jehan committed
130 131 132 133 134 135 136 137 138 139 140 141
				}
			}
			break;
		}
		case BELLE_SIP_DIALOG_TERMINATED:
			if (op->refresher) {
				belle_sip_refresher_stop(op->refresher);
				belle_sip_object_unref(op->refresher);
				op->refresher=NULL;
			}
		break;
		default: {
142
			ms_error("presence op [%p] receive answer [%i] not implemented",op,code);
jehan's avatar
jehan committed
143 144 145 146 147 148 149 150 151
		}
		/* no break */
	}


}
static void presence_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
	ms_error("presence_process_timeout not implemented yet");
}
152

jehan's avatar
jehan committed
153
static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
jehan's avatar
jehan committed
154
	ms_message("presence_process_transaction_terminated not implemented yet");
jehan's avatar
jehan committed
155
}
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_request_t *req) {
	belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_type_t);
	belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_length_t);
	const char *body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
	SalPresenceModel *result = NULL;

	if ((content_type == NULL) || (content_length == NULL))
		return NULL;
	if (belle_sip_header_content_length_get_content_length(content_length) == 0)
		return NULL;

	op->base.root->callbacks.parse_presence_requested(op,
							  belle_sip_header_content_type_get_type(content_type),
							  belle_sip_header_content_type_get_subtype(content_type),
							  body,
							  &result);

	return result;
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
static void handle_notify(SalOp *op, belle_sip_request_t *req){
	belle_sip_response_t* resp;
	belle_sip_server_transaction_t* server_transaction=op->pending_server_trans;
	belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t);
	SalSubscribeStatus sub_state;
	
	if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) {
		SalPresenceModel *presence_model = NULL;
		const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
		if (body==NULL){
			ms_error("No body in NOTIFY received from [%s]",sal_op_get_from(op));
			resp = sal_op_create_response_from_request(op, req, 415);
			belle_sip_server_transaction_send_response(server_transaction,resp);
			return;
		}
		presence_model = process_presence_notification(op, req);
		if (presence_model != NULL) {
			/* Presence notification body parsed successfully. */
			if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) {
				sub_state=SalSubscribeTerminated;
				ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op));
			} else {
				sub_state=SalSubscribeActive;
			}
			op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL);
			resp = sal_op_create_response_from_request(op, req, 200);
		} else {
			/* Formatting error in presence notification body. */
			ms_error("Wrongly formatted presence notification received");
			resp = sal_op_create_response_from_request(op, req, 400);
		}
		belle_sip_server_transaction_send_response(server_transaction,resp);
	}
}

jehan's avatar
jehan committed
212 213 214 215 216
static void presence_process_request_event(void *op_base, const belle_sip_request_event_t *event) {
	SalOp* op = (SalOp*)op_base;
	belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event));
	belle_sip_request_t* req = belle_sip_request_event_get_request(event);
	belle_sip_dialog_state_t dialog_state;
Sylvain Berfini's avatar
Sylvain Berfini committed
217
	belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);
jehan's avatar
jehan committed
218
	belle_sip_response_t* resp;
219 220
	const char *method=belle_sip_request_get_method(req);
	
jehan's avatar
jehan committed
221 222 223 224
	belle_sip_object_ref(server_transaction);
	if (op->pending_server_trans)  belle_sip_object_unref(op->pending_server_trans);
	op->pending_server_trans=server_transaction;

jehan's avatar
jehan committed
225

jehan's avatar
jehan committed
226
	if (!op->dialog) {
227 228 229 230 231 232 233 234 235 236
		if (strcmp(method,"SUBSCRIBE")==0){
			op->dialog=belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction));
			belle_sip_dialog_set_application_data(op->dialog,op);
			sal_op_ref(op);
			ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
		}else{ /* this is a NOTIFY */
			ms_message("Receiving out of dialog notify");
			handle_notify(op,req);
			return;
		}
jehan's avatar
jehan committed
237 238 239
	}
	dialog_state=belle_sip_dialog_get_state(op->dialog);
	switch(dialog_state) {
240 241 242 243 244 245 246
		case BELLE_SIP_DIALOG_NULL: {
			op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
			break;
		}
		case BELLE_SIP_DIALOG_EARLY:
			ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",method,op->dialog);
			break;
jehan's avatar
jehan committed
247

248 249 250 251 252 253 254 255 256 257 258
		case BELLE_SIP_DIALOG_CONFIRMED:
			if (strcmp("NOTIFY",method)==0) {
				handle_notify(op,req);
			} else if (strcmp("SUBSCRIBE",method)==0) {
				/*either a refresh or an unsubscribe*/
				if (expires && belle_sip_header_expires_get_expires(expires)>0) {
					op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
				} else if(expires) {
					ms_message("Unsubscribe received from [%s]",sal_op_get_from(op));
					resp=sal_op_create_response_from_request(op,req,200);
					belle_sip_server_transaction_send_response(server_transaction,resp);
259
				}
jehan's avatar
jehan committed
260
			}
261 262 263 264
			break;
		default: 
			ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
			break;
jehan's avatar
jehan committed
265 266
	}
}
267

jehan's avatar
jehan committed
268 269 270 271 272 273 274
void sal_op_presence_fill_cbs(SalOp*op) {
	op->callbacks.process_io_error=presence_process_io_error;
	op->callbacks.process_response_event=presence_response_event;
	op->callbacks.process_timeout=presence_process_timeout;
	op->callbacks.process_transaction_terminated=presence_process_transaction_terminated;
	op->callbacks.process_request_event=presence_process_request_event;
	op->callbacks.process_dialog_terminated=presence_process_dialog_terminated;
275
	op->type=SalOpPresence;
jehan's avatar
jehan committed
276 277
}

jehan's avatar
jehan committed
278

jehan's avatar
jehan committed
279
/*presence Subscribe/notify*/
Simon Morlat's avatar
Simon Morlat committed
280
int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expires){
jehan's avatar
jehan committed
281 282 283 284 285 286 287 288
	belle_sip_request_t *req=NULL;
	if (from)
		sal_op_set_from(op,from);
	if (to)
		sal_op_set_to(op,to);

	sal_op_presence_fill_cbs(op);

Simon Morlat's avatar
Simon Morlat committed
289 290 291
	if (expires==-1){
		if (op->refresher){
			expires=belle_sip_refresher_get_expires(op->refresher);
292 293
			belle_sip_object_unref(op->refresher);
			op->refresher=NULL;
Simon Morlat's avatar
Simon Morlat committed
294 295
		}else{
			ms_error("sal_subscribe_presence(): cannot guess expires from previous refresher.");
296
			return -1;
Simon Morlat's avatar
Simon Morlat committed
297 298
		}
	}
299 300
	belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.from_address),"tag");
	belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.to_address),"tag");
jehan's avatar
jehan committed
301
	req=sal_op_build_request(op,"SUBSCRIBE");
302
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence"));
Simon Morlat's avatar
Simon Morlat committed
303
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires)));
jehan's avatar
jehan committed
304

jehan's avatar
jehan committed
305
	return sal_op_send_request(op,req);
jehan's avatar
jehan committed
306
}
307

308 309

static belle_sip_request_t *create_presence_notify(SalOp *op){
jehan's avatar
jehan committed
310
	belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY");
311 312
	if (!notify) return NULL;

313 314 315 316
	belle_sip_message_add_header((belle_sip_message_t*)notify,belle_sip_header_create("Event","presence"));
	return notify;
}

317
static int sal_op_check_dialog_state(SalOp *op) {
318 319
	belle_sip_dialog_state_t state=op->dialog?belle_sip_dialog_get_state(op->dialog): BELLE_SIP_DIALOG_NULL;
	if (state != BELLE_SIP_DIALOG_CONFIRMED) {
320 321 322 323 324 325 326 327 328
		ms_warning("Cannot notify presence for op [%p] because dialog in state [%s]",op, belle_sip_dialog_state_to_string(state));
		return -1;
	} else
		return 0;

}
int sal_notify_presence(SalOp *op, SalPresenceModel *presence){
	belle_sip_request_t* notify=NULL;
	if (sal_op_check_dialog_state(op)) {
329 330 331
		return -1;
	}
	notify=create_presence_notify(op);
332 333
	if (!notify) return-1;

334
	sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),presence); /*FIXME, what about expires ??*/
jehan's avatar
jehan committed
335
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
336
			,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)));
jehan's avatar
jehan committed
337 338
	return sal_op_send_request(op,notify);
}
339

340
int sal_notify_presence_close(SalOp *op){
341 342 343 344 345 346 347
	belle_sip_request_t* notify=NULL;
	if (sal_op_check_dialog_state(op)) {
		return -1;
	}
	notify=create_presence_notify(op);
	if (!notify) return-1;

348
	sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),NULL); /*FIXME, what about expires ??*/
jehan's avatar
jehan committed
349
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
350
		,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1)));
jehan's avatar
jehan committed
351 352
	return sal_op_send_request(op,notify);
}
jehan's avatar
jehan committed
353 354 355