presence.c 70.7 KB
Newer Older
aymeric's avatar
aymeric committed
1 2
/*
linphone
3
Copyright (C) 2010-2013  Belledonne Communications SARL
aymeric's avatar
aymeric committed
4 5 6 7 8 9 10 11 12 13 14 15 16

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.
aymeric's avatar
aymeric committed
18 19
*/

20 21 22
#include "linphone/core.h"
#include "linphone/lpconfig.h"
#include "linphone/presence.h"
23

24
#include "c-wrapper/c-wrapper.h"
aymeric's avatar
aymeric committed
25

26 27 28
// TODO: From coreapi. Remove me later.
#include "private.h"

29
using namespace LinphonePrivate;
30

aymeric's avatar
aymeric committed
31 32
extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol);

33
struct _LinphonePresenceNote {
34
	belle_sip_object_t base;
Ghislain MARY's avatar
Ghislain MARY committed
35
	void *user_data;
36 37 38 39
	char *lang;
	char *content;
};

40
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceNote);
Ghislain MARY's avatar
Ghislain MARY committed
41
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceNote);
42

43
struct _LinphonePresenceService {
44
	belle_sip_object_t base;
45
	void *user_data;
46 47
	char *id;
	LinphonePresenceBasicStatus status;
48
	char *contact;
49
	bctbx_list_t *notes;				/**< A list of _LinphonePresenceNote structures. */
50
	time_t timestamp;
51 52
};

53
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceService);
Ghislain MARY's avatar
Ghislain MARY committed
54
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceService);
55

56
struct _LinphonePresenceActivity {
57
	belle_sip_object_t base;
Ghislain MARY's avatar
Ghislain MARY committed
58
	void *user_data;
59
	LinphonePresenceActivityType type;
60 61 62
	char *description;
};

63
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceActivity);
Ghislain MARY's avatar
Ghislain MARY committed
64
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceActivity);
65

66
struct _LinphonePresencePerson {
67
	belle_sip_object_t base;
68
	void *user_data;
69
	char *id;
70 71 72
	bctbx_list_t *activities;		/**< A list of _LinphonePresenceActivity structures. */
	bctbx_list_t *activities_notes;	/**< A list of _LinphonePresenceNote structures. */
	bctbx_list_t *notes;			/**< A list of _LinphonePresenceNote structures. */
73 74 75
	time_t timestamp;
};

76
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresencePerson);
Ghislain MARY's avatar
Ghislain MARY committed
77
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresencePerson);
78

79 80 81 82 83
/**
 * Represents the presence model as defined in RFC 4479 and RFC 4480.
 * This model is not complete. For example, it does not handle devices.
 */
struct _LinphonePresenceModel {
84
	belle_sip_object_t base;
85
	LinphoneAddress *presentity; /* "The model seeks to describe the presentity, identified by a presentity URI.*/
Ghislain MARY's avatar
Ghislain MARY committed
86
	void *user_data;
87 88 89
	bctbx_list_t *services;	/**< A list of _LinphonePresenceService structures. Also named tuples in the RFC. */
	bctbx_list_t *persons;	/**< A list of _LinphonePresencePerson structures. */
	bctbx_list_t *notes;		/**< A list of _LinphonePresenceNote structures. */
90
	bool_t is_online;
91 92
};

93
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceModel);
Ghislain MARY's avatar
Ghislain MARY committed
94
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceModel);
95

96

97 98 99 100 101 102
static const char *person_prefix = "/pidf:presence/dm:person";


/*****************************************************************************
 * PRIVATE FUNCTIONS                                                         *
 ****************************************************************************/
Ghislain MARY's avatar
Ghislain MARY committed
103 104

/* Defined in http://www.w3.org/TR/REC-xml/ */
jehan's avatar
jehan committed
105
static char presence_id_valid_characters[] = "0123456789abcdefghijklmnopqrstuvwxyz-.";
Ghislain MARY's avatar
Ghislain MARY committed
106 107

/* NameStartChar (NameChar)* */
jehan's avatar
jehan committed
108
static char presence_id_valid_start_characters[] = ":_abcdefghijklmnopqrstuvwxyz";
109 110 111 112

static char * generate_presence_id(void) {
	char id[7];
	int i;
113
	id[0] = presence_id_valid_start_characters[ortp_random() % (sizeof(presence_id_valid_start_characters)-1)];
jehan's avatar
jehan committed
114
	for (i = 1; i < 6; i++) {
115
		id[i] = presence_id_valid_characters[ortp_random() % (sizeof(presence_id_valid_characters)-1)];
116 117 118 119 120 121
	}
	id[6] = '\0';

	return ms_strdup(id);
}

122
static void presence_note_uninit(LinphonePresenceNote *note) {
123 124 125 126 127 128
	ms_free(note->content);
	if (note->lang != NULL) {
		ms_free(note->lang);
	}
}

129
static LinphonePresenceService * presence_service_new(const char *id, LinphonePresenceBasicStatus status) {
130
	LinphonePresenceService *service = belle_sip_object_new(LinphonePresenceService);
131 132 133 134
	if (id != NULL) {
		service->id = ms_strdup(id);
	}
	service->status = status;
135
	service->timestamp = time(NULL);
136 137 138
	return service;
}

139
static void presence_service_uninit(LinphonePresenceService *service) {
140 141 142
	if (service->id != NULL) {
		ms_free(service->id);
	}
143 144 145
	if (service->contact != NULL) {
		ms_free(service->contact);
	}
146 147
	bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(service->notes);
148 149
};

150
static void presence_service_set_timestamp(LinphonePresenceService *service, time_t timestamp) {
151 152 153
	service->timestamp = timestamp;
}

154
static void presence_service_add_note(LinphonePresenceService *service, LinphonePresenceNote *note) {
155
	service->notes = bctbx_list_append(service->notes, note);
156 157
}

158
static void presence_activity_uninit(LinphonePresenceActivity *activity) {
159 160 161 162 163 164 165 166
	if (activity->description != NULL) {
		ms_free(activity->description);
	}
}

static time_t parse_timestamp(const char *timestamp) {
	struct tm ret;
	time_t seconds;
167
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
168 169 170 171
	long adjust_timezone;
#else
	time_t adjust_timezone;
#endif
172 173 174 175 176 177 178 179 180 181 182 183

	memset(&ret, 0, sizeof(ret));
	sscanf(timestamp, "%d-%d-%dT%d:%d:%d",
	       &ret.tm_year, &ret.tm_mon, &ret.tm_mday, &ret.tm_hour, &ret.tm_min, &ret.tm_sec);
	ret.tm_mon--;
	ret.tm_year -= 1900;
	ret.tm_isdst = 0;
	seconds = mktime(&ret);
	if (seconds == (time_t)-1) {
		ms_error("mktime() failed: %s", strerror(errno));
		return (time_t)-1;
	}
184
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
185 186 187 188 189
	_get_timezone(&adjust_timezone);
#else
	adjust_timezone = timezone;
#endif
	return seconds - (time_t)adjust_timezone;
190 191
}

192
char * linphone_timestamp_to_rfc3339_string(time_t timestamp) {
193 194
	char timestamp_str[22];
	struct tm *ret;
195
#ifndef _WIN32
196 197 198
	struct tm gmt;
	ret = gmtime_r(&timestamp,&gmt);
#else
199
	ret = gmtime(&timestamp);
200 201 202 203 204 205
#endif
	snprintf(timestamp_str, sizeof(timestamp_str), "%4d-%02d-%02dT%02d:%02d:%02dZ",
		 ret->tm_year + 1900, ret->tm_mon + 1, ret->tm_mday, ret->tm_hour, ret->tm_min, ret->tm_sec);
	return ms_strdup(timestamp_str);
}

206
static LinphonePresencePerson * presence_person_new(const char *id,  time_t timestamp) {
207
	LinphonePresencePerson *person = belle_sip_object_new(LinphonePresencePerson);
208 209 210
	if (id != NULL) {
		person->id = ms_strdup(id);
	}
211
	if (person->timestamp == ((time_t)-1))
212
		person->timestamp = time(NULL);
213 214
	else
		person->timestamp = timestamp;
215 216 217
	return person;
}

218
static void presence_person_uninit(LinphonePresencePerson *person) {
219 220 221
	if (person->id != NULL) {
		ms_free(person->id);
	}
222 223 224 225 226 227
	bctbx_list_for_each(person->activities, (MSIterateFunc)linphone_presence_activity_unref);
	bctbx_list_free(person->activities);
	bctbx_list_for_each(person->activities_notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(person->activities_notes);
	bctbx_list_for_each(person->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(person->notes);
228 229
}

230
static void presence_person_add_activities_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
231
	person->activities_notes = bctbx_list_append(person->activities_notes, note);
232 233
}

234
static void presence_person_add_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
235
	person->notes = bctbx_list_append(person->notes, note);
236 237
}

238
static int presence_model_insert_person_by_timestamp(LinphonePresencePerson *current, LinphonePresencePerson *to_insert) {
239
	return current->timestamp < to_insert->timestamp;
240 241
}

242
static void presence_model_add_person(LinphonePresenceModel *model, LinphonePresencePerson *person) {
243
	model->persons = bctbx_list_insert_sorted(model->persons, linphone_presence_person_ref(person), (bctbx_compare_func)presence_model_insert_person_by_timestamp);
244 245
}

246
static void presence_model_add_note(LinphonePresenceModel *model, LinphonePresenceNote *note) {
247
	model->notes = bctbx_list_append(model->notes, note);
248 249
}

250
static void presence_model_find_open_basic_status(LinphonePresenceService *service, LinphonePresenceBasicStatus *status) {
251 252 253 254 255
	if (service->status == LinphonePresenceBasicStatusOpen) {
		*status = LinphonePresenceBasicStatusOpen;
	}
}

256
static void presence_model_uninit(LinphonePresenceModel *model) {
257 258
	if (model->presentity)
		linphone_address_unref(model->presentity);
259 260 261 262 263 264
	bctbx_list_for_each(model->services, (MSIterateFunc)linphone_presence_service_unref);
	bctbx_list_free(model->services);
	bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_unref);
	bctbx_list_free(model->persons);
	bctbx_list_for_each(model->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(model->notes);
265 266
}

Ghislain MARY's avatar
Ghislain MARY committed
267

268 269 270 271 272 273 274 275

/*****************************************************************************
 * HELPER FUNCTIONS TO EASE ACCESS IN MOST SIMPLER CASES                     *
 ****************************************************************************/

LinphonePresenceModel * linphone_presence_model_new_with_activity(LinphonePresenceActivityType acttype, const char *description) {
	LinphonePresenceModel *model = linphone_presence_model_new();
	if (model != NULL) {
276
		linphone_presence_model_set_basic_status(model, LinphonePresenceBasicStatusOpen);
277
		linphone_presence_model_set_activity(model, acttype, description);
Ghislain MARY's avatar
Ghislain MARY committed
278 279 280 281
	}
	return model;
}

282 283 284
LinphonePresenceModel * linphone_presence_model_new_with_activity_and_note(LinphonePresenceActivityType acttype, const char *description, const char *note, const char *lang) {
	LinphonePresenceModel *model = linphone_presence_model_new();
	if (model != NULL) {
285
		linphone_presence_model_set_basic_status(model, LinphonePresenceBasicStatusOpen);
286 287 288 289
		linphone_presence_model_set_activity(model, acttype, description);
		linphone_presence_model_add_note(model, note, lang);
	}
	return model;
Ghislain MARY's avatar
Ghislain MARY committed
290 291
}

292 293 294
/* Suppose that if at least one service is open, then the model is open. */
LinphonePresenceBasicStatus linphone_presence_model_get_basic_status(const LinphonePresenceModel *model) {
	LinphonePresenceBasicStatus status = LinphonePresenceBasicStatusClosed;
295
	if (model != NULL) {
296
		bctbx_list_for_each2(model->services, (MSIterate2Func)presence_model_find_open_basic_status, &status);
297
	}
298 299 300
	return status;
}

301
LinphoneStatus linphone_presence_model_set_basic_status(LinphonePresenceModel *model, LinphonePresenceBasicStatus basic_status) {
302
	LinphonePresenceService *service;
303
	int err = 0;
304 305 306

	if (model == NULL) return -1;

307
	linphone_presence_model_clear_services(model);
308
	service = linphone_presence_service_new(NULL, basic_status, NULL);
309 310
	if (service == NULL) return -1;

311 312 313
	err = linphone_presence_model_add_service(model, service);
	linphone_presence_service_unref(service);
	return err;
314 315
}

316
static void presence_service_find_newer_timestamp(LinphonePresenceService *service, time_t *timestamp) {
317 318 319 320
	if (service->timestamp > *timestamp)
		*timestamp = service->timestamp;
}

321
static void presence_person_find_newer_timestamp(LinphonePresencePerson *person, time_t *timestamp) {
322 323 324 325 326 327 328 329 330 331
	if (person->timestamp > *timestamp)
		*timestamp = person->timestamp;
}

time_t linphone_presence_model_get_timestamp(const LinphonePresenceModel *model) {
	time_t timestamp = (time_t)-1;

	if (model == NULL)
		return timestamp;

332 333
	bctbx_list_for_each2(model->services, (MSIterate2Func)presence_service_find_newer_timestamp, &timestamp);
	bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_person_find_newer_timestamp, &timestamp);
334 335 336 337

	return timestamp;
}

338
static void presence_model_find_contact(LinphonePresenceService *service, char **contact) {
339 340 341 342 343 344
	if ((service->contact != NULL) && (*contact == NULL))
		*contact = service->contact;
}

char * linphone_presence_model_get_contact(const LinphonePresenceModel *model) {
	char *contact = NULL;
345
	bctbx_list_for_each2(model->services, (MSIterate2Func)presence_model_find_contact, &contact);
346 347 348 349
	if (contact == NULL) return NULL;
	return ms_strdup(contact);
}

350
LinphoneStatus linphone_presence_model_set_contact(LinphonePresenceModel *model, const char *contact) {
351 352 353 354 355 356
	LinphonePresenceService *service;

	if (model == NULL) return -1;

	service = linphone_presence_model_get_nth_service(model, 0);
	if (service == NULL) {
357
		service = linphone_presence_service_new(NULL, LinphonePresenceBasicStatusClosed, NULL);
358 359
		if (service == NULL) return -1;
		linphone_presence_model_add_service(model, service);
360
	}
361 362 363 364
	return linphone_presence_service_set_contact(service, contact);
}

static void presence_model_count_activities(const LinphonePresencePerson *person, unsigned int *nb) {
Ghislain MARY's avatar
Ghislain MARY committed
365
	*nb += (unsigned int)bctbx_list_size(person->activities);
366 367 368 369 370
}

struct _get_activity_st {
	unsigned int requested_idx;
	unsigned int current_idx;
371
	LinphonePresenceActivity *activity;
372 373
};

374
static void presence_model_get_activity(const LinphonePresencePerson *person, struct _get_activity_st *st) {
375
	if (st->current_idx != (unsigned)-1) {
Ghislain MARY's avatar
Ghislain MARY committed
376
		unsigned int size = (unsigned int)bctbx_list_size(person->activities);
377
		if (st->requested_idx < (st->current_idx + size)) {
Benjamin REIS's avatar
Benjamin REIS committed
378
			st->activity = (LinphonePresenceActivity *)bctbx_list_nth_data(person->activities, (int)(st->requested_idx - st->current_idx));
379 380 381 382
			st->current_idx = (unsigned)-1;
		} else {
			st->current_idx += size;
		}
383 384 385
	}
}

386 387
LinphonePresenceActivity * linphone_presence_model_get_activity(const LinphonePresenceModel *model) {
	return linphone_presence_model_get_nth_activity(model, 0);
388 389
}

390
LinphoneStatus linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivityType acttype, const char *description) {
391
	LinphonePresenceActivity *activity;
392
	int err = 0;
393 394 395

	if (model == NULL) return -1;

396
	linphone_presence_model_clear_activities(model);
397 398
	activity = linphone_presence_activity_new(acttype, description);
	if (activity == NULL) return -1;
399 400 401
	err = linphone_presence_model_add_activity(model, activity);
	linphone_presence_activity_unref(activity);
	return err;
402 403
}

404
unsigned int linphone_presence_model_get_nb_activities(const LinphonePresenceModel *model) {
405
	unsigned int nb = 0;
406
	bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_model_count_activities, &nb);
407 408 409 410 411 412
	return nb;
}

LinphonePresenceActivity * linphone_presence_model_get_nth_activity(const LinphonePresenceModel *model, unsigned int idx) {
	struct _get_activity_st st;

413
	if ((model == NULL) || (idx >= linphone_presence_model_get_nb_activities(model)))
414 415 416 417
		return NULL;

	memset(&st, 0, sizeof(st));
	st.requested_idx = idx;
418
	bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_model_get_activity, &st);
419 420 421 422

	return st.activity;
}

423
LinphoneStatus linphone_presence_model_add_activity(LinphonePresenceModel *model, LinphonePresenceActivity *activity) {
424
	char *id = NULL;
425
	LinphonePresencePerson *person = NULL;
426

427
	if ((model == NULL) || (activity == NULL)) return -1;
428

429
	if (bctbx_list_size(model->persons) == 0) {
430 431 432 433 434 435 436 437
		/* There is no person in the presence model, add one. */
		id = generate_presence_id();
		person = presence_person_new(id, time(NULL));
		if (id != NULL) ms_free(id);
		if (person == NULL)
			return -1;

		presence_model_add_person(model, person);
Ghislain MARY's avatar
Ghislain MARY committed
438
		linphone_presence_person_unref(person);
439 440
	} else {
		/* Add the activity to the first person in the model. */
441
		person = (LinphonePresencePerson *)bctbx_list_nth_data(model->persons, 0);
442 443
	}

444
	linphone_presence_person_add_activity(person, activity);
445 446 447
	return 0;
}

448
LinphoneStatus linphone_presence_model_clear_activities(LinphonePresenceModel *model) {
449 450
	if (model == NULL) return -1;

451
	bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_clear_activities);
452 453 454
	return 0;
}

455 456
struct _find_note_st {
	const char *lang;
457
	LinphonePresenceNote *note;
458 459
};

460
static LinphonePresenceNote * find_presence_note_in_list(bctbx_list_t *list, const char *lang) {
461
	int i;
Ghislain MARY's avatar
Ghislain MARY committed
462
	int nb = (int)bctbx_list_size(list);
463
	for (i = 0; i < nb; i++) {
464
		LinphonePresenceNote *note = (LinphonePresenceNote *)bctbx_list_nth_data(list, i);
465 466 467 468 469 470 471 472 473 474 475
		if (lang == NULL) {
			if (note->lang == NULL) {
				return note;
			}
		} else {
			if ((note->lang != NULL) && (strcmp(lang, note->lang) == 0)) {
				return note;
			}
		}
	}

476 477 478
	return NULL;
}

479
static void find_presence_person_note(LinphonePresencePerson *person, struct _find_note_st *st) {
480 481 482 483 484 485 486 487
	/* First look for the note in the activities notes... */
	st->note = find_presence_note_in_list(person->activities_notes, st->lang);
	if (st->note != NULL) return;

	/* ... then look in the person notes. */
	st->note = find_presence_note_in_list(person->notes, st->lang);
}

488
static void find_presence_service_note(LinphonePresenceService *service, struct _find_note_st *st) {
489 490 491
	st->note = find_presence_note_in_list(service->notes, st->lang);
}

492 493
static LinphonePresenceNote * get_first_presence_note_in_list(bctbx_list_t *list) {
	return (LinphonePresenceNote *)bctbx_list_nth_data(list, 0);
494 495
}

496
static void get_first_presence_person_note(LinphonePresencePerson *person, struct _find_note_st *st) {
497 498 499 500 501
	st->note = get_first_presence_note_in_list(person->activities_notes);
	if (st->note != NULL) return;
	st->note = get_first_presence_note_in_list(person->notes);
}

502
static void get_first_presence_service_note(LinphonePresenceService *service, struct _find_note_st *st) {
503 504 505
	st->note = get_first_presence_note_in_list(service->notes);
}

506
LinphonePresenceNote * linphone_presence_model_get_note(const LinphonePresenceModel *model, const char *lang) {
507 508 509 510 511 512 513 514
	struct _find_note_st st;

	if (model == NULL) return NULL;

	st.note = NULL;
	if (lang != NULL) {
		/* First try to find a note in the specified language exactly. */
		st.lang = lang;
515
		bctbx_list_for_each2(model->persons, (MSIterate2Func)find_presence_person_note, &st);
516
		if (st.note == NULL) {
517
			bctbx_list_for_each2(model->services, (MSIterate2Func)find_presence_service_note, &st);
518 519 520 521 522 523 524 525 526
		}
		if (st.note == NULL) {
			st.note = find_presence_note_in_list(model->notes, lang);
		}
	}

	if (st.note == NULL) {
		/* No notes in the specified language has been found, try to find one without language. */
		st.lang = NULL;
527
		bctbx_list_for_each2(model->persons, (MSIterate2Func)find_presence_person_note, &st);
528
		if (st.note == NULL) {
529
			bctbx_list_for_each2(model->services, (MSIterate2Func)find_presence_service_note, &st);
530 531 532 533 534 535 536 537
		}
		if (st.note == NULL) {
			st.note = find_presence_note_in_list(model->notes, NULL);
		}
	}

	if (st.note == NULL) {
		/* Still no result, so get the first note even if it is not in the specified language. */
538
		bctbx_list_for_each2(model->persons, (MSIterate2Func)get_first_presence_person_note, &st);
539
		if (st.note == NULL) {
540
			bctbx_list_for_each2(model->services, (MSIterate2Func)get_first_presence_service_note, &st);
541 542 543 544 545 546
		}
		if (st.note == NULL) {
			st.note = get_first_presence_note_in_list(model->notes);
		}
	}

547
	return st.note;
548 549
}

550
LinphoneStatus linphone_presence_model_add_note(LinphonePresenceModel *model, const char *note_content, const char *lang) {
551 552
	LinphonePresenceService *service;
	LinphonePresenceNote *note;
553 554 555 556 557

	if ((model == NULL) || (note_content == NULL))
		return -1;

	/* Will put the note in the first service. */
Ghislain MARY's avatar
Ghislain MARY committed
558
	service = reinterpret_cast<LinphonePresenceService *>(bctbx_list_nth_data(model->services, 0));
559 560 561 562 563 564 565 566 567 568
	if (service == NULL) {
		/* If no service exists, create one. */
		service = presence_service_new(generate_presence_id(), LinphonePresenceBasicStatusClosed);
	}
	if (service == NULL)
		return -1;

	/* Search for an existing note in the specified language. */
	note = find_presence_note_in_list(service->notes, lang);
	if (note == NULL) {
Ghislain MARY's avatar
Ghislain MARY committed
569
		note = linphone_presence_note_new(note_content, lang);
570
	} else {
Ghislain MARY's avatar
Ghislain MARY committed
571
		linphone_presence_note_set_content(note, note_content);
572 573 574 575 576 577 578 579 580
	}
	if (note == NULL)
		return -1;

	presence_service_add_note(service, note);

	return 0;
}

581
static void clear_presence_person_notes(LinphonePresencePerson *person) {
582 583
	bctbx_list_for_each(person->activities_notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(person->activities_notes);
584
	person->activities_notes = NULL;
585 586
	bctbx_list_for_each(person->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(person->notes);
587 588 589
	person->notes = NULL;
}

590
static void clear_presence_service_notes(LinphonePresenceService *service) {
591 592
	bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(service->notes);
593 594 595
	service->notes = NULL;
}

596
LinphoneStatus linphone_presence_model_clear_notes(LinphonePresenceModel *model) {
597 598 599
	if (model == NULL)
		return -1;

600 601 602 603
	bctbx_list_for_each(model->persons, (MSIterateFunc)clear_presence_person_notes);
	bctbx_list_for_each(model->services, (MSIterateFunc)clear_presence_service_notes);
	bctbx_list_for_each(model->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(model->notes);
604 605 606
	model->notes = NULL;

	return 0;
607 608
}

609 610 611 612 613
/*****************************************************************************
 * PRESENCE MODEL FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES             *
 ****************************************************************************/

LinphonePresenceModel * linphone_presence_model_new(void) {
614
	LinphonePresenceModel *model = belle_sip_object_new(LinphonePresenceModel);
615
	return model;
616 617
}

618
unsigned int linphone_presence_model_get_nb_services(const LinphonePresenceModel *model) {
Ghislain MARY's avatar
Ghislain MARY committed
619
	return (unsigned int)bctbx_list_size(model->services);
620
}
621

622
LinphonePresenceService * linphone_presence_model_get_nth_service(const LinphonePresenceModel *model, unsigned int idx) {
623
	if ((model == NULL) || (idx >= linphone_presence_model_get_nb_services(model)))
624
		return NULL;
625

Benjamin REIS's avatar
Benjamin REIS committed
626
	return (LinphonePresenceService *)bctbx_list_nth_data(model->services, (int)idx);
627 628
}

629
LinphoneStatus linphone_presence_model_add_service(LinphonePresenceModel *model, LinphonePresenceService *service) {
630
	if ((model == NULL) || (service == NULL)) return -1;
631
	model->services = bctbx_list_append(model->services, linphone_presence_service_ref(service));
632
	return 0;
633 634
}

635
LinphoneStatus linphone_presence_model_clear_services(LinphonePresenceModel *model) {
636 637
	if (model == NULL) return -1;

638 639
	bctbx_list_for_each(model->services, (MSIterateFunc)linphone_presence_service_unref);
	bctbx_list_free(model->services);
640 641
	model->services = NULL;
	return 0;
642 643
}

644
unsigned int linphone_presence_model_get_nb_persons(const LinphonePresenceModel *model) {
Ghislain MARY's avatar
Ghislain MARY committed
645
	return (unsigned int)bctbx_list_size(model->persons);
646 647 648
}

LinphonePresencePerson * linphone_presence_model_get_nth_person(const LinphonePresenceModel *model, unsigned int idx) {
649
	if ((model == NULL) || (idx >= linphone_presence_model_get_nb_persons(model)))
650 651
		return NULL;

Benjamin REIS's avatar
Benjamin REIS committed
652
	return (LinphonePresencePerson *)bctbx_list_nth_data(model->persons, (int)idx);
653 654
}

655
LinphoneStatus linphone_presence_model_add_person(LinphonePresenceModel *model, LinphonePresencePerson *person) {
656
	if ((model == NULL) || (person == NULL)) return -1;
657
	presence_model_add_person(model, person);
658 659 660
	return 0;
}

661
LinphoneStatus linphone_presence_model_clear_persons(LinphonePresenceModel *model) {
662 663
	if (model == NULL) return -1;

664 665
	bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_unref);
	bctbx_list_free(model->persons);
666 667 668 669
	model->persons = NULL;
	return 0;
}

670
LinphoneStatus linphone_presence_model_set_presentity(LinphonePresenceModel *model, const LinphoneAddress *presentity) {
671

672 673 674 675 676 677 678 679 680 681
	if (model->presentity) {
		linphone_address_unref(model->presentity);
		model->presentity = NULL;
	}
	if (presentity) {
		model->presentity=linphone_address_clone(presentity);
		linphone_address_clean(model->presentity);
	}
	return 0;
}
682

683 684 685
const LinphoneAddress * linphone_presence_model_get_presentity(const LinphonePresenceModel *model) {
	return model->presentity;
}
686

687 688 689 690 691 692 693 694 695 696 697 698 699
LinphoneConsolidatedPresence linphone_presence_model_get_consolidated_presence(const LinphonePresenceModel *model) {
	LinphonePresenceBasicStatus basic_status;

	if (linphone_presence_model_is_online(model)) return LinphoneConsolidatedPresenceOnline;
	basic_status = linphone_presence_model_get_basic_status(model);
	if (basic_status == LinphonePresenceBasicStatusClosed) {
		unsigned int nb_activities = linphone_presence_model_get_nb_activities(model);
		if (nb_activities == 0) return LinphoneConsolidatedPresenceOffline;
		else return LinphoneConsolidatedPresenceDoNotDisturb;
	}
	return LinphoneConsolidatedPresenceBusy;
}

700 701 702 703 704 705 706 707 708
BELLE_SIP_INSTANCIATE_VPTR(
	LinphonePresenceModel,
	belle_sip_object_t,
	presence_model_uninit, // destroy
	NULL, // clone
	NULL, // marshal
	FALSE // unown
);

709 710 711
/*****************************************************************************
 * PRESENCE SERVICE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES           *
 ****************************************************************************/
712

713 714 715 716 717 718 719 720 721 722
char * linphone_presence_basic_status_to_string(LinphonePresenceBasicStatus basic_status) {
	switch (basic_status) {
		case LinphonePresenceBasicStatusOpen:
			return ms_strdup("open");
		case LinphonePresenceBasicStatusClosed:
		default:
			return ms_strdup("closed");
	}
}

723
LinphonePresenceService * linphone_presence_service_new(const char *id, LinphonePresenceBasicStatus basic_status, const char *contact) {
724
	LinphonePresenceService *service;
725 726 727 728 729
	char *service_id;
	if (id == NULL)
		service_id = generate_presence_id();
	else
		service_id = ms_strdup(id);
730 731
	service = presence_service_new(service_id, basic_status);
	linphone_presence_service_set_contact(service, contact);
732 733
	if (service_id != NULL)
		ms_free(service_id);
734 735
	return service;
}
736

737 738 739 740 741
char * linphone_presence_service_get_id(const LinphonePresenceService *service) {
	if (service == NULL) return NULL;
	return ms_strdup(service->id);
}

742
LinphoneStatus linphone_presence_service_set_id(LinphonePresenceService *service, const char *id) {
743 744 745 746 747 748 749 750 751 752
	if (service == NULL) return -1;
	if (service->id != NULL)
		ms_free(service->id);
	if (id == NULL)
		service->id = generate_presence_id();
	else
		service->id = ms_strdup(id);
	return 0;
}

753 754 755
LinphonePresenceBasicStatus linphone_presence_service_get_basic_status(const LinphonePresenceService *service) {
	if (service == NULL) return LinphonePresenceBasicStatusClosed;
	return service->status;
756 757
}

758
LinphoneStatus linphone_presence_service_set_basic_status(LinphonePresenceService *service, LinphonePresenceBasicStatus basic_status) {
759 760 761 762
	if (service == NULL) return -1;
	service->status = basic_status;
	return 0;
}
763

764 765 766 767
char * linphone_presence_service_get_contact(const LinphonePresenceService *service) {
	if (service->contact == NULL) return NULL;
	return ms_strdup(service->contact);
}
768

769
LinphoneStatus linphone_presence_service_set_contact(LinphonePresenceService *service, const char *contact) {
770
	if (service == NULL) return -1;
771 772 773 774 775 776
	if (service->contact != NULL)
		ms_free(service->contact);
	if (contact != NULL)
		service->contact = ms_strdup(contact);
	else
		service->contact = NULL;
777 778 779
	return 0;
}

780
unsigned int linphone_presence_service_get_nb_notes(const LinphonePresenceService *service) {
Ghislain MARY's avatar
Ghislain MARY committed
781
	return (unsigned int)bctbx_list_size(service->notes);
782 783 784
}

LinphonePresenceNote * linphone_presence_service_get_nth_note(const LinphonePresenceService *service, unsigned int idx) {
785
	if ((service == NULL) || (idx >= linphone_presence_service_get_nb_notes(service)))
786 787
		return NULL;

Benjamin REIS's avatar
Benjamin REIS committed
788
	return (LinphonePresenceNote *)bctbx_list_nth_data(service->notes, (int)idx);
789 790
}

791
LinphoneStatus linphone_presence_service_add_note(LinphonePresenceService *service, LinphonePresenceNote *note) {
792
	if ((service == NULL) || (note == NULL)) return -1;
793
	service->notes = bctbx_list_append(service->notes, linphone_presence_note_ref(note));
794 795 796
	return 0;
}

797
LinphoneStatus linphone_presence_service_clear_notes(LinphonePresenceService *service) {
798 799
	if (service == NULL) return -1;

800 801
	bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
	bctbx_list_free(service->notes);
802 803 804 805
	service->notes = NULL;
	return 0;
}

806 807 808 809 810 811 812 813 814
BELLE_SIP_INSTANCIATE_VPTR(
	LinphonePresenceService,
	belle_sip_object_t,
	presence_service_uninit, // destroy
	NULL, // clone
	NULL, // marshal
	FALSE // unown
);

815 816


817 818 819 820 821 822 823 824 825 826 827 828 829
/*****************************************************************************
 * PRESENCE PERSON FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES            *
 ****************************************************************************/

LinphonePresencePerson * linphone_presence_person_new(const char *id) {
	return presence_person_new(id, time(NULL));
}

char * linphone_presence_person_get_id(const LinphonePresencePerson *person) {
	if (person == NULL) return NULL;
	return ms_strdup(person->id);
}

830
LinphoneStatus linphone_presence_person_set_id(LinphonePresencePerson *person, const char *id) {
831 832 833 834 835 836 837 838 839 840
	if (person == NULL) return -1;
	if (person->id != NULL)
		ms_free(person->id);
	if (id == NULL)
		person->id = generate_presence_id();
	else
		person->id = ms_strdup(id);
	return 0;
}

841
unsigned int linphone_presence_person_get_nb_activities(const LinphonePresencePerson *person) {
842
	if (person == NULL) return 0;
Ghislain MARY's avatar
Ghislain MARY committed
843
	return (unsigned int)bctbx_list_size(person->activities);
844 845 846
}

LinphonePresenceActivity * linphone_presence_person_get_nth_activity(const LinphonePresencePerson *person, unsigned int idx) {
847
	if ((person == NULL) || (idx >= linphone_presence_person_get_nb_activities(person)))
848
		return NULL;
Benjamin REIS's avatar
Benjamin REIS committed
849
	return (LinphonePresenceActivity *)bctbx_list_nth_data(person->activities, (int)idx);
850 851
}

852
LinphoneStatus linphone_presence_person_add_activity(LinphonePresencePerson *person, LinphonePresenceActivity *activity) {
853
	if ((person == NULL) || (activity == NULL)) return -1;
854
	// insert in first position since its the most recent activity!
855
	person->activities = bctbx_list_prepend(person->activities, linphone_presence_activity_ref(activity));
856 857 858
	return 0;
}

859
LinphoneStatus linphone_presence_person_clear_activities(LinphonePresencePerson *person) {
860
	if (person == NULL) return -1;
861 862
	bctbx_list_for_each(person->activities, (MSIterateFunc)linphone_presence_activity_unref);
	bctbx_list_free(person->activities);
863 864 865 866
	person->activities = NULL;
	return 0;
}

867
unsigned int linphone_presence_person_get_nb_notes(const LinphonePresencePerson *person) {
868
	if (person == NULL) return 0;
Ghislain MARY's avatar
Ghislain MARY committed
869
	return (unsigned int)bctbx_list_size(person->notes);
870 871 872
}

LinphonePresenceNote * linphone_presence_person_get_nth_note(const LinphonePresencePerson *person, unsigned int idx) {
873
	if ((person == NULL) || (idx >= linphone_presence_person_get_nb_notes(person)))
874
		return NULL;
Benjamin REIS's avatar
Benjamin REIS committed
875
	return (LinphonePresenceNote *)bctbx_list_nth_data(person->notes, (int)idx);
876