event.c 16.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU 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.
18 19
*/

Ghislain MARY's avatar
Ghislain MARY committed
20
#include "linphone/event.h"
21
#include "private.h"
22
#include "linphone/lpconfig.h"
23

24 25 26 27 28 29 30 31 32 33 34
const char * linphone_subscription_dir_to_string(LinphoneSubscriptionDir dir){
	switch(dir){
		case LinphoneSubscriptionIncoming:
			return "LinphoneSubscriptionIncoming";
		case LinphoneSubscriptionOutgoing:
			return "LinphoneSubscriptionOutgoing";
		case LinphoneSubscriptionInvalidDir:
			return "LinphoneSubscriptionInvalidDir";
	}
	return "INVALID";
}
35 36 37 38 39 40 41 42 43 44 45

LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss){
	switch(ss){
		case SalSubscribeNone: return LinphoneSubscriptionNone;
		case SalSubscribePending: return LinphoneSubscriptionPending;
		case SalSubscribeTerminated: return LinphoneSubscriptionTerminated;
		case SalSubscribeActive: return LinphoneSubscriptionActive;
	}
	return LinphoneSubscriptionNone;
}

46 47 48 49
const char *linphone_subscription_state_to_string(LinphoneSubscriptionState state){
	switch(state){
		case LinphoneSubscriptionNone: return "LinphoneSubscriptionNone";
		case LinphoneSubscriptionIncomingReceived: return "LinphoneSubscriptionIncomingReceived";
50
		case LinphoneSubscriptionOutgoingProgress: return "LinphoneSubscriptionOutgoingProgress";
51 52 53 54
		case LinphoneSubscriptionPending: return "LinphoneSubscriptionPending";
		case LinphoneSubscriptionActive: return "LinphoneSubscriptionActive";
		case LinphoneSubscriptionTerminated: return "LinphoneSubscriptionTerminated";
		case LinphoneSubscriptionError: return "LinphoneSubscriptionError";
55
		case LinphoneSubscriptionExpiring: return "LinphoneSubscriptionExpiring";
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
	}
	return NULL;
}

LINPHONE_PUBLIC const char *linphone_publish_state_to_string(LinphonePublishState state){
	switch(state){
		case LinphonePublishNone: return "LinphonePublishNone";
		case LinphonePublishProgress: return "LinphonePublishProgress";
		case LinphonePublishOk: return "LinphonePublishOk";
		case LinphonePublishError: return "LinphonePublishError";
		case LinphonePublishCleared: return "LinphonePublishCleared";
		case LinphonePublishExpiring: return "LinphonePublishExpiring";
	}
	return NULL;
}

72 73
static void linphone_event_release(LinphoneEvent *lev){
	if (lev->op) {
74 75
		/*this will stop the refresher*/
		sal_op_stop_refreshing(lev->op);
76 77 78 79
	}
	linphone_event_unref(lev);
}

80
static LinphoneEvent * linphone_event_new_base(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, SalOp *op){
81
	LinphoneEvent *lev=belle_sip_object_new(LinphoneEvent);
82 83
	lev->lc=lc;
	lev->dir=dir;
84
	lev->op=op;
Simon Morlat's avatar
Simon Morlat committed
85
	lev->name=ms_strdup(name);
86 87 88 89
	sal_op_set_user_pointer(lev->op,lev);
	return lev;
}

90
LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, int expires){
91
	LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, sal_op_new(lc->sal));
92
	lev->expires=expires;
93 94 95
	return lev;
}

96
static LinphoneEvent *linphone_event_new_with_op_base(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name, bool_t is_out_of_dialog){
97
	LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, op);
98
	lev->is_out_of_dialog_op=is_out_of_dialog;
99 100
	return lev;
}
Simon Morlat's avatar
Simon Morlat committed
101

102 103 104
LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) {
	return linphone_event_new_with_op_base(lc,op,dir,name,FALSE);
}
Simon Morlat's avatar
Simon Morlat committed
105

106 107 108
LinphoneEvent *linphone_event_new_with_out_of_dialog_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name) {
	return linphone_event_new_with_op_base(lc,op,dir,name,TRUE);
}
Simon Morlat's avatar
Simon Morlat committed
109

110 111 112 113 114 115 116 117
void linphone_event_set_internal(LinphoneEvent *lev, bool_t internal) {
	lev->internal = internal;
}

bool_t linphone_event_is_internal(LinphoneEvent *lev) {
	return lev->internal;
}

118
void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state){
119 120 121
	if (lev->subscription_state!=state){
		ms_message("LinphoneEvent [%p] moving to subscription state %s",lev,linphone_subscription_state_to_string(state));
		lev->subscription_state=state;
122
		linphone_core_notify_subscription_state_changed(lev->lc,lev,state);
123
		if (state==LinphoneSubscriptionTerminated || state == LinphoneSubscriptionError){
124
			linphone_event_release(lev);
125
		}
126 127 128
	}
}

129 130 131 132
void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state){
	if (lev->publish_state!=state){
		ms_message("LinphoneEvent [%p] moving to publish state %s",lev,linphone_publish_state_to_string(state));
		lev->publish_state=state;
133
		linphone_core_notify_publish_state_changed(lev->lc,lev,state);
Simon Morlat's avatar
Simon Morlat committed
134 135
		switch(state){
			case LinphonePublishCleared:
136
				linphone_event_release(lev);
Simon Morlat's avatar
Simon Morlat committed
137 138
				break;
			case LinphonePublishOk:
139
				if (lev->oneshot) linphone_event_release(lev);
140
				break;
Simon Morlat's avatar
Simon Morlat committed
141
			case LinphonePublishError:
142
				linphone_event_release(lev);
Simon Morlat's avatar
Simon Morlat committed
143 144 145 146 147 148
				break;
			case LinphonePublishNone:
			case LinphonePublishProgress:
			case LinphonePublishExpiring:
				/*nothing special to do*/
				break;
149
		}
150

151 152 153 154 155 156 157
	}
}

LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){
	return lev->publish_state;
}

158
const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev){
159 160 161
	if (!lev->ei) ((LinphoneEvent*)lev)->ei = linphone_error_info_new();
	linphone_error_info_from_sal_op(lev->ei, lev->op);
	return lev->ei;
162 163 164
}

LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){
165
	return linphone_error_info_get_reason(linphone_event_get_error_info(lev));
166 167
}

168 169
LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){
	LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event, expires);
170
	linphone_configure_op(lc,lev->op,resource,NULL,TRUE);
171
	sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_subscribe",1));
172 173 174
	return lev;
}

175 176 177 178 179 180 181 182 183
LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){
	LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionIncoming, event, -1);
	linphone_configure_op(lc,lev->op,resource,NULL,TRUE);
	lev->subscription_state = LinphoneSubscriptionIncomingReceived;
	sal_op_set_event(lev->op, event);
	lev->is_out_of_dialog_op = TRUE;
	return lev;
}

184 185 186 187 188
LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){
	LinphoneEvent *lev=linphone_core_create_subscribe(lc,resource,event,expires);
	linphone_event_send_subscribe(lev,body);
	return lev;
}
189

190
LinphoneStatus linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *body){
191
	SalBodyHandler *body_handler;
192
	int err;
193

194 195 196 197
	if (lev->dir!=LinphoneSubscriptionOutgoing){
		ms_error("linphone_event_send_subscribe(): cannot send or update something that is not an outgoing subscription.");
		return -1;
	}
198 199 200
	switch (lev->subscription_state){
		case LinphoneSubscriptionIncomingReceived:
		case LinphoneSubscriptionTerminated:
201
		case LinphoneSubscriptionOutgoingProgress:
202
			ms_error("linphone_event_send_subscribe(): cannot update subscription while in state [%s]", linphone_subscription_state_to_string(lev->subscription_state));
203 204
			return -1;
		break;
205
		case LinphoneSubscriptionNone:
206 207 208 209 210 211
		case LinphoneSubscriptionActive:
		case LinphoneSubscriptionExpiring:
		case LinphoneSubscriptionError:
		case LinphoneSubscriptionPending:
			/*those states are ok*/
		break;
212
	}
213

214 215
	if (lev->send_custom_headers){
		sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers);
216
		sal_custom_header_free(lev->send_custom_headers);
217 218
		lev->send_custom_headers=NULL;
	}else sal_op_set_sent_custom_header(lev->op,NULL);
219

220 221
	body_handler = sal_body_handler_from_content(body);
	err=sal_subscribe(lev->op,NULL,NULL,lev->name,lev->expires,body_handler);
222 223
	if (err==0){
		if (lev->subscription_state==LinphoneSubscriptionNone)
224
			linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress);
225
	}
226 227 228
	return err;
}

229
LinphoneStatus linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){
230
	return linphone_event_send_subscribe(lev,body);
231 232
}

233
LinphoneStatus linphone_event_refresh_subscribe(LinphoneEvent *lev) {
234
	return sal_op_refresh(lev->op);
235 236
}

237
LinphoneStatus linphone_event_accept_subscription(LinphoneEvent *lev){
238
	int err;
239
	if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
240 241 242 243 244 245 246 247 248 249
		ms_error("linphone_event_accept_subscription(): cannot accept subscription if subscription wasn't just received.");
		return -1;
	}
	err=sal_subscribe_accept(lev->op);
	if (err==0){
		linphone_event_set_state(lev,LinphoneSubscriptionActive);
	}
	return err;
}

250
LinphoneStatus linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){
251
	int err;
252
	if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
253 254 255
		ms_error("linphone_event_deny_subscription(): cannot deny subscription if subscription wasn't just received.");
		return -1;
	}
256 257 258
	err=sal_subscribe_decline(lev->op,linphone_reason_to_sal(reason));
	linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
	return err;
259 260
}

261
LinphoneStatus linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){
262
	SalBodyHandler *body_handler;
263
	if (lev->subscription_state!=LinphoneSubscriptionActive && lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
264 265 266 267 268 269 270
		ms_error("linphone_event_notify(): cannot notify if subscription is not active.");
		return -1;
	}
	if (lev->dir!=LinphoneSubscriptionIncoming){
		ms_error("linphone_event_notify(): cannot notify if not an incoming subscription.");
		return -1;
	}
271 272
	body_handler = sal_body_handler_from_content(body);
	return sal_notify(lev->op, body_handler);
273 274
}

275 276
LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){
	LinphoneEvent *lev=linphone_event_new(lc,LinphoneSubscriptionInvalidDir, event,expires);
277
	linphone_configure_op(lc,lev->op,resource,NULL,lp_config_get_int(lc->config,"sip","publish_msg_with_contact",0));
278
	sal_op_set_manual_refresher_mode(lev->op,!lp_config_get_int(lc->config,"sip","refresh_generic_publish",1));
279 280 281
	return lev;
}

282 283 284 285 286 287
LinphoneEvent *linphone_core_create_one_shot_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){
	LinphoneEvent *lev = linphone_core_create_publish(lc, resource, event, -1);
	lev->oneshot = TRUE;
	return lev;
}

288
static int _linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body, bool_t notify_err){
289
	SalBodyHandler *body_handler;
290
	int err;
291

292
	if (lev->dir!=LinphoneSubscriptionInvalidDir){
293 294 295
		ms_error("linphone_event_update_publish(): this is not a PUBLISH event.");
		return -1;
	}
296 297
	if (lev->send_custom_headers){
		sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers);
298
		sal_custom_header_free(lev->send_custom_headers);
299 300
		lev->send_custom_headers=NULL;
	}else sal_op_set_sent_custom_header(lev->op,NULL);
301 302
	body_handler = sal_body_handler_from_content(body);
	err=sal_publish(lev->op,NULL,NULL,lev->name,lev->expires,body_handler);
303 304
	if (err==0){
		linphone_event_set_publish_state(lev,LinphonePublishProgress);
305
	}else if (notify_err){
306 307 308
		linphone_event_set_publish_state(lev,LinphonePublishError);
	}
	return err;
309 310
}

311 312 313 314 315 316 317 318 319 320 321 322
LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){
	int err;
	LinphoneEvent *lev=linphone_core_create_publish(lc,resource,event,expires);
	err=_linphone_event_send_publish(lev,body,FALSE);
	if (err==-1){
		linphone_event_unref(lev);
		lev=NULL;
	}
	return lev;
}


323
LinphoneStatus linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body){
324 325 326
	return _linphone_event_send_publish(lev,body,TRUE);
}

327
LinphoneStatus linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* body ) {
328 329 330
	return linphone_event_send_publish(lev,body);
}

331
LinphoneStatus linphone_event_refresh_publish(LinphoneEvent *lev) {
332 333 334 335 336 337 338 339 340
	return sal_op_refresh(lev->op);
}
void linphone_event_pause_publish(LinphoneEvent *lev) {
	if (lev->op) sal_op_stop_refreshing(lev->op);
}
void linphone_event_unpublish(LinphoneEvent *lev) {
	lev->terminating = TRUE; /* needed to get clear event*/
	if (lev->op) sal_op_unpublish(lev->op);
}
341
void linphone_event_set_user_data(LinphoneEvent *ev, void *up){
342 343 344
	ev->userdata=up;
}

345
void *linphone_event_get_user_data(const LinphoneEvent *ev){
346 347 348
	return ev->userdata;
}

349 350 351 352 353 354 355 356 357 358
void linphone_event_add_custom_header(LinphoneEvent *ev, const char *name, const char *value){
	ev->send_custom_headers=sal_custom_header_append(ev->send_custom_headers, name, value);
}

const char* linphone_event_get_custom_header(LinphoneEvent* ev, const char* name){
	const SalCustomHeader *ch=sal_op_get_recv_custom_header(ev->op);
	return sal_custom_header_find(ch,name);
}


359
void linphone_event_terminate(LinphoneEvent *lev){
360 361
	// if event was already terminated (including on error), we should not terminate it again
	// otherwise it will be unreffed twice.
362
	if (lev->publish_state == LinphonePublishError || lev->publish_state == LinphonePublishCleared) {
363 364 365 366 367 368
		return;
	}
	if (lev->subscription_state == LinphoneSubscriptionError || lev->subscription_state == LinphoneSubscriptionTerminated) {
		return;
	}

369
	lev->terminating=TRUE;
370

371 372 373 374 375
	if (lev->dir==LinphoneSubscriptionIncoming){
		sal_notify_close(lev->op);
	}else if (lev->dir==LinphoneSubscriptionOutgoing){
		sal_unsubscribe(lev->op);
	}
376

377
	if (lev->publish_state!=LinphonePublishNone){
378
		if (lev->publish_state==LinphonePublishOk && lev->expires!=-1){
379 380
			sal_op_unpublish(lev->op);
		}
381
		linphone_event_set_publish_state(lev,LinphonePublishCleared);
382 383
		return;
	}
384

385
	if (lev->subscription_state!=LinphoneSubscriptionNone){
386
		linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
387
		return;
388 389 390
	}
}

391 392

LinphoneEvent *linphone_event_ref(LinphoneEvent *lev){
393
	belle_sip_object_ref(lev);
394 395 396 397
	return lev;
}

static void linphone_event_destroy(LinphoneEvent *lev){
398
	if (lev->ei) linphone_error_info_unref(lev->ei);
Ghislain MARY's avatar
Ghislain MARY committed
399 400
	if (lev->op) sal_op_release(lev->op);
	if (lev->send_custom_headers) sal_custom_header_free(lev->send_custom_headers);
401
	ms_free(lev->name);
402 403
}

404
void linphone_event_unref(LinphoneEvent *lev){
405
	belle_sip_object_unref(lev);
406 407
}

Simon Morlat's avatar
Simon Morlat committed
408
LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev){
409 410
	return lev->dir;
}
411 412

LinphoneSubscriptionState linphone_event_get_subscription_state(const LinphoneEvent *lev){
413
	return lev->subscription_state;
414 415 416 417 418 419
}

const char *linphone_event_get_name(const LinphoneEvent *lev){
	return lev->name;
}

420
const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){
421
	if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){
422 423 424 425
		return (LinphoneAddress*)sal_op_get_to_address(lev->op);
	}else{
		return (LinphoneAddress*)sal_op_get_from_address(lev->op);
	}
426 427 428
}

const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){
429
	if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){
430 431 432 433
		return (LinphoneAddress*)sal_op_get_from_address(lev->op);
	}else{
		return (LinphoneAddress*)sal_op_get_to_address(lev->op);
	}
434 435
}

436 437 438 439
LinphoneCore *linphone_event_get_core(const LinphoneEvent *lev){
	return lev->lc;
}

440 441 442
static belle_sip_error_code _linphone_event_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) {
	LinphoneEvent *ev = (LinphoneEvent*)obj;
	belle_sip_error_code err = BELLE_SIP_OK;
443 444

	err = belle_sip_snprintf(buff, buff_size, offset, "%s of %s", ev->dir == LinphoneSubscriptionIncoming ?
445
		"Incoming Subscribe" : (ev->dir == LinphoneSubscriptionOutgoing ? "Outgoing subscribe" : "Publish"), ev->name);
446

447 448 449 450 451 452 453 454 455 456 457 458
	return err;
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneEvent);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneEvent, belle_sip_object_t,
	(belle_sip_object_destroy_t) linphone_event_destroy,
	NULL, // clone
	_linphone_event_marshall,
	FALSE
);