sal_op_presence.c 11 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 44 45 46 47 48
	ms_free(contact_info);
}

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");
}
49 50 51 52 53 54
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
55 56 57 58 59 60 61 62 63 64 65 66 67
}

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;
68

jehan's avatar
jehan committed
69 70 71 72 73
	if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) {
		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, SalPresenceOffline,NULL);
		return;
	}
74
	set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event));
75 76 77 78
	if (!op->dialog) {
		ms_message("presence op [%p] receive out of dialog answer [%i]",op,code);
		return;
	}
jehan's avatar
jehan committed
79 80
	dialog_state=belle_sip_dialog_get_state(op->dialog);

81
	switch(dialog_state) {
jehan's avatar
jehan committed
82 83
		case BELLE_SIP_DIALOG_NULL:
		case BELLE_SIP_DIALOG_EARLY: {
84
			ms_error("presence op [%p] receive an unexpected answer [%i]",op,code);
jehan's avatar
jehan committed
85 86 87 88 89 90 91 92
			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);
93
					op->refresher=NULL;
jehan's avatar
jehan committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
				}
				if (expires>0){
					op->refresher=belle_sip_client_transaction_create_refresher(client_transaction);
				}
			}
			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: {
109
			ms_error("presence op [%p] receive answer [%i] not implemented",op,code);
jehan's avatar
jehan committed
110 111 112 113 114 115 116 117 118
		}
		/* 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");
}
119

jehan's avatar
jehan committed
120
static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
jehan's avatar
jehan committed
121
	ms_message("presence_process_transaction_terminated not implemented yet");
jehan's avatar
jehan committed
122
}
123

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
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;
}

jehan's avatar
jehan committed
144 145 146 147 148 149
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;
	belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t);
Sylvain Berfini's avatar
Sylvain Berfini committed
150
	belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);
jehan's avatar
jehan committed
151
	const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
152
	SalPresenceModel *presence_model = NULL;
153
	SalSubscribeStatus sub_state;
jehan's avatar
jehan committed
154 155 156 157 158
	belle_sip_response_t* resp;
	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
159

jehan's avatar
jehan committed
160 161 162
	if (!op->dialog) {
		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);
jehan's avatar
jehan committed
163
		sal_op_ref(op);
jehan's avatar
jehan committed
164 165 166 167 168 169
		ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
	}
	dialog_state=belle_sip_dialog_get_state(op->dialog);
	switch(dialog_state) {

	case BELLE_SIP_DIALOG_NULL: {
170
		op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
jehan's avatar
jehan committed
171 172 173 174 175 176 177 178 179 180 181 182
		break;
	}
	case BELLE_SIP_DIALOG_EARLY:
		ms_error("unexpected method [%s] for dialog [%p] in state BELLE_SIP_DIALOG_EARLY ",belle_sip_request_get_method(req),op->dialog);
		break;

	case BELLE_SIP_DIALOG_CONFIRMED:
		if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) {
			if (body==NULL){
				ms_error("No body in NOTIFY received from [%s]",sal_op_get_from(op));
				return;
			}
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
			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);
jehan's avatar
jehan committed
198 199 200 201 202
			}
			belle_sip_server_transaction_send_response(server_transaction,resp);
		} else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0) {
			/*either a refresh of an unsubscribe*/
			if (expires && belle_sip_header_expires_get_expires(expires)>0) {
203
				op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
jehan's avatar
jehan committed
204 205
			} else if(expires) {
				ms_message("Unsubscribe received from [%s]",sal_op_get_from(op));
206
				resp=sal_op_create_response_from_request(op,req,200);
jehan's avatar
jehan committed
207 208 209 210 211 212 213 214 215 216
				belle_sip_server_transaction_send_response(server_transaction,resp);
			}
		}
		break;
	default: {
		ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state));
	}
	/* no break */
	}
}
217

jehan's avatar
jehan committed
218 219 220 221 222 223 224
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;
225
	op->type=SalOpPresence;
jehan's avatar
jehan committed
226 227
}

jehan's avatar
jehan committed
228

jehan's avatar
jehan committed
229 230 231 232 233 234 235 236 237 238 239 240
/*presence Subscribe/notify*/
int sal_subscribe_presence(SalOp *op, const char *from, const char *to){
	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);

	/*???sal_exosip_fix_route(op); make sure to ha ;lr*/
	req=sal_op_build_request(op,"SUBSCRIBE");
241
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence"));
jehan's avatar
jehan committed
242 243
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(600)));

jehan's avatar
jehan committed
244
	return sal_op_send_request(op,req);
jehan's avatar
jehan committed
245
}
246

247 248

static belle_sip_request_t *create_presence_notify(SalOp *op){
jehan's avatar
jehan committed
249
	belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY");
250 251 252 253
	belle_sip_message_add_header((belle_sip_message_t*)notify,belle_sip_header_create("Event","presence"));
	return notify;
}

254
int sal_notify_presence(SalOp *op, SalPresenceModel *presence){
255
	belle_sip_request_t* notify=create_presence_notify(op);
256
	sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),presence); /*FIXME, what about expires ??*/
jehan's avatar
jehan committed
257
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
258
			,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)));
jehan's avatar
jehan committed
259 260
	return sal_op_send_request(op,notify);
}
261

262
int sal_notify_presence_close(SalOp *op){
263
	belle_sip_request_t* notify=create_presence_notify(op);
264
	sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),NULL); /*FIXME, what about expires ??*/
jehan's avatar
jehan committed
265
	belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
266
		,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1)));
jehan's avatar
jehan committed
267 268
	return sal_op_send_request(op,notify);
}
jehan's avatar
jehan committed
269 270 271