mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
2033 lines
71 KiB
C
2033 lines
71 KiB
C
/*
|
|
linphone
|
|
Copyright (C) 2010-2013 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "linphone/core.h"
|
|
#include "linphone/lpconfig.h"
|
|
#include "linphone/presence.h"
|
|
|
|
#include "c-wrapper/c-wrapper.h"
|
|
|
|
// TODO: From coreapi. Remove me later.
|
|
#include "private.h"
|
|
|
|
using namespace LinphonePrivate;
|
|
|
|
extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol);
|
|
|
|
struct _LinphonePresenceNote {
|
|
belle_sip_object_t base;
|
|
void *user_data;
|
|
char *lang;
|
|
char *content;
|
|
};
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceNote);
|
|
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceNote);
|
|
|
|
struct _LinphonePresenceService {
|
|
belle_sip_object_t base;
|
|
void *user_data;
|
|
char *id;
|
|
LinphonePresenceBasicStatus status;
|
|
char *contact;
|
|
bctbx_list_t *notes; /**< A list of _LinphonePresenceNote structures. */
|
|
time_t timestamp;
|
|
};
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceService);
|
|
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceService);
|
|
|
|
struct _LinphonePresenceActivity {
|
|
belle_sip_object_t base;
|
|
void *user_data;
|
|
LinphonePresenceActivityType type;
|
|
char *description;
|
|
};
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceActivity);
|
|
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceActivity);
|
|
|
|
struct _LinphonePresencePerson {
|
|
belle_sip_object_t base;
|
|
void *user_data;
|
|
char *id;
|
|
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. */
|
|
time_t timestamp;
|
|
};
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresencePerson);
|
|
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresencePerson);
|
|
|
|
/**
|
|
* 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 {
|
|
belle_sip_object_t base;
|
|
LinphoneAddress *presentity; /* "The model seeks to describe the presentity, identified by a presentity URI.*/
|
|
void *user_data;
|
|
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. */
|
|
bool_t is_online;
|
|
};
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphonePresenceModel);
|
|
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphonePresenceModel);
|
|
|
|
|
|
static const char *person_prefix = "/pidf:presence/dm:person";
|
|
|
|
|
|
/*****************************************************************************
|
|
* PRIVATE FUNCTIONS *
|
|
****************************************************************************/
|
|
|
|
/* Defined in http://www.w3.org/TR/REC-xml/ */
|
|
static char presence_id_valid_characters[] = "0123456789abcdefghijklmnopqrstuvwxyz-.";
|
|
|
|
/* NameStartChar (NameChar)* */
|
|
static char presence_id_valid_start_characters[] = ":_abcdefghijklmnopqrstuvwxyz";
|
|
|
|
static char * generate_presence_id(void) {
|
|
char id[7];
|
|
int i;
|
|
id[0] = presence_id_valid_start_characters[ortp_random() % (sizeof(presence_id_valid_start_characters)-1)];
|
|
for (i = 1; i < 6; i++) {
|
|
id[i] = presence_id_valid_characters[ortp_random() % (sizeof(presence_id_valid_characters)-1)];
|
|
}
|
|
id[6] = '\0';
|
|
|
|
return ms_strdup(id);
|
|
}
|
|
|
|
static void presence_note_uninit(LinphonePresenceNote *note) {
|
|
ms_free(note->content);
|
|
if (note->lang != NULL) {
|
|
ms_free(note->lang);
|
|
}
|
|
}
|
|
|
|
static LinphonePresenceService * presence_service_new(const char *id, LinphonePresenceBasicStatus status) {
|
|
LinphonePresenceService *service = belle_sip_object_new(LinphonePresenceService);
|
|
if (id != NULL) {
|
|
service->id = ms_strdup(id);
|
|
}
|
|
service->status = status;
|
|
service->timestamp = time(NULL);
|
|
return service;
|
|
}
|
|
|
|
static void presence_service_uninit(LinphonePresenceService *service) {
|
|
if (service->id != NULL) {
|
|
ms_free(service->id);
|
|
}
|
|
if (service->contact != NULL) {
|
|
ms_free(service->contact);
|
|
}
|
|
bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(service->notes);
|
|
};
|
|
|
|
static void presence_service_set_timestamp(LinphonePresenceService *service, time_t timestamp) {
|
|
service->timestamp = timestamp;
|
|
}
|
|
|
|
static void presence_service_add_note(LinphonePresenceService *service, LinphonePresenceNote *note) {
|
|
service->notes = bctbx_list_append(service->notes, note);
|
|
}
|
|
|
|
static void presence_activity_uninit(LinphonePresenceActivity *activity) {
|
|
if (activity->description != NULL) {
|
|
ms_free(activity->description);
|
|
}
|
|
}
|
|
|
|
static time_t parse_timestamp(const char *timestamp) {
|
|
struct tm ret;
|
|
time_t seconds;
|
|
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
|
|
long adjust_timezone;
|
|
#else
|
|
time_t adjust_timezone;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
#if defined(LINPHONE_WINDOWS_UNIVERSAL) || defined(LINPHONE_MSC_VER_GREATER_19)
|
|
_get_timezone(&adjust_timezone);
|
|
#else
|
|
adjust_timezone = timezone;
|
|
#endif
|
|
return seconds - (time_t)adjust_timezone;
|
|
}
|
|
|
|
char * linphone_timestamp_to_rfc3339_string(time_t timestamp) {
|
|
char timestamp_str[22];
|
|
struct tm *ret;
|
|
#ifndef _WIN32
|
|
struct tm gmt;
|
|
ret = gmtime_r(×tamp,&gmt);
|
|
#else
|
|
ret = gmtime(×tamp);
|
|
#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);
|
|
}
|
|
|
|
static LinphonePresencePerson * presence_person_new(const char *id, time_t timestamp) {
|
|
LinphonePresencePerson *person = belle_sip_object_new(LinphonePresencePerson);
|
|
if (id != NULL) {
|
|
person->id = ms_strdup(id);
|
|
}
|
|
if (person->timestamp == ((time_t)-1))
|
|
person->timestamp = time(NULL);
|
|
else
|
|
person->timestamp = timestamp;
|
|
return person;
|
|
}
|
|
|
|
static void presence_person_uninit(LinphonePresencePerson *person) {
|
|
if (person->id != NULL) {
|
|
ms_free(person->id);
|
|
}
|
|
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);
|
|
}
|
|
|
|
static void presence_person_add_activities_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
|
|
person->activities_notes = bctbx_list_append(person->activities_notes, note);
|
|
}
|
|
|
|
static void presence_person_add_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
|
|
person->notes = bctbx_list_append(person->notes, note);
|
|
}
|
|
|
|
static int presence_model_insert_person_by_timestamp(LinphonePresencePerson *current, LinphonePresencePerson *to_insert) {
|
|
return current->timestamp < to_insert->timestamp;
|
|
}
|
|
|
|
static void presence_model_add_person(LinphonePresenceModel *model, LinphonePresencePerson *person) {
|
|
model->persons = bctbx_list_insert_sorted(model->persons, linphone_presence_person_ref(person), (bctbx_compare_func)presence_model_insert_person_by_timestamp);
|
|
}
|
|
|
|
static void presence_model_add_note(LinphonePresenceModel *model, LinphonePresenceNote *note) {
|
|
model->notes = bctbx_list_append(model->notes, note);
|
|
}
|
|
|
|
static void presence_model_find_open_basic_status(LinphonePresenceService *service, LinphonePresenceBasicStatus *status) {
|
|
if (service->status == LinphonePresenceBasicStatusOpen) {
|
|
*status = LinphonePresenceBasicStatusOpen;
|
|
}
|
|
}
|
|
|
|
static void presence_model_uninit(LinphonePresenceModel *model) {
|
|
if (model->presentity)
|
|
linphone_address_unref(model->presentity);
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* 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) {
|
|
linphone_presence_model_set_basic_status(model, LinphonePresenceBasicStatusOpen);
|
|
linphone_presence_model_set_activity(model, acttype, description);
|
|
}
|
|
return model;
|
|
}
|
|
|
|
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) {
|
|
linphone_presence_model_set_basic_status(model, LinphonePresenceBasicStatusOpen);
|
|
linphone_presence_model_set_activity(model, acttype, description);
|
|
linphone_presence_model_add_note(model, note, lang);
|
|
}
|
|
return model;
|
|
}
|
|
|
|
/* 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;
|
|
if (model != NULL) {
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)presence_model_find_open_basic_status, &status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_set_basic_status(LinphonePresenceModel *model, LinphonePresenceBasicStatus basic_status) {
|
|
LinphonePresenceService *service;
|
|
int err = 0;
|
|
|
|
if (model == NULL) return -1;
|
|
|
|
linphone_presence_model_clear_services(model);
|
|
service = linphone_presence_service_new(NULL, basic_status, NULL);
|
|
if (service == NULL) return -1;
|
|
|
|
err = linphone_presence_model_add_service(model, service);
|
|
linphone_presence_service_unref(service);
|
|
return err;
|
|
}
|
|
|
|
static void presence_service_find_newer_timestamp(LinphonePresenceService *service, time_t *timestamp) {
|
|
if (service->timestamp > *timestamp)
|
|
*timestamp = service->timestamp;
|
|
}
|
|
|
|
static void presence_person_find_newer_timestamp(LinphonePresencePerson *person, time_t *timestamp) {
|
|
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;
|
|
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)presence_service_find_newer_timestamp, ×tamp);
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_person_find_newer_timestamp, ×tamp);
|
|
|
|
return timestamp;
|
|
}
|
|
|
|
static void presence_model_find_contact(LinphonePresenceService *service, char **contact) {
|
|
if ((service->contact != NULL) && (*contact == NULL))
|
|
*contact = service->contact;
|
|
}
|
|
|
|
char * linphone_presence_model_get_contact(const LinphonePresenceModel *model) {
|
|
char *contact = NULL;
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)presence_model_find_contact, &contact);
|
|
if (contact == NULL) return NULL;
|
|
return ms_strdup(contact);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_set_contact(LinphonePresenceModel *model, const char *contact) {
|
|
LinphonePresenceService *service;
|
|
|
|
if (model == NULL) return -1;
|
|
|
|
service = linphone_presence_model_get_nth_service(model, 0);
|
|
if (service == NULL) {
|
|
service = linphone_presence_service_new(NULL, LinphonePresenceBasicStatusClosed, NULL);
|
|
if (service == NULL) return -1;
|
|
linphone_presence_model_add_service(model, service);
|
|
linphone_presence_service_unref(service);
|
|
}
|
|
return linphone_presence_service_set_contact(service, contact);
|
|
}
|
|
|
|
static void presence_model_count_activities(const LinphonePresencePerson *person, unsigned int *nb) {
|
|
*nb += (unsigned int)bctbx_list_size(person->activities);
|
|
}
|
|
|
|
struct _get_activity_st {
|
|
unsigned int requested_idx;
|
|
unsigned int current_idx;
|
|
LinphonePresenceActivity *activity;
|
|
};
|
|
|
|
static void presence_model_get_activity(const LinphonePresencePerson *person, struct _get_activity_st *st) {
|
|
if (st->current_idx != (unsigned)-1) {
|
|
unsigned int size = (unsigned int)bctbx_list_size(person->activities);
|
|
if (st->requested_idx < (st->current_idx + size)) {
|
|
st->activity = (LinphonePresenceActivity *)bctbx_list_nth_data(person->activities, (int)(st->requested_idx - st->current_idx));
|
|
st->current_idx = (unsigned)-1;
|
|
} else {
|
|
st->current_idx += size;
|
|
}
|
|
}
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_model_get_activity(const LinphonePresenceModel *model) {
|
|
return linphone_presence_model_get_nth_activity(model, 0);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivityType acttype, const char *description) {
|
|
LinphonePresenceActivity *activity;
|
|
int err = 0;
|
|
|
|
if (model == NULL) return -1;
|
|
|
|
linphone_presence_model_clear_activities(model);
|
|
activity = linphone_presence_activity_new(acttype, description);
|
|
if (activity == NULL) return -1;
|
|
err = linphone_presence_model_add_activity(model, activity);
|
|
linphone_presence_activity_unref(activity);
|
|
return err;
|
|
}
|
|
|
|
unsigned int linphone_presence_model_get_nb_activities(const LinphonePresenceModel *model) {
|
|
unsigned int nb = 0;
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_model_count_activities, &nb);
|
|
return nb;
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_model_get_nth_activity(const LinphonePresenceModel *model, unsigned int idx) {
|
|
struct _get_activity_st st;
|
|
|
|
if ((model == NULL) || (idx >= linphone_presence_model_get_nb_activities(model)))
|
|
return NULL;
|
|
|
|
memset(&st, 0, sizeof(st));
|
|
st.requested_idx = idx;
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)presence_model_get_activity, &st);
|
|
|
|
return st.activity;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_add_activity(LinphonePresenceModel *model, LinphonePresenceActivity *activity) {
|
|
char *id = NULL;
|
|
LinphonePresencePerson *person = NULL;
|
|
|
|
if ((model == NULL) || (activity == NULL)) return -1;
|
|
|
|
if (bctbx_list_size(model->persons) == 0) {
|
|
/* 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);
|
|
linphone_presence_person_unref(person);
|
|
} else {
|
|
/* Add the activity to the first person in the model. */
|
|
person = (LinphonePresencePerson *)bctbx_list_nth_data(model->persons, 0);
|
|
}
|
|
|
|
linphone_presence_person_add_activity(person, activity);
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_clear_activities(LinphonePresenceModel *model) {
|
|
if (model == NULL) return -1;
|
|
|
|
bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_clear_activities);
|
|
return 0;
|
|
}
|
|
|
|
struct _find_note_st {
|
|
const char *lang;
|
|
LinphonePresenceNote *note;
|
|
};
|
|
|
|
static LinphonePresenceNote * find_presence_note_in_list(bctbx_list_t *list, const char *lang) {
|
|
int i;
|
|
int nb = (int)bctbx_list_size(list);
|
|
for (i = 0; i < nb; i++) {
|
|
LinphonePresenceNote *note = (LinphonePresenceNote *)bctbx_list_nth_data(list, i);
|
|
if (lang == NULL) {
|
|
if (note->lang == NULL) {
|
|
return note;
|
|
}
|
|
} else {
|
|
if ((note->lang != NULL) && (strcmp(lang, note->lang) == 0)) {
|
|
return note;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void find_presence_person_note(LinphonePresencePerson *person, struct _find_note_st *st) {
|
|
/* 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);
|
|
}
|
|
|
|
static void find_presence_service_note(LinphonePresenceService *service, struct _find_note_st *st) {
|
|
st->note = find_presence_note_in_list(service->notes, st->lang);
|
|
}
|
|
|
|
static LinphonePresenceNote * get_first_presence_note_in_list(bctbx_list_t *list) {
|
|
return (LinphonePresenceNote *)bctbx_list_nth_data(list, 0);
|
|
}
|
|
|
|
static void get_first_presence_person_note(LinphonePresencePerson *person, struct _find_note_st *st) {
|
|
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);
|
|
}
|
|
|
|
static void get_first_presence_service_note(LinphonePresenceService *service, struct _find_note_st *st) {
|
|
st->note = get_first_presence_note_in_list(service->notes);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_model_get_note(const LinphonePresenceModel *model, const char *lang) {
|
|
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;
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)find_presence_person_note, &st);
|
|
if (st.note == NULL) {
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)find_presence_service_note, &st);
|
|
}
|
|
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;
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)find_presence_person_note, &st);
|
|
if (st.note == NULL) {
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)find_presence_service_note, &st);
|
|
}
|
|
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. */
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)get_first_presence_person_note, &st);
|
|
if (st.note == NULL) {
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)get_first_presence_service_note, &st);
|
|
}
|
|
if (st.note == NULL) {
|
|
st.note = get_first_presence_note_in_list(model->notes);
|
|
}
|
|
}
|
|
|
|
return st.note;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_add_note(LinphonePresenceModel *model, const char *note_content, const char *lang) {
|
|
LinphonePresenceService *service;
|
|
LinphonePresenceNote *note;
|
|
|
|
if ((model == NULL) || (note_content == NULL))
|
|
return -1;
|
|
|
|
/* Will put the note in the first service. */
|
|
service = reinterpret_cast<LinphonePresenceService *>(bctbx_list_nth_data(model->services, 0));
|
|
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) {
|
|
note = linphone_presence_note_new(note_content, lang);
|
|
} else {
|
|
linphone_presence_note_set_content(note, note_content);
|
|
}
|
|
if (note == NULL)
|
|
return -1;
|
|
|
|
presence_service_add_note(service, note);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void clear_presence_person_notes(LinphonePresencePerson *person) {
|
|
bctbx_list_for_each(person->activities_notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(person->activities_notes);
|
|
person->activities_notes = NULL;
|
|
bctbx_list_for_each(person->notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(person->notes);
|
|
person->notes = NULL;
|
|
}
|
|
|
|
static void clear_presence_service_notes(LinphonePresenceService *service) {
|
|
bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(service->notes);
|
|
service->notes = NULL;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_clear_notes(LinphonePresenceModel *model) {
|
|
if (model == NULL)
|
|
return -1;
|
|
|
|
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);
|
|
model->notes = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PRESENCE MODEL FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
|
****************************************************************************/
|
|
|
|
LinphonePresenceModel * linphone_presence_model_new(void) {
|
|
LinphonePresenceModel *model = belle_sip_object_new(LinphonePresenceModel);
|
|
return model;
|
|
}
|
|
|
|
unsigned int linphone_presence_model_get_nb_services(const LinphonePresenceModel *model) {
|
|
return (unsigned int)bctbx_list_size(model->services);
|
|
}
|
|
|
|
LinphonePresenceService * linphone_presence_model_get_nth_service(const LinphonePresenceModel *model, unsigned int idx) {
|
|
if ((model == NULL) || (idx >= linphone_presence_model_get_nb_services(model)))
|
|
return NULL;
|
|
|
|
return (LinphonePresenceService *)bctbx_list_nth_data(model->services, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_add_service(LinphonePresenceModel *model, LinphonePresenceService *service) {
|
|
if ((model == NULL) || (service == NULL)) return -1;
|
|
model->services = bctbx_list_append(model->services, linphone_presence_service_ref(service));
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_clear_services(LinphonePresenceModel *model) {
|
|
if (model == NULL) return -1;
|
|
|
|
bctbx_list_for_each(model->services, (MSIterateFunc)linphone_presence_service_unref);
|
|
bctbx_list_free(model->services);
|
|
model->services = NULL;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int linphone_presence_model_get_nb_persons(const LinphonePresenceModel *model) {
|
|
return (unsigned int)bctbx_list_size(model->persons);
|
|
}
|
|
|
|
LinphonePresencePerson * linphone_presence_model_get_nth_person(const LinphonePresenceModel *model, unsigned int idx) {
|
|
if ((model == NULL) || (idx >= linphone_presence_model_get_nb_persons(model)))
|
|
return NULL;
|
|
|
|
return (LinphonePresencePerson *)bctbx_list_nth_data(model->persons, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_add_person(LinphonePresenceModel *model, LinphonePresencePerson *person) {
|
|
if ((model == NULL) || (person == NULL)) return -1;
|
|
presence_model_add_person(model, person);
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_clear_persons(LinphonePresenceModel *model) {
|
|
if (model == NULL) return -1;
|
|
|
|
bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_unref);
|
|
bctbx_list_free(model->persons);
|
|
model->persons = NULL;
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_model_set_presentity(LinphonePresenceModel *model, const LinphoneAddress *presentity) {
|
|
|
|
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;
|
|
}
|
|
|
|
const LinphoneAddress * linphone_presence_model_get_presentity(const LinphonePresenceModel *model) {
|
|
return model->presentity;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
BELLE_SIP_INSTANCIATE_VPTR(
|
|
LinphonePresenceModel,
|
|
belle_sip_object_t,
|
|
presence_model_uninit, // destroy
|
|
NULL, // clone
|
|
NULL, // marshal
|
|
FALSE // unown
|
|
);
|
|
|
|
/*****************************************************************************
|
|
* PRESENCE SERVICE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
|
****************************************************************************/
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
LinphonePresenceService * linphone_presence_service_new(const char *id, LinphonePresenceBasicStatus basic_status, const char *contact) {
|
|
LinphonePresenceService *service;
|
|
char *service_id;
|
|
if (id == NULL)
|
|
service_id = generate_presence_id();
|
|
else
|
|
service_id = ms_strdup(id);
|
|
service = presence_service_new(service_id, basic_status);
|
|
linphone_presence_service_set_contact(service, contact);
|
|
if (service_id != NULL)
|
|
ms_free(service_id);
|
|
return service;
|
|
}
|
|
|
|
char * linphone_presence_service_get_id(const LinphonePresenceService *service) {
|
|
if (service == NULL) return NULL;
|
|
return ms_strdup(service->id);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_service_set_id(LinphonePresenceService *service, const char *id) {
|
|
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;
|
|
}
|
|
|
|
LinphonePresenceBasicStatus linphone_presence_service_get_basic_status(const LinphonePresenceService *service) {
|
|
if (service == NULL) return LinphonePresenceBasicStatusClosed;
|
|
return service->status;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_service_set_basic_status(LinphonePresenceService *service, LinphonePresenceBasicStatus basic_status) {
|
|
if (service == NULL) return -1;
|
|
service->status = basic_status;
|
|
return 0;
|
|
}
|
|
|
|
char * linphone_presence_service_get_contact(const LinphonePresenceService *service) {
|
|
if (service->contact == NULL) return NULL;
|
|
return ms_strdup(service->contact);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_service_set_contact(LinphonePresenceService *service, const char *contact) {
|
|
if (service == NULL) return -1;
|
|
if (service->contact != NULL)
|
|
ms_free(service->contact);
|
|
if (contact != NULL)
|
|
service->contact = ms_strdup(contact);
|
|
else
|
|
service->contact = NULL;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int linphone_presence_service_get_nb_notes(const LinphonePresenceService *service) {
|
|
return (unsigned int)bctbx_list_size(service->notes);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_service_get_nth_note(const LinphonePresenceService *service, unsigned int idx) {
|
|
if ((service == NULL) || (idx >= linphone_presence_service_get_nb_notes(service)))
|
|
return NULL;
|
|
|
|
return (LinphonePresenceNote *)bctbx_list_nth_data(service->notes, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_service_add_note(LinphonePresenceService *service, LinphonePresenceNote *note) {
|
|
if ((service == NULL) || (note == NULL)) return -1;
|
|
service->notes = bctbx_list_append(service->notes, linphone_presence_note_ref(note));
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_service_clear_notes(LinphonePresenceService *service) {
|
|
if (service == NULL) return -1;
|
|
|
|
bctbx_list_for_each(service->notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(service->notes);
|
|
service->notes = NULL;
|
|
return 0;
|
|
}
|
|
|
|
BELLE_SIP_INSTANCIATE_VPTR(
|
|
LinphonePresenceService,
|
|
belle_sip_object_t,
|
|
presence_service_uninit, // destroy
|
|
NULL, // clone
|
|
NULL, // marshal
|
|
FALSE // unown
|
|
);
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* 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);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_set_id(LinphonePresencePerson *person, const char *id) {
|
|
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;
|
|
}
|
|
|
|
unsigned int linphone_presence_person_get_nb_activities(const LinphonePresencePerson *person) {
|
|
if (person == NULL) return 0;
|
|
return (unsigned int)bctbx_list_size(person->activities);
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_person_get_nth_activity(const LinphonePresencePerson *person, unsigned int idx) {
|
|
if ((person == NULL) || (idx >= linphone_presence_person_get_nb_activities(person)))
|
|
return NULL;
|
|
return (LinphonePresenceActivity *)bctbx_list_nth_data(person->activities, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_add_activity(LinphonePresencePerson *person, LinphonePresenceActivity *activity) {
|
|
if ((person == NULL) || (activity == NULL)) return -1;
|
|
// insert in first position since its the most recent activity!
|
|
person->activities = bctbx_list_prepend(person->activities, linphone_presence_activity_ref(activity));
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_clear_activities(LinphonePresencePerson *person) {
|
|
if (person == NULL) return -1;
|
|
bctbx_list_for_each(person->activities, (MSIterateFunc)linphone_presence_activity_unref);
|
|
bctbx_list_free(person->activities);
|
|
person->activities = NULL;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int linphone_presence_person_get_nb_notes(const LinphonePresencePerson *person) {
|
|
if (person == NULL) return 0;
|
|
return (unsigned int)bctbx_list_size(person->notes);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_person_get_nth_note(const LinphonePresencePerson *person, unsigned int idx) {
|
|
if ((person == NULL) || (idx >= linphone_presence_person_get_nb_notes(person)))
|
|
return NULL;
|
|
return (LinphonePresenceNote *)bctbx_list_nth_data(person->notes, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_add_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
|
|
if ((person == NULL) || (note == NULL)) return -1;
|
|
person->notes = bctbx_list_append(person->notes, linphone_presence_note_ref(note));
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_clear_notes(LinphonePresencePerson *person) {
|
|
if (person == NULL) return -1;
|
|
bctbx_list_for_each(person->notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(person->notes);
|
|
person->notes = NULL;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int linphone_presence_person_get_nb_activities_notes(const LinphonePresencePerson *person) {
|
|
if (person == NULL) return 0;
|
|
return (unsigned int)bctbx_list_size(person->activities_notes);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_person_get_nth_activities_note(const LinphonePresencePerson *person, unsigned int idx) {
|
|
if ((person == NULL) || (idx >= linphone_presence_person_get_nb_activities_notes(person)))
|
|
return NULL;
|
|
return (LinphonePresenceNote *)bctbx_list_nth_data(person->activities_notes, (int)idx);
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_add_activities_note(LinphonePresencePerson *person, LinphonePresenceNote *note) {
|
|
if ((person == NULL) || (note == NULL)) return -1;
|
|
person->notes = bctbx_list_append(person->activities_notes, linphone_presence_note_ref(note));
|
|
return 0;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_person_clear_activities_notes(LinphonePresencePerson *person) {
|
|
if (person == NULL) return -1;
|
|
bctbx_list_for_each(person->activities_notes, (MSIterateFunc)linphone_presence_note_unref);
|
|
bctbx_list_free(person->activities_notes);
|
|
person->activities_notes = NULL;
|
|
return 0;
|
|
}
|
|
|
|
BELLE_SIP_INSTANCIATE_VPTR(
|
|
LinphonePresencePerson,
|
|
belle_sip_object_t,
|
|
presence_person_uninit, // destroy
|
|
NULL, // clone
|
|
NULL, // marshal
|
|
FALSE // unown
|
|
)
|
|
|
|
|
|
/*****************************************************************************
|
|
* PRESENCE ACTIVITY FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
|
****************************************************************************/
|
|
|
|
struct _presence_activity_name_map {
|
|
const char *name;
|
|
LinphonePresenceActivityType type;
|
|
};
|
|
|
|
static struct _presence_activity_name_map activity_map[] = {
|
|
{ "appointment", LinphonePresenceActivityAppointment },
|
|
{ "away", LinphonePresenceActivityAway },
|
|
{ "breakfast", LinphonePresenceActivityBreakfast },
|
|
{ "busy", LinphonePresenceActivityBusy },
|
|
{ "dinner", LinphonePresenceActivityDinner },
|
|
{ "holiday", LinphonePresenceActivityHoliday },
|
|
{ "in-transit", LinphonePresenceActivityInTransit },
|
|
{ "looking-for-work", LinphonePresenceActivityLookingForWork },
|
|
{ "lunch", LinphonePresenceActivityLunch },
|
|
{ "meal", LinphonePresenceActivityMeal },
|
|
{ "meeting", LinphonePresenceActivityMeeting },
|
|
{ "on-the-phone", LinphonePresenceActivityOnThePhone },
|
|
{ "other", LinphonePresenceActivityOther },
|
|
{ "performance", LinphonePresenceActivityPerformance },
|
|
{ "permanent-absence", LinphonePresenceActivityPermanentAbsence },
|
|
{ "playing", LinphonePresenceActivityPlaying },
|
|
{ "presentation", LinphonePresenceActivityPresentation },
|
|
{ "shopping", LinphonePresenceActivityShopping },
|
|
{ "sleeping", LinphonePresenceActivitySleeping },
|
|
{ "spectator", LinphonePresenceActivitySpectator },
|
|
{ "steering", LinphonePresenceActivitySteering },
|
|
{ "travel", LinphonePresenceActivityTravel },
|
|
{ "tv", LinphonePresenceActivityTV },
|
|
{ "unknown", LinphonePresenceActivityUnknown },
|
|
{ "vacation", LinphonePresenceActivityVacation },
|
|
{ "working", LinphonePresenceActivityWorking },
|
|
{ "worship", LinphonePresenceActivityWorship }
|
|
};
|
|
|
|
static int activity_name_to_presence_activity_type(const char *name, LinphonePresenceActivityType *acttype) {
|
|
unsigned int i;
|
|
for (i = 0; i < (sizeof(activity_map) / sizeof(activity_map[0])); i++) {
|
|
if (strcmp(name, activity_map[i].name) == 0) {
|
|
*acttype = activity_map[i].type;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static const char * presence_activity_type_to_string(LinphonePresenceActivityType acttype) {
|
|
unsigned int i;
|
|
for (i = 0; i < (sizeof(activity_map) / sizeof(activity_map[0])); i++) {
|
|
if (acttype == activity_map[i].type) {
|
|
return activity_map[i].name;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_activity_new(LinphonePresenceActivityType acttype, const char *description) {
|
|
LinphonePresenceActivity *act = belle_sip_object_new(LinphonePresenceActivity);
|
|
act->type = acttype;
|
|
if (description != NULL) {
|
|
act->description = ms_strdup(description);
|
|
}
|
|
return act;
|
|
}
|
|
|
|
char * linphone_presence_activity_to_string(const LinphonePresenceActivity *activity) {
|
|
LinphonePresenceActivityType acttype = linphone_presence_activity_get_type(activity);
|
|
const char *description = linphone_presence_activity_get_description(activity);
|
|
const char *acttype_str = presence_activity_type_to_string(acttype);
|
|
return ms_strdup_printf("%s%s%s", acttype_str,
|
|
(description == NULL) ? "" : ": ",
|
|
(description == NULL) ? "" : description);
|
|
}
|
|
|
|
LinphonePresenceActivityType linphone_presence_activity_get_type(const LinphonePresenceActivity *activity) {
|
|
return activity->type;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_activity_set_type(LinphonePresenceActivity *activity, LinphonePresenceActivityType acttype) {
|
|
if (activity == NULL) return -1;
|
|
activity->type = acttype;
|
|
return 0;
|
|
}
|
|
|
|
const char * linphone_presence_activity_get_description(const LinphonePresenceActivity *activity) {
|
|
if (activity == NULL)
|
|
return NULL;
|
|
return activity->description;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_activity_set_description(LinphonePresenceActivity *activity, const char *description) {
|
|
if (activity == NULL) return -1;
|
|
if (activity->description != NULL)
|
|
ms_free(activity->description);
|
|
if (description != NULL)
|
|
activity->description = ms_strdup(description);
|
|
else
|
|
activity->description = NULL;
|
|
return 0;
|
|
}
|
|
|
|
BELLE_SIP_INSTANCIATE_VPTR(
|
|
LinphonePresenceActivity,
|
|
belle_sip_object_t, // parent
|
|
presence_activity_uninit, // destroy
|
|
NULL, // clone
|
|
NULL, // marshal
|
|
FALSE // unown
|
|
);
|
|
|
|
|
|
/*****************************************************************************
|
|
* PRESENCE NOTE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES *
|
|
****************************************************************************/
|
|
|
|
LinphonePresenceNote * linphone_presence_note_new(const char *content, const char *lang) {
|
|
LinphonePresenceNote *note;
|
|
|
|
if (content == NULL) return NULL;
|
|
note = belle_sip_object_new(LinphonePresenceNote);
|
|
note->content = ms_strdup(content);
|
|
if (lang != NULL) {
|
|
note->lang = ms_strdup(lang);
|
|
}
|
|
return note;
|
|
}
|
|
|
|
const char * linphone_presence_note_get_content(const LinphonePresenceNote *note) {
|
|
if (note == NULL)
|
|
return NULL;
|
|
return note->content;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_note_set_content(LinphonePresenceNote *note, const char *content) {
|
|
if (content == NULL) return -1;
|
|
if (note->content != NULL) {
|
|
ms_free(note->content);
|
|
}
|
|
note->content = ms_strdup(content);
|
|
return 0;
|
|
}
|
|
|
|
const char * linphone_presence_note_get_lang(const LinphonePresenceNote *note) {
|
|
if (note == NULL)
|
|
return NULL;
|
|
return note->lang;
|
|
}
|
|
|
|
LinphoneStatus linphone_presence_note_set_lang(LinphonePresenceNote *note, const char *lang) {
|
|
if (note->lang != NULL) {
|
|
ms_free(note->lang);
|
|
note->lang = NULL;
|
|
}
|
|
if (lang != NULL) {
|
|
note->lang = ms_strdup(lang);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BELLE_SIP_INSTANCIATE_VPTR(
|
|
LinphonePresenceNote,
|
|
belle_sip_object_t, // parent
|
|
presence_note_uninit, // destroy
|
|
NULL, // clone
|
|
NULL, // marshal
|
|
FALSE // unown
|
|
)
|
|
|
|
|
|
/*****************************************************************************
|
|
* PRESENCE INTERNAL FUNCTIONS FOR WRAPPERS IN OTHER PROGRAMMING LANGUAGES *
|
|
****************************************************************************/
|
|
|
|
LinphonePresenceModel * linphone_presence_model_ref(LinphonePresenceModel *model) {
|
|
return (LinphonePresenceModel *)belle_sip_object_ref(model);
|
|
}
|
|
|
|
LinphonePresenceModel * linphone_presence_model_unref(LinphonePresenceModel *model) {
|
|
LinphonePresenceModel *returned_model = model->base.ref > 1 ? model : NULL;
|
|
belle_sip_object_unref(model);
|
|
return returned_model;
|
|
}
|
|
|
|
void linphone_presence_model_set_user_data(LinphonePresenceModel *model, void *user_data) {
|
|
model->user_data = user_data;
|
|
}
|
|
|
|
void * linphone_presence_model_get_user_data(const LinphonePresenceModel *model) {
|
|
return model->user_data;
|
|
}
|
|
|
|
LinphonePresenceService * linphone_presence_service_ref(LinphonePresenceService *service) {
|
|
return (LinphonePresenceService *)belle_sip_object_ref(service);
|
|
}
|
|
|
|
LinphonePresenceService * linphone_presence_service_unref(LinphonePresenceService *service) {
|
|
LinphonePresenceService *returned_service = service->base.ref > 1 ? service : NULL;
|
|
belle_sip_object_unref(service);
|
|
return returned_service;
|
|
}
|
|
|
|
void linphone_presence_service_set_user_data(LinphonePresenceService *service, void *user_data) {
|
|
service->user_data = user_data;
|
|
}
|
|
|
|
void * linphone_presence_service_get_user_data(const LinphonePresenceService *service) {
|
|
return service->user_data;
|
|
}
|
|
|
|
LinphonePresencePerson * linphone_presence_person_ref(LinphonePresencePerson *person) {
|
|
return (LinphonePresencePerson *)belle_sip_object_ref(person);
|
|
}
|
|
|
|
LinphonePresencePerson * linphone_presence_person_unref(LinphonePresencePerson *person) {
|
|
LinphonePresencePerson *returned_person = person->base.ref > 1 ? person : NULL;
|
|
belle_sip_object_unref(person);
|
|
return returned_person;
|
|
}
|
|
|
|
void linphone_presence_person_set_user_data(LinphonePresencePerson *person, void *user_data) {
|
|
person->user_data = user_data;
|
|
}
|
|
|
|
void * linphone_presence_person_get_user_data(const LinphonePresencePerson *person) {
|
|
return person->user_data;
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_activity_ref(LinphonePresenceActivity *activity) {
|
|
return (LinphonePresenceActivity *)belle_sip_object_ref(activity);
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_presence_activity_unref(LinphonePresenceActivity *activity) {
|
|
LinphonePresenceActivity *returned_activity = activity->base.ref > 1 ? activity : NULL;
|
|
belle_sip_object_unref(activity);
|
|
return returned_activity;
|
|
}
|
|
|
|
void linphone_presence_activity_set_user_data(LinphonePresenceActivity *activity, void *user_data) {
|
|
activity->user_data = user_data;
|
|
}
|
|
|
|
void * linphone_presence_activity_get_user_data(const LinphonePresenceActivity *activity) {
|
|
return activity->user_data;
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_note_ref(LinphonePresenceNote *note) {
|
|
return (LinphonePresenceNote *)belle_sip_object_ref(note);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_presence_note_unref(LinphonePresenceNote *note) {
|
|
LinphonePresenceNote *returned_note = note->base.ref > 1 ? note : NULL;
|
|
belle_sip_object_unref(note);
|
|
return returned_note;
|
|
}
|
|
|
|
void linphone_presence_note_set_user_data(LinphonePresenceNote *note, void *user_data) {
|
|
note->user_data = user_data;
|
|
}
|
|
|
|
void * linphone_presence_note_get_user_data(const LinphonePresenceNote *note) {
|
|
return note->user_data;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* XML PRESENCE INTERNAL HANDLING *
|
|
****************************************************************************/
|
|
|
|
static const char *service_prefix = "/pidf:presence/pidf:tuple";
|
|
|
|
static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx, LinphonePresenceService *service, unsigned int service_idx) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr note_object;
|
|
LinphonePresenceNote *note;
|
|
char *note_str;
|
|
char *lang;
|
|
int i;
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note", service_prefix, service_idx);
|
|
note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
|
|
if ((note_object != NULL) && (note_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= note_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]", service_prefix, service_idx, i);
|
|
note_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (note_str == NULL) continue;
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]/@xml:lang", service_prefix, service_idx, i);
|
|
lang = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
note = linphone_presence_note_new(note_str, lang);
|
|
presence_service_add_note(service, note);
|
|
if (lang != NULL) linphone_free_xml_text_content(lang);
|
|
linphone_free_xml_text_content(note_str);
|
|
}
|
|
}
|
|
if (note_object != NULL) xmlXPathFreeObject(note_object);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr service_object;
|
|
xmlXPathObjectPtr pidfonline_object;
|
|
LinphonePresenceService *service;
|
|
char *basic_status_str;
|
|
char *service_id_str;
|
|
char *timestamp_str;
|
|
char *contact_str;
|
|
LinphonePresenceBasicStatus basic_status;
|
|
int i;
|
|
|
|
service_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, service_prefix);
|
|
if ((service_object != NULL) && (service_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= service_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:status/pidf:basic", service_prefix, i);
|
|
basic_status_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (basic_status_str == NULL)
|
|
continue;
|
|
|
|
if (strcmp(basic_status_str, "open") == 0) {
|
|
basic_status = LinphonePresenceBasicStatusOpen;
|
|
} else if (strcmp(basic_status_str, "closed") == 0) {
|
|
basic_status = LinphonePresenceBasicStatusClosed;
|
|
} else {
|
|
/* Invalid value for basic status. */
|
|
linphone_free_xml_text_content(basic_status_str);
|
|
return -1;
|
|
}
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:status/pidfonline:online", service_prefix, i);
|
|
if ((pidfonline_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str)) != NULL
|
|
&& pidfonline_object->nodesetval
|
|
&& pidfonline_object->nodesetval->nodeNr >0) {
|
|
model->is_online = TRUE;
|
|
}
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:timestamp", service_prefix, i);
|
|
timestamp_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:contact", service_prefix, i);
|
|
contact_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/@id", service_prefix, i);
|
|
service_id_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
service = presence_service_new(service_id_str, basic_status);
|
|
if (service != NULL) {
|
|
if (timestamp_str != NULL) presence_service_set_timestamp(service, parse_timestamp(timestamp_str));
|
|
if (contact_str != NULL) linphone_presence_service_set_contact(service, contact_str);
|
|
process_pidf_xml_presence_service_notes(xml_ctx, service, (unsigned int)i);
|
|
linphone_presence_model_add_service(model, service);
|
|
linphone_presence_service_unref(service);
|
|
}
|
|
if (timestamp_str != NULL) linphone_free_xml_text_content(timestamp_str);
|
|
if (contact_str != NULL) linphone_free_xml_text_content(contact_str);
|
|
if (service_id_str != NULL) linphone_free_xml_text_content(service_id_str);
|
|
linphone_free_xml_text_content(basic_status_str);
|
|
}
|
|
}
|
|
if (service_object != NULL) xmlXPathFreeObject(service_object);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool_t is_valid_activity_name(const char *name) {
|
|
unsigned int i;
|
|
for (i = 0; i < (sizeof(activity_map) / sizeof(activity_map[0])); i++) {
|
|
if (strcmp(name, activity_map[i].name) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int process_pidf_xml_presence_person_activities(xmlparsing_context_t *xml_ctx, LinphonePresencePerson *person, unsigned int person_idx) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr activities_nodes_object;
|
|
xmlXPathObjectPtr activities_object;
|
|
xmlNodePtr activity_node;
|
|
LinphonePresenceActivity *activity;
|
|
char *description;
|
|
int i, j;
|
|
int err = 0;
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities", person_prefix, person_idx);
|
|
activities_nodes_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
|
|
if ((activities_nodes_object != NULL) && (activities_nodes_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= activities_nodes_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities[%i]/rpid:*", person_prefix, person_idx, i);
|
|
activities_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
|
|
if ((activities_object != NULL) && (activities_object->nodesetval != NULL)) {
|
|
for (j = 0; j < activities_object->nodesetval->nodeNr; j++) {
|
|
activity_node = activities_object->nodesetval->nodeTab[j];
|
|
if ((activity_node->name != NULL) && (is_valid_activity_name((const char *)activity_node->name) == TRUE)) {
|
|
LinphonePresenceActivityType acttype;
|
|
description = (char *)xmlNodeGetContent(activity_node);
|
|
if ((description != NULL) && (description[0] == '\0')) {
|
|
linphone_free_xml_text_content(description);
|
|
description = NULL;
|
|
}
|
|
err = activity_name_to_presence_activity_type((const char *)activity_node->name, &acttype);
|
|
if (err < 0) break;
|
|
activity = linphone_presence_activity_new(acttype, description);
|
|
linphone_presence_person_add_activity(person, activity);
|
|
linphone_presence_activity_unref(activity);
|
|
if (description != NULL) linphone_free_xml_text_content(description);
|
|
}
|
|
}
|
|
}
|
|
if (activities_object != NULL) xmlXPathFreeObject(activities_object);
|
|
if (err < 0) break;
|
|
}
|
|
}
|
|
if (activities_nodes_object != NULL) xmlXPathFreeObject(activities_nodes_object);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int process_pidf_xml_presence_person_notes(xmlparsing_context_t *xml_ctx, LinphonePresencePerson *person, unsigned int person_idx) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr note_object;
|
|
LinphonePresenceNote *note;
|
|
char *note_str;
|
|
char *lang;
|
|
int i;
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note", person_prefix, person_idx);
|
|
note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
|
|
if ((note_object != NULL) && (note_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= note_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note[%i]", person_prefix, person_idx, i);
|
|
note_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (note_str == NULL) continue;
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note[%i]/@xml:lang", person_prefix, person_idx, i);
|
|
lang = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
note = linphone_presence_note_new(note_str, lang);
|
|
presence_person_add_activities_note(person, note);
|
|
if (lang != NULL) linphone_free_xml_text_content(lang);
|
|
linphone_free_xml_text_content(note_str);
|
|
}
|
|
}
|
|
if (note_object != NULL) xmlXPathFreeObject(note_object);
|
|
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note", person_prefix, person_idx);
|
|
note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
|
|
if ((note_object != NULL) && (note_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= note_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note[%i]", person_prefix, person_idx, i);
|
|
note_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (note_str == NULL) continue;
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/dm:note[%i]/@xml:lang", person_prefix, person_idx, i);
|
|
lang = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
note = linphone_presence_note_new(note_str, lang);
|
|
presence_person_add_note(person, note);
|
|
if (lang != NULL) linphone_free_xml_text_content(lang);
|
|
linphone_free_xml_text_content(note_str);
|
|
}
|
|
}
|
|
if (note_object != NULL) xmlXPathFreeObject(note_object);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int process_pidf_xml_presence_persons(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr person_object;
|
|
LinphonePresencePerson *person;
|
|
char *person_id_str;
|
|
char *person_timestamp_str;
|
|
time_t timestamp;
|
|
int i;
|
|
int err = 0;
|
|
|
|
person_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, person_prefix);
|
|
if ((person_object != NULL) && (person_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= person_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/@id", person_prefix, i);
|
|
person_id_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:timestamp", person_prefix, i);
|
|
person_timestamp_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (person_timestamp_str == NULL)
|
|
timestamp = time(NULL);
|
|
else
|
|
timestamp = parse_timestamp(person_timestamp_str);
|
|
person = presence_person_new(person_id_str, timestamp);
|
|
|
|
if (person != NULL) {
|
|
err = process_pidf_xml_presence_person_activities(xml_ctx, person, (unsigned int)i);
|
|
if (err == 0) {
|
|
err = process_pidf_xml_presence_person_notes(xml_ctx, person, (unsigned int)i);
|
|
}
|
|
if (err == 0) {
|
|
presence_model_add_person(model, person);
|
|
linphone_presence_person_unref(person);
|
|
} else {
|
|
linphone_presence_person_unref(person);
|
|
break;
|
|
}
|
|
}
|
|
if (person_id_str != NULL) linphone_free_xml_text_content(person_id_str);
|
|
if (person_timestamp_str != NULL) linphone_free_xml_text_content(person_timestamp_str);
|
|
}
|
|
}
|
|
if (person_object != NULL) xmlXPathFreeObject(person_object);
|
|
|
|
if (err < 0) {
|
|
/* Remove all the persons added since there was an error. */
|
|
bctbx_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_unref);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int process_pidf_xml_presence_notes(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) {
|
|
char xpath_str[MAX_XPATH_LENGTH];
|
|
xmlXPathObjectPtr note_object;
|
|
LinphonePresenceNote *note;
|
|
char *note_str;
|
|
char *lang;
|
|
int i;
|
|
|
|
note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/pidf:presence/pidf:note");
|
|
if ((note_object != NULL) && (note_object->nodesetval != NULL)) {
|
|
for (i = 1; i <= note_object->nodesetval->nodeNr; i++) {
|
|
snprintf(xpath_str, sizeof(xpath_str), "/pidf:presence/pidf:note[%i]", i);
|
|
note_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
if (note_str == NULL) continue;
|
|
snprintf(xpath_str, sizeof(xpath_str), "/pidf:presence/pidf:note[%i]/@xml:lang", i);
|
|
lang = linphone_get_xml_text_content(xml_ctx, xpath_str);
|
|
|
|
note = linphone_presence_note_new(note_str, lang);
|
|
presence_model_add_note(model, note);
|
|
if (lang != NULL) linphone_free_xml_text_content(lang);
|
|
linphone_free_xml_text_content(note_str);
|
|
}
|
|
}
|
|
if (note_object != NULL) xmlXPathFreeObject(note_object);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LinphonePresenceModel * process_pidf_xml_presence_notification(xmlparsing_context_t *xml_ctx) {
|
|
LinphonePresenceModel *model = NULL;
|
|
int err;
|
|
|
|
if (linphone_create_xml_xpath_context(xml_ctx) < 0)
|
|
return NULL;
|
|
|
|
model = linphone_presence_model_new();
|
|
xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"pidf", (const xmlChar *)"urn:ietf:params:xml:ns:pidf");
|
|
xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"dm", (const xmlChar *)"urn:ietf:params:xml:ns:pidf:data-model");
|
|
xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rpid", (const xmlChar *)"urn:ietf:params:xml:ns:pidf:rpid");
|
|
xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"pidfonline", (const xmlChar *)"http://www.linphone.org/xsds/pidfonline.xsd");
|
|
err = process_pidf_xml_presence_services(xml_ctx, model);
|
|
if (err == 0) {
|
|
err = process_pidf_xml_presence_persons(xml_ctx, model);
|
|
}
|
|
if (err == 0) {
|
|
err = process_pidf_xml_presence_notes(xml_ctx, model);
|
|
}
|
|
|
|
if (err < 0) {
|
|
linphone_presence_model_unref(model);
|
|
model = NULL;
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|
|
|
|
|
|
|
|
void linphone_core_add_subscriber(LinphoneCore *lc, const char *subscriber, SalOp *op){
|
|
LinphoneFriend *fl=linphone_core_create_friend_with_address(lc,subscriber);
|
|
const LinphoneAddress *addr;
|
|
char *tmp;
|
|
|
|
if (fl==NULL) return ;
|
|
fl->lc = lc;
|
|
linphone_friend_add_incoming_subscription(fl, op);
|
|
linphone_friend_set_inc_subscribe_policy(fl,LinphoneSPAccept);
|
|
fl->inc_subscribe_pending=TRUE;
|
|
/* the newly created "not yet" friend ownership is transfered to the lc->subscribers list*/
|
|
lc->subscribers=bctbx_list_append(lc->subscribers,fl);
|
|
|
|
addr = linphone_friend_get_address(fl);
|
|
if (addr != NULL) {
|
|
tmp = linphone_address_as_string(addr);
|
|
linphone_core_notify_new_subscription_requested(lc,fl,tmp);
|
|
ms_free(tmp);
|
|
}
|
|
}
|
|
|
|
void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf){
|
|
linphone_friend_set_inc_subscribe_policy(lf,LinphoneSPDeny);
|
|
}
|
|
|
|
void linphone_core_notify_all_friends(LinphoneCore *lc, LinphonePresenceModel *presence){
|
|
char *activity_str;
|
|
LinphonePresenceActivity *activity = linphone_presence_model_get_activity(presence);
|
|
if (activity == NULL) {
|
|
activity_str = linphone_presence_basic_status_to_string(linphone_presence_model_get_basic_status(presence));
|
|
} else {
|
|
activity_str = linphone_presence_activity_to_string(activity);
|
|
}
|
|
LinphoneFriendList *lfl = linphone_core_get_default_friend_list(lc);
|
|
ms_message("Notifying all friends that we are [%s]", activity_str);
|
|
if (activity_str != NULL) ms_free(activity_str);
|
|
|
|
if (lfl) {
|
|
linphone_friend_list_notify_presence(lfl, presence);
|
|
} else {
|
|
ms_error("Default friend list is null, skipping...");
|
|
}
|
|
}
|
|
|
|
void linphone_subscription_new(LinphoneCore *lc, SalSubscribeOp *op, const char *from){
|
|
LinphoneFriend *lf=NULL;
|
|
char *tmp;
|
|
LinphoneAddress *uri;
|
|
|
|
uri=linphone_address_new(from);
|
|
linphone_address_clean(uri);
|
|
tmp=linphone_address_as_string(uri);
|
|
ms_message("Receiving new subscription from %s.",from);
|
|
|
|
/* check if we answer to this subscription */
|
|
lf = linphone_core_find_friend(lc, uri);
|
|
if (lf!=NULL){
|
|
if (lf->pol != LinphoneSPDeny) {
|
|
linphone_friend_add_incoming_subscription(lf, op);
|
|
lf->inc_subscribe_pending=TRUE;
|
|
if (lp_config_get_int(lc->config,"sip","notify_pending_state",0)) {
|
|
op->notifyPendingState();
|
|
}
|
|
op->accept();
|
|
} else {
|
|
ms_message("%s is not authorized to subscribe", from);
|
|
op->decline(SalReasonDeclined);
|
|
}
|
|
linphone_friend_done(lf); /*this will do all necessary actions */
|
|
}else{
|
|
/* check if this subscriber is in our black list */
|
|
if (linphone_find_friend_by_address(lc->subscribers,uri,&lf)){
|
|
if (lf->pol==LinphoneSPDeny){
|
|
ms_message("Rejecting %s because we already rejected it once.",from);
|
|
op->decline(SalReasonDeclined);
|
|
}
|
|
else {
|
|
/* else it is in wait for approval state, because otherwise it is in the friend list.*/
|
|
ms_message("New subscriber found in subscriber list, in %s state.",__policy_enum_to_str(lf->pol));
|
|
}
|
|
}else {
|
|
op->accept();
|
|
linphone_core_add_subscriber(lc,tmp,op);
|
|
}
|
|
}
|
|
linphone_address_unref(uri);
|
|
ms_free(tmp);
|
|
}
|
|
|
|
void linphone_notify_parse_presence(const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
|
|
xmlparsing_context_t *xml_ctx;
|
|
LinphonePresenceModel *model = NULL;
|
|
|
|
if (strcmp(content_type, "application") != 0) {
|
|
*result = NULL;
|
|
return;
|
|
}
|
|
|
|
if (strcmp(content_subtype, "pidf+xml") == 0) {
|
|
xml_ctx = linphone_xmlparsing_context_new();
|
|
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
|
|
xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0);
|
|
if (xml_ctx->doc != NULL) {
|
|
model = process_pidf_xml_presence_notification(xml_ctx);
|
|
} else {
|
|
ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer);
|
|
}
|
|
linphone_xmlparsing_context_destroy(xml_ctx);
|
|
} else {
|
|
ms_error("Unknown content type '%s/%s' for presence", content_type, content_subtype);
|
|
}
|
|
|
|
*result = (SalPresenceModel *)model;
|
|
}
|
|
|
|
struct _presence_service_obj_st {
|
|
xmlTextWriterPtr writer;
|
|
const char *contact;
|
|
bool_t online;
|
|
int *err;
|
|
};
|
|
|
|
struct _presence_person_obj_st {
|
|
xmlTextWriterPtr writer;
|
|
int *err;
|
|
};
|
|
|
|
struct _presence_activity_obj_st {
|
|
xmlTextWriterPtr writer;
|
|
int *err;
|
|
};
|
|
|
|
struct _presence_note_obj_st {
|
|
xmlTextWriterPtr writer;
|
|
const char *ns;
|
|
int *err;
|
|
};
|
|
|
|
static int write_xml_presence_note(xmlTextWriterPtr writer, LinphonePresenceNote *note, const char *ns) {
|
|
int err;
|
|
if (ns == NULL) {
|
|
err = xmlTextWriterStartElement(writer, (const xmlChar *)"note");
|
|
} else {
|
|
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)ns, (const xmlChar *)"note", NULL);
|
|
}
|
|
if ((err >= 0) && (note->lang != NULL)) {
|
|
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xml", (const xmlChar *)"lang", NULL, (const xmlChar *)note->lang);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterWriteString(writer, (const xmlChar *)note->content);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void write_xml_presence_note_obj(LinphonePresenceNote *note, struct _presence_note_obj_st *st) {
|
|
int err = write_xml_presence_note(st->writer, note, st->ns);
|
|
if (err < 0) *st->err = err;
|
|
}
|
|
|
|
static int write_xml_presence_timestamp(xmlTextWriterPtr writer, time_t timestamp) {
|
|
int err;
|
|
char *timestamp_str = linphone_timestamp_to_rfc3339_string(timestamp);
|
|
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"timestamp", (const xmlChar *)timestamp_str);
|
|
if (timestamp_str) ms_free(timestamp_str);
|
|
return err;
|
|
}
|
|
|
|
static int write_xml_presence_service(xmlTextWriterPtr writer, LinphonePresenceService *service, const char *contact, bool_t online) {
|
|
int err = xmlTextWriterStartElement(writer, (const xmlChar *)"tuple");
|
|
if (err >= 0) {
|
|
if ((service == NULL) || (service->id == NULL)) {
|
|
char *text = generate_presence_id();
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"id", (const xmlChar *)text);
|
|
if (text != NULL) ms_free(text);
|
|
} else {
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"id", (const xmlChar *)service->id);
|
|
}
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterStartElement(writer, (const xmlChar *)"status");
|
|
}
|
|
if (err >= 0) {
|
|
char *basic_status_str;
|
|
LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed;
|
|
if (service != NULL) basic_status = service->status;
|
|
basic_status_str = linphone_presence_basic_status_to_string(basic_status);
|
|
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"basic", (const xmlChar *)basic_status_str);
|
|
ms_free(basic_status_str);
|
|
}
|
|
if (online) {
|
|
if (err >= 0) {
|
|
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"pidfonline", (const xmlChar *)"online", NULL);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "status" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterStartElement(writer, (const xmlChar *)"contact");
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"priority", (const xmlChar *)"0.8");
|
|
}
|
|
if (err >= 0) {
|
|
const char *contact_str;
|
|
if ((service == NULL) || (service->contact == NULL))
|
|
contact_str = contact;
|
|
else
|
|
contact_str = service->contact;
|
|
err = xmlTextWriterWriteString(writer, (const xmlChar *)contact_str);
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "contact" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
if ((err >= 0) && (service != NULL) && (service->notes != NULL)) {
|
|
struct _presence_note_obj_st st;
|
|
st.writer = writer;
|
|
st.ns = NULL;
|
|
st.err = &err;
|
|
bctbx_list_for_each2(service->notes, (MSIterate2Func)write_xml_presence_note_obj, &st);
|
|
}
|
|
if (err >= 0) {
|
|
if (service == NULL)
|
|
err = write_xml_presence_timestamp(writer, time(NULL));
|
|
else
|
|
err = write_xml_presence_timestamp(writer, service->timestamp);
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "tuple" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int write_xml_presence_activity(xmlTextWriterPtr writer, LinphonePresenceActivity *activity) {
|
|
int err;
|
|
|
|
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"rpid",
|
|
(const xmlChar *)presence_activity_type_to_string(activity->type), NULL);
|
|
if ((err >= 0) && (activity->description != NULL)) {
|
|
err = xmlTextWriterWriteString(writer, (const xmlChar *)activity->description);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void write_xml_presence_activity_obj(LinphonePresenceActivity *activity, struct _presence_activity_obj_st *st) {
|
|
int err = write_xml_presence_activity(st->writer, activity);
|
|
if (err < 0) *st->err = err;
|
|
}
|
|
|
|
static int write_xml_presence_person(xmlTextWriterPtr writer, LinphonePresencePerson *person) {
|
|
int err;
|
|
|
|
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"dm", (const xmlChar *)"person", NULL);
|
|
if (err >= 0) {
|
|
if (person->id == NULL) {
|
|
char *text = generate_presence_id();
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"id", (const xmlChar *)text);
|
|
if (text != NULL) ms_free(text);
|
|
} else {
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"id", (const xmlChar *)person->id);
|
|
}
|
|
}
|
|
if ((err >= 0) && ((person->activities_notes != NULL) || (person->activities != NULL))) {
|
|
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"rpid", (const xmlChar *)"activities", NULL);
|
|
if ((err >= 0) && (person->activities_notes != NULL)) {
|
|
struct _presence_note_obj_st st;
|
|
st.writer = writer;
|
|
st.ns = "rpid";
|
|
st.err = &err;
|
|
bctbx_list_for_each2(person->activities_notes, (MSIterate2Func)write_xml_presence_note_obj, &st);
|
|
}
|
|
if ((err >= 0) && (person->activities != NULL)) {
|
|
struct _presence_activity_obj_st st;
|
|
st.writer = writer;
|
|
st.err = &err;
|
|
bctbx_list_for_each2(person->activities, (MSIterate2Func)write_xml_presence_activity_obj, &st);
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "activities" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
}
|
|
if ((err >= 0) && (person->notes != NULL)) {
|
|
struct _presence_note_obj_st st;
|
|
st.writer = writer;
|
|
st.ns = "dm";
|
|
st.err = &err;
|
|
bctbx_list_for_each2(person->notes, (MSIterate2Func)write_xml_presence_note_obj, &st);
|
|
}
|
|
if (err >= 0) {
|
|
write_xml_presence_timestamp(writer, person->timestamp);
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "person" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void write_xml_presence_service_obj(LinphonePresenceService *service, struct _presence_service_obj_st *st) {
|
|
int err = write_xml_presence_service(st->writer, service, st->contact, st->online);
|
|
if (err < 0) *st->err = err;
|
|
}
|
|
|
|
static void write_xml_presence_person_obj(LinphonePresencePerson *person, struct _presence_person_obj_st *st) {
|
|
int err = write_xml_presence_person(st->writer, person);
|
|
if (err < 0) *st->err = err;
|
|
}
|
|
|
|
bool_t linphone_presence_model_is_online(const LinphonePresenceModel *model) {
|
|
if ((model->is_online == TRUE)
|
|
|| ((linphone_presence_model_get_basic_status(model) == LinphonePresenceBasicStatusOpen)
|
|
&& (linphone_presence_model_get_nb_activities(model) == 0)))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
char *linphone_presence_model_to_xml(LinphonePresenceModel *model) {
|
|
xmlBufferPtr buf = NULL;
|
|
xmlTextWriterPtr writer = NULL;
|
|
int err;
|
|
char *contact = NULL;
|
|
char * content = NULL;
|
|
|
|
if (model->presentity) {
|
|
contact = linphone_address_as_string_uri_only(model->presentity);
|
|
} else {
|
|
ms_error("Cannot convert presence model [%p] to xml because no presentity set", model);
|
|
goto end;
|
|
}
|
|
buf = xmlBufferCreate();
|
|
if (buf == NULL) {
|
|
ms_error("Error creating the XML buffer");
|
|
goto end;
|
|
}
|
|
writer = xmlNewTextWriterMemory(buf, 0);
|
|
if (writer == NULL) {
|
|
ms_error("Error creating the XML writer");
|
|
goto end;
|
|
}
|
|
|
|
xmlTextWriterSetIndent(writer,1);
|
|
|
|
err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
|
|
if (err >= 0) {
|
|
err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"presence", (const xmlChar *)"urn:ietf:params:xml:ns:pidf");
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"dm",
|
|
NULL, (const xmlChar *)"urn:ietf:params:xml:ns:pidf:data-model");
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"rpid",
|
|
NULL, (const xmlChar *)"urn:ietf:params:xml:ns:pidf:rpid");
|
|
}
|
|
if ((err >= 0) && linphone_presence_model_is_online(model)) {
|
|
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"pidfonline",
|
|
NULL, (const xmlChar *)"http://www.linphone.org/xsds/pidfonline.xsd");
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"entity", (const xmlChar *)contact);
|
|
}
|
|
if (err >= 0) {
|
|
if ((model == NULL) || (model->services == NULL)) {
|
|
err = write_xml_presence_service(writer, NULL, contact, FALSE);
|
|
} else {
|
|
struct _presence_service_obj_st st={0};
|
|
st.writer = writer;
|
|
st.contact = contact; /*default value*/
|
|
st.online = linphone_presence_model_is_online(model);
|
|
st.err = &err;
|
|
bctbx_list_for_each2(model->services, (MSIterate2Func)write_xml_presence_service_obj, &st);
|
|
}
|
|
}
|
|
if ((err >= 0) && (model != NULL)) {
|
|
struct _presence_person_obj_st st={0};
|
|
st.writer = writer;
|
|
st.err = &err;
|
|
bctbx_list_for_each2(model->persons, (MSIterate2Func)write_xml_presence_person_obj, &st);
|
|
}
|
|
if ((err >= 0) && (model != NULL)) {
|
|
struct _presence_note_obj_st st={0};
|
|
st.writer = writer;
|
|
st.ns = NULL;
|
|
st.err = &err;
|
|
bctbx_list_for_each2(model->notes, (MSIterate2Func)write_xml_presence_note_obj, &st);
|
|
}
|
|
if (err >= 0) {
|
|
/* Close the "presence" element. */
|
|
err = xmlTextWriterEndElement(writer);
|
|
}
|
|
if (err >= 0) {
|
|
err = xmlTextWriterEndDocument(writer);
|
|
}
|
|
if (err > 0) {
|
|
/* xmlTextWriterEndDocument returns the size of the content. */
|
|
content = ms_strdup((char *)buf->content);
|
|
}
|
|
|
|
end:
|
|
if (contact) ms_free(contact);
|
|
if (writer) xmlFreeTextWriter(writer);
|
|
if (buf) xmlBufferFree(buf);
|
|
return content;
|
|
}
|
|
|
|
void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model){
|
|
char *tmp;
|
|
LinphoneFriend *lf = NULL;
|
|
const LinphoneAddress *lfa=NULL;
|
|
LinphonePresenceModel *presence;
|
|
|
|
if (model != NULL) {
|
|
presence = (LinphonePresenceModel *)model;
|
|
} else {
|
|
presence = linphone_presence_model_new();
|
|
linphone_presence_model_set_basic_status(presence, LinphonePresenceBasicStatusClosed);
|
|
}
|
|
|
|
if (linphone_core_get_default_friend_list(lc) != NULL)
|
|
lf=linphone_core_find_friend_by_out_subscribe(lc, op);
|
|
if (lf==NULL && lp_config_get_int(lc->config,"sip","allow_out_of_subscribe_presence",0)){
|
|
char *buf = sal_address_as_string_uri_only(op->getFromAddress());
|
|
LinphoneAddress *addr = linphone_address_new(buf);
|
|
lf = linphone_core_find_friend(lc, addr);
|
|
ms_free(buf);
|
|
linphone_address_unref(addr);
|
|
}
|
|
if (lf!=NULL){
|
|
LinphonePresenceActivity *activity = NULL;
|
|
char *activity_str;
|
|
activity = linphone_presence_model_get_activity(presence);
|
|
lfa=linphone_friend_get_address(lf);
|
|
if (lfa != NULL) {
|
|
tmp=linphone_address_as_string(lfa);
|
|
if (activity == NULL) {
|
|
activity_str = linphone_presence_basic_status_to_string(linphone_presence_model_get_basic_status(presence));
|
|
} else {
|
|
activity_str = linphone_presence_activity_to_string(activity);
|
|
}
|
|
ms_message("We are notified that [%s] has presence [%s]", tmp, activity_str);
|
|
if (activity_str != NULL) ms_free(activity_str);
|
|
ms_free(tmp);
|
|
}
|
|
linphone_friend_set_presence_model(lf, presence);
|
|
lf->subscribe_active=TRUE;
|
|
lf->presence_received = TRUE;
|
|
lf->out_sub_state = linphone_subscription_state_from_sal(ss);
|
|
linphone_core_notify_notify_presence_received(lc,(LinphoneFriend*)lf);
|
|
if (op != lf->outsub){
|
|
/*case of a NOTIFY received out of any dialog*/
|
|
op->release();
|
|
return;
|
|
}
|
|
}else{
|
|
ms_message("But this person is not part of our friend list, so we don't care.");
|
|
linphone_presence_model_unref(presence);
|
|
op->release();
|
|
return ;
|
|
}
|
|
if (ss==SalSubscribeTerminated){
|
|
if (lf){
|
|
if (lf->outsub != op){
|
|
op->release();
|
|
}
|
|
if (lf->outsub){
|
|
lf->outsub->release();
|
|
lf->outsub=NULL;
|
|
}
|
|
lf->subscribe_active=FALSE;
|
|
}else{
|
|
op->release();
|
|
}
|
|
}
|
|
}
|
|
|
|
void linphone_subscription_closed(LinphoneCore *lc, SalOp *op){
|
|
LinphoneFriend *lf = NULL;
|
|
|
|
lf = linphone_core_find_friend_by_inc_subscribe(lc, op);
|
|
|
|
if (lf!=NULL){
|
|
/*this will release the op*/
|
|
linphone_friend_remove_incoming_subscription(lf, op);
|
|
}else{
|
|
/*case of an op that we already released because the friend was destroyed*/
|
|
ms_message("Receiving unsuscribe for unknown in-subscribtion from %s", op->getFrom());
|
|
}
|
|
}
|
|
|
|
LinphonePresenceActivity * linphone_core_create_presence_activity(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description) {
|
|
return linphone_presence_activity_new(acttype, description);
|
|
}
|
|
|
|
LinphonePresenceModel * linphone_core_create_presence_model(LinphoneCore *lc) {
|
|
return linphone_presence_model_new();
|
|
}
|
|
|
|
LinphonePresenceModel * linphone_core_create_presence_model_with_activity(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description) {
|
|
return linphone_presence_model_new_with_activity(acttype, description);
|
|
}
|
|
|
|
LinphonePresenceModel * linphone_core_create_presence_model_with_activity_and_note(LinphoneCore *lc, LinphonePresenceActivityType acttype, const char *description, const char *note, const char *lang) {
|
|
return linphone_presence_model_new_with_activity_and_note(acttype, description, note, lang);
|
|
}
|
|
|
|
LinphonePresenceNote * linphone_core_create_presence_note(LinphoneCore *lc, const char *content, const char *lang) {
|
|
return linphone_presence_note_new(content, lang);
|
|
}
|
|
|
|
LinphonePresencePerson * linphone_core_create_presence_person(LinphoneCore *lc, const char *id) {
|
|
return linphone_presence_person_new(id);
|
|
}
|
|
|
|
LinphonePresenceService * linphone_core_create_presence_service(LinphoneCore *lc, const char *id, LinphonePresenceBasicStatus basic_status, const char *contact) {
|
|
return linphone_presence_service_new(id, basic_status, contact);
|
|
}
|