diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 8184a3df0..183fe92f0 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -16,7 +16,7 @@ CLEANFILES=$(GITVERSION_FILE) ## Process this file with automake to produce Makefile.in linphone_includedir=$(includedir)/linphone -linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonecore_utils.h lpconfig.h sipsetup.h event.h +linphone_include_HEADERS=linphonecore.h linphonefriend.h linphonepresence.h linphonecore_utils.h lpconfig.h sipsetup.h event.h if BUILD_TUNNEL linphone_include_HEADERS+=linphone_tunnel.h diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 1583871e1..61cc949fd 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -486,6 +486,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ ctx->callbacks.subscribe_received=(SalOnSubscribeReceived)unimplemented_stub; if (ctx->callbacks.subscribe_closed==NULL) ctx->callbacks.subscribe_closed=(SalOnSubscribeClosed)unimplemented_stub; + if (ctx->callbacks.parse_presence_requested==NULL) + ctx->callbacks.parse_presence_requested=(SalOnParsePresenceRequested)unimplemented_stub; if (ctx->callbacks.notify_presence==NULL) ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub; if (ctx->callbacks.subscribe_presence_received==NULL) diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index af321069b..4b4476b40 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -497,6 +497,26 @@ static void presence_process_transaction_terminated(void *user_ctx, const belle_ ms_message("presence_process_transaction_terminated not implemented yet"); } +static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_request_t *req) { + belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_type_t); + belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_length_t); + const char *body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); + SalPresenceModel *result = NULL; + + if ((content_type == NULL) || (content_length == NULL)) + return NULL; + if (belle_sip_header_content_length_get_content_length(content_length) == 0) + return NULL; + + op->base.root->callbacks.parse_presence_requested(op, + belle_sip_header_content_type_get_type(content_type), + belle_sip_header_content_type_get_subtype(content_type), + body, + &result); + + return result; +} + static void presence_process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event)); @@ -505,7 +525,7 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t); belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t); const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)); - SalPresenceStatus estatus=SalPresenceOffline; + SalPresenceModel *presence_model = NULL; SalSubscribeStatus sub_state; belle_sip_response_t* resp; belle_sip_object_ref(server_transaction); @@ -536,41 +556,22 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques ms_error("No body in NOTIFY received from [%s]",sal_op_get_from(op)); return; } - if (strstr(body,"pending")!=NULL){ - estatus=SalPresenceOffline; - }else if (strstr(body,"busy")!=NULL){ - estatus=SalPresenceBusy; - }else if (strstr(body,"berightback")!=NULL - || strstr(body,"in-transit")!=NULL ){ - estatus=SalPresenceBerightback; - }else if (strstr(body,"away")!=NULL - || strstr(body,"idle")){ - estatus=SalPresenceAway; - }else if (strstr(body,"onthephone")!=NULL - || strstr(body,"on-the-phone")!=NULL){ - estatus=SalPresenceOnthephone; - }else if (strstr(body,"outtolunch")!=NULL - || strstr(body,"lunch")!=NULL - || strstr(body,"meal")!=NULL){ - estatus=SalPresenceOuttolunch; - }else if (strstr(body,"closed")!=NULL){ - estatus=SalPresenceOffline; - }else if ((strstr(body,"online")!=NULL) || (strstr(body,"open")!=NULL)) { - estatus=SalPresenceOnline; - }else if((strstr(body,"vacation")!=NULL)) { - estatus = SalPresenceOnVacation; - }else{ - estatus=SalPresenceOffline; + presence_model = process_presence_notification(op, req); + if (presence_model != NULL) { + /* Presence notification body parsed successfully. */ + if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { + sub_state=SalSubscribeTerminated; + ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); + } else { + sub_state=SalSubscribeActive; + } + op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL); + resp = sal_op_create_response_from_request(op, req, 200); + } else { + /* Formatting error in presence notification body. */ + ms_error("Wrongly formatted presence notification received"); + resp = sal_op_create_response_from_request(op, req, 400); } - ms_message("We are notified that [%s] has online status [%s]",sal_op_get_to(op),sal_presence_status_to_string(estatus)); - if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { - sub_state=SalSubscribeTerminated; - ms_message("And outgoing subscription terminated by remote [%s]",sal_op_get_to(op)); - } else - sub_state=SalSubscribeActive; - - op->base.root->callbacks.notify_presence(op,sub_state, estatus,NULL); - resp=sal_op_create_response_from_request(op,req,200); belle_sip_server_transaction_send_response(server_transaction,resp); } else if (strcmp("SUBSCRIBE",belle_sip_request_get_method(req))==0) { /*either a refresh of an unsubscribe*/ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index f8ef9dfba..6d7e6d58e 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -877,9 +877,13 @@ static void text_received(SalOp *op, const SalMessage *msg){ } } -static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){ +static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { + linphone_notify_parse_presence(op, content_type, content_subtype, body, result); +} + +static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - linphone_notify_recv(lc,op,ss,status); + linphone_notify_recv(lc,op,ss,model); } static void subscribe_presence_received(SalOp *op, const char *from){ @@ -1085,6 +1089,7 @@ SalCallbacks linphone_sal_callbacks={ notify, subscribe_presence_received, subscribe_presence_closed, + parse_presence_requested, notify_presence, ping_reply, auth_requested, diff --git a/coreapi/friend.c b/coreapi/friend.c index 8bd252aed..df66ada73 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -119,7 +119,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ - fr->status=LinphoneStatusOffline; + fr->presence=NULL; /* if (fr->lc->vtable.notify_recv) fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); @@ -138,7 +138,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ LinphoneFriend * linphone_friend_new(){ LinphoneFriend *obj=ms_new0(LinphoneFriend,1); obj->pol=LinphoneSPAccept; - obj->status=LinphoneStatusOffline; + obj->presence=NULL; obj->subscribe=TRUE; return obj; } @@ -304,6 +304,7 @@ void linphone_friend_destroy(LinphoneFriend *lf){ sal_op_release(lf->outsub); lf->outsub=NULL; } + if (lf->presence != NULL) linphone_presence_model_delete(lf->presence); if (lf->uri!=NULL) linphone_address_destroy(lf->uri); if (lf->info!=NULL) buddy_info_free(lf->info); ms_free(lf); @@ -322,7 +323,90 @@ LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneF } LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){ - return lf->status; + LinphoneOnlineStatus online_status = LinphoneStatusOffline; + LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed; + LinphonePresenceActivity activity = LinphonePresenceActivityUnknown; + char *activity_description = NULL; + unsigned int nb_activities = 0; + int err = 0; + + if (lf->presence != NULL) { + basic_status = linphone_presence_model_get_basic_status(lf->presence); + nb_activities = linphone_presence_model_nb_activities(lf->presence); + online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline; + if (nb_activities > 1) { + char *tmp = NULL; + const LinphoneAddress *addr = linphone_friend_get_address(lf); + if (addr) tmp = linphone_address_as_string(addr); + ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown"); + if (tmp) ms_free(tmp); + nb_activities = 1; + } + if (nb_activities == 1) { + err = linphone_presence_model_get_activity(lf->presence, 0, &activity, &activity_description); + if (err == 0) { + switch (activity) { + case LinphonePresenceActivityBreakfast: + case LinphonePresenceActivityDinner: + case LinphonePresenceActivityLunch: + case LinphonePresenceActivityMeal: + online_status = LinphoneStatusOutToLunch; + break; + case LinphonePresenceActivityAppointment: + case LinphonePresenceActivityMeeting: + case LinphonePresenceActivityPerformance: + case LinphonePresenceActivityPresentation: + case LinphonePresenceActivitySpectator: + case LinphonePresenceActivityWorking: + case LinphonePresenceActivityWorship: + online_status = LinphoneStatusDoNotDisturb; + break; + case LinphonePresenceActivityAway: + case LinphonePresenceActivitySleeping: + online_status = LinphoneStatusAway; + break; + case LinphonePresenceActivityHoliday: + case LinphonePresenceActivityTravel: + case LinphonePresenceActivityVacation: + online_status = LinphoneStatusVacation; + break; + case LinphonePresenceActivityBusy: + case LinphonePresenceActivityLookingForWork: + case LinphonePresenceActivityPlaying: + case LinphonePresenceActivityShopping: + case LinphonePresenceActivityTV: + online_status = LinphoneStatusBusy; + break; + case LinphonePresenceActivityInTransit: + case LinphonePresenceActivitySteering: + online_status = LinphoneStatusBeRightBack; + break; + case LinphonePresenceActivityOnThePhone: + online_status = LinphoneStatusOnThePhone; + break; + case LinphonePresenceActivityOther: + case LinphonePresenceActivityPermanentAbsence: + online_status = LinphoneStatusMoved; + break; + case LinphonePresenceActivityUnknown: + break; + } + } + } + } + + return online_status; +} + +LinphonePresenceModel * linphone_friend_get_presence(LinphoneFriend *lf) { + return lf->presence; +} + +void linphone_friend_set_presence(LinphoneFriend *lf, LinphonePresenceModel *model) { + if (lf->presence != NULL) { + linphone_presence_model_delete(lf->presence); + } + lf->presence = model; } BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h index cd5e74a48..e01a5fe44 100644 --- a/coreapi/linphonefriend.h +++ b/coreapi/linphonefriend.h @@ -19,9 +19,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef LINPHONEFRIEND_H_ #define LINPHONEFRIEND_H_ + +#include "linphonepresence.h" + #ifdef __cplusplus extern "C" { #endif + /** * @addtogroup buddy_list * @{ @@ -206,6 +210,10 @@ LINPHONE_PUBLIC void linphone_friend_done(LinphoneFriend *fr); * @return #LinphoneOnlineStatus */ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf); + +LinphonePresenceModel * linphone_friend_get_presence(LinphoneFriend *lf); +void linphone_friend_set_presence(LinphoneFriend *lf, LinphonePresenceModel *presence); + BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf); void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key); const char *linphone_friend_get_ref_key(const LinphoneFriend *lf); diff --git a/coreapi/linphonepresence.h b/coreapi/linphonepresence.h new file mode 100644 index 000000000..317e25b2e --- /dev/null +++ b/coreapi/linphonepresence.h @@ -0,0 +1,80 @@ +/* +linphonepresence.h +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef LINPHONEPRESENCE_H_ +#define LINPHONEPRESENCE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Basic status as defined in section 4.1.4 of RFC 3863 */ +typedef enum LinphonePresenceBasicStatus { + LinphonePresenceBasicStatusOpen, + LinphonePresenceBasicStatusClosed +} LinphonePresenceBasicStatus; + +/** Activities as defined in section 3.2 of RFC 4480 */ +typedef enum LinphonePresenceActivity { + LinphonePresenceActivityAppointment, + LinphonePresenceActivityAway, + LinphonePresenceActivityBreakfast, + LinphonePresenceActivityBusy, + LinphonePresenceActivityDinner, + LinphonePresenceActivityHoliday, + LinphonePresenceActivityInTransit, + LinphonePresenceActivityLookingForWork, + LinphonePresenceActivityLunch, + LinphonePresenceActivityMeal, + LinphonePresenceActivityMeeting, + LinphonePresenceActivityOnThePhone, + LinphonePresenceActivityOther, + LinphonePresenceActivityPerformance, + LinphonePresenceActivityPermanentAbsence, + LinphonePresenceActivityPlaying, + LinphonePresenceActivityPresentation, + LinphonePresenceActivityShopping, + LinphonePresenceActivitySleeping, + LinphonePresenceActivitySpectator, + LinphonePresenceActivitySteering, + LinphonePresenceActivityTravel, + LinphonePresenceActivityTV, + LinphonePresenceActivityUnknown, + LinphonePresenceActivityVacation, + LinphonePresenceActivityWorking, + LinphonePresenceActivityWorship +} LinphonePresenceActivity; + +struct _LinphonePresenceModel; +typedef struct _LinphonePresenceModel LinphonePresenceModel; + + +LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new(void); +LINPHONE_PUBLIC void linphone_presence_model_delete(LinphonePresenceModel *model); +LINPHONE_PUBLIC LinphonePresenceBasicStatus linphone_presence_model_get_basic_status(const LinphonePresenceModel *model); +LINPHONE_PUBLIC unsigned int linphone_presence_model_nb_activities(const LinphonePresenceModel *model); +LINPHONE_PUBLIC int linphone_presence_model_get_activity(const LinphonePresenceModel *model, unsigned int idx, LinphonePresenceActivity *activity, char **description); + + +#ifdef __cplusplus +} +#endif + +#endif /* LINPHONEPRESENCE_H_ */ diff --git a/coreapi/presence.c b/coreapi/presence.c index 2eb11ff64..9ad01bc07 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -19,11 +19,744 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #include "private.h" +#include +#include +#include + + +#define XMLPARSING_BUFFER_LEN 2048 +#define MAX_XPATH_LENGTH 256 + extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol); + +struct _LinphonePresenceNote { + char *lang; + char *content; +}; + +struct _LinphonePresenceService { + char *id; + LinphonePresenceBasicStatus status; +}; + +struct _LinphonePresenceActivity { + LinphonePresenceActivity activity; + char *description; +}; + +struct _LinphonePresencePerson { + char *id; + MSList *activities; /**< A list of _LinphonePresenceActivity structures. */ + MSList *activities_notes; /**< A list of _LinphonePresenceNote structures. */ + MSList *notes; /**< A list of _LinphonePresenceNote structures. */ + time_t timestamp; +}; + +/** + * 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 { + MSList *services; /**< A list of _LinphonePresenceService structures. Also named tuples in the RFC. */ + MSList *persons; /**< A list of _LinphonePresencePerson structures. */ + MSList *notes; /**< A list of _LinphonePresenceNote structures. */ +}; + +typedef struct _xmlparsing_context { + xmlDoc *doc; + xmlXPathContextPtr xpath_ctx; + char errorBuffer[XMLPARSING_BUFFER_LEN]; + char warningBuffer[XMLPARSING_BUFFER_LEN]; +} xmlparsing_context_t; + + + +static xmlparsing_context_t * xmlparsing_context_new() { + xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)malloc(sizeof(xmlparsing_context_t)); + if (xmlCtx != NULL) { + xmlCtx->doc = NULL; + xmlCtx->xpath_ctx = NULL; + xmlCtx->errorBuffer[0] = '\0'; + xmlCtx->warningBuffer[0] = '\0'; + } + return xmlCtx; +} + +static void xmlparsing_context_destroy(xmlparsing_context_t *ctx) { + if (ctx->doc != NULL) { + xmlFreeDoc(ctx->doc); + ctx->doc = NULL; + } + if (ctx->xpath_ctx != NULL) { + xmlXPathFreeContext(ctx->xpath_ctx); + ctx->xpath_ctx = NULL; + } + free(ctx); +} + +static void xmlparsing_genericxml_error(void *ctx, const char *fmt, ...) { + xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)ctx; + int sl = strlen(xmlCtx->errorBuffer); + va_list args; + va_start(args, fmt); + vsnprintf(xmlCtx->errorBuffer + sl, XMLPARSING_BUFFER_LEN - sl, fmt, args); + va_end(args); +} + +static struct _LinphonePresenceNote * presence_note_new(const char *content, const char *lang) { + struct _LinphonePresenceNote * note = ms_new0(struct _LinphonePresenceNote, 1); + note->content = ms_strdup(content); + if (lang != NULL) { + note->lang = ms_strdup(lang); + } + return note; +} + +static void presence_note_delete(struct _LinphonePresenceNote *note) { + ms_free(note->content); + if (note->lang != NULL) { + ms_free(note->lang); + } + ms_free(note); +} + +static struct _LinphonePresenceService * presence_service_new(const char *id, LinphonePresenceBasicStatus status) { + struct _LinphonePresenceService *service = ms_new0(struct _LinphonePresenceService, 1); + if (id != NULL) { + service->id = ms_strdup(id); + } + service->status = status; + return service; +} + +static void presence_service_delete(struct _LinphonePresenceService *service) { + if (service->id != NULL) { + ms_free(service->id); + } + ms_free(service); +}; + +static struct _LinphonePresenceActivity * presence_activity_new(LinphonePresenceActivity activity, const char *description) { + struct _LinphonePresenceActivity *act = ms_new0(struct _LinphonePresenceActivity, 1); + act->activity = activity; + if (description != NULL) { + act->description = ms_strdup(description); + } + return act; +} + +static void presence_activity_delete(struct _LinphonePresenceActivity *activity) { + if (activity->description != NULL) { + ms_free(activity->description); + } + ms_free(activity); +} + +static time_t parse_timestamp(const char *timestamp) { + struct tm ret; + time_t seconds; + + 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; + } + return seconds - timezone; +} + +static struct _LinphonePresencePerson * presence_person_new(const char *id, const char *timestamp) { + struct _LinphonePresencePerson *person = ms_new0(struct _LinphonePresencePerson, 1); + if (id != NULL) { + person->id = ms_strdup(id); + } + if (timestamp != NULL) { + person->timestamp = parse_timestamp(timestamp); + if (person->timestamp == ((time_t)-1)) + person->timestamp = time(NULL); + } else { + person->timestamp = time(NULL); + } + return person; +} + +static void presence_person_delete(struct _LinphonePresencePerson *person) { + if (person->id != NULL) { + ms_free(person->id); + } + ms_list_for_each(person->activities, (MSIterateFunc)presence_activity_delete); + ms_list_free(person->activities); + ms_list_for_each(person->activities_notes, (MSIterateFunc)presence_note_delete); + ms_list_free(person->activities_notes); + ms_list_for_each(person->notes, (MSIterateFunc)presence_note_delete); + ms_list_free(person->notes); + ms_free(person); +} + +static void presence_person_add_activity(struct _LinphonePresencePerson *person, struct _LinphonePresenceActivity *activity) { + person->activities = ms_list_append(person->activities, activity); +} + +static void presence_person_add_activities_note(struct _LinphonePresencePerson *person, struct _LinphonePresenceNote *note) { + person->activities_notes = ms_list_append(person->activities_notes, note); +} + +static void presence_person_add_note(struct _LinphonePresencePerson *person, struct _LinphonePresenceNote *note) { + person->notes = ms_list_append(person->notes, note); +} + +static void presence_model_add_service(LinphonePresenceModel *model, struct _LinphonePresenceService *service) { + model->services = ms_list_append(model->services, service); +} + +static void presence_model_add_person(LinphonePresenceModel *model, struct _LinphonePresencePerson *person) { + model->persons = ms_list_append(model->persons, person); +} + +static void presence_model_add_note(LinphonePresenceModel *model, struct _LinphonePresenceNote *note) { + model->notes = ms_list_append(model->notes, note); +} + +static void presence_model_find_open_basic_status(struct _LinphonePresenceService *service, LinphonePresenceBasicStatus *status) { + if (service->status == LinphonePresenceBasicStatusOpen) { + *status = LinphonePresenceBasicStatusOpen; + } +} + +LinphonePresenceModel * linphone_presence_model_new(void) { + return ms_new0(LinphonePresenceModel, 1); +} + +void linphone_presence_model_delete(LinphonePresenceModel *model) { + ms_list_for_each(model->services, (MSIterateFunc)presence_service_delete); + ms_list_free(model->services); + ms_list_for_each(model->persons, (MSIterateFunc)presence_person_delete); + ms_list_free(model->persons); + ms_list_for_each(model->notes, (MSIterateFunc)presence_note_delete); + ms_list_free(model->notes); + ms_free(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; + ms_list_for_each2(model->services, (MSIterate2Func)presence_model_find_open_basic_status, &status); + return status; +} + +static void presence_model_count_activities(const struct _LinphonePresencePerson *person, unsigned int *nb) { + *nb += ms_list_size(person->activities); +} + +unsigned int linphone_presence_model_nb_activities(const LinphonePresenceModel *model) { + unsigned int nb = 0; + ms_list_for_each2(model->persons, (MSIterate2Func)presence_model_count_activities, &nb); + return nb; +} + +struct _get_activity_st { + unsigned int requested_idx; + unsigned int current_idx; + LinphonePresenceActivity *activity; + char **description; +}; + +static void presence_model_get_activity(const struct _LinphonePresencePerson *person, struct _get_activity_st *st) { + struct _LinphonePresenceActivity *activity; + unsigned int size = ms_list_size(person->activities); + if (st->requested_idx < (st->current_idx + size)) { + activity = (struct _LinphonePresenceActivity *)ms_list_nth_data(person->activities, st->requested_idx - st->current_idx); + *st->activity = activity->activity; + if (st->description != NULL) { + *st->description = activity->description; + } + } else { + st->current_idx += size; + } +} + +int linphone_presence_model_get_activity(const LinphonePresenceModel *model, unsigned int idx, LinphonePresenceActivity *activity, char **description) { + struct _get_activity_st st; + if ((activity == NULL) || (idx >= linphone_presence_model_nb_activities(model))) + return -1; + memset(&st, 0, sizeof(st)); + st.requested_idx = idx; + st.activity = activity; + *st.activity = LinphonePresenceActivityUnknown; + if (description != NULL) { + st.description = description; + } + ms_list_for_each2(model->persons, (MSIterate2Func)presence_model_get_activity, &st); + return 0; +} + +static int create_xml_xpath_context(xmlparsing_context_t *xml_ctx) { + if (xml_ctx->xpath_ctx != NULL) { + xmlXPathFreeContext(xml_ctx->xpath_ctx); + } + xml_ctx->xpath_ctx = xmlXPathNewContext(xml_ctx->doc); + if (xml_ctx->xpath_ctx == NULL) return -1; + return 0; +} + +static char * get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { + xmlXPathObjectPtr xpath_obj; + xmlChar *text = NULL; + int i; + + xpath_obj = xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); + if (xpath_obj != NULL) { + if (xpath_obj->nodesetval != NULL) { + xmlNodeSetPtr nodes = xpath_obj->nodesetval; + for (i = 0; i < nodes->nodeNr; i++) { + xmlNodePtr node = nodes->nodeTab[i]; + if (node->children != NULL) { + text = xmlNodeListGetString(xml_ctx->doc, node->children, 1); + } + } + } + xmlXPathFreeObject(xpath_obj); + } + + return (char *)text; +} + +static void free_xml_text_content(const char *text) { + xmlFree((xmlChar *)text); +} + +static xmlXPathObjectPtr get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression) { + return xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx); +} + +static int process_rfcxxxx_presence_notification(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) { + LinphonePresenceBasicStatus basic_status; + struct _LinphonePresenceService *service; + struct _LinphonePresencePerson *person; + struct _LinphonePresenceActivity *activity = NULL; + char *status_text = NULL; + char *substatus_text = NULL; + + if (create_xml_xpath_context(xml_ctx) < 0) + return -1; + status_text = get_xml_text_content(xml_ctx, "/presence/atom/address/status/@status"); + if (status_text == NULL) + return -1; + substatus_text = get_xml_text_content(xml_ctx, "/presence/atom/address/msnsubstatus/@substatus"); + if (substatus_text == NULL) { + free_xml_text_content(status_text); + return -1; + } + + if (strcmp(status_text, "open") == 0) { + basic_status = LinphonePresenceBasicStatusOpen; + if (strcmp(substatus_text, "berightback") == 0) { + activity = presence_activity_new(LinphonePresenceActivityInTransit, NULL); + } else if (strcmp(substatus_text, "away") == 0) { + activity = presence_activity_new(LinphonePresenceActivityAway, NULL); + } else if (strcmp(substatus_text, "outtolunch") == 0) { + activity = presence_activity_new(LinphonePresenceActivityMeal, NULL); + } + } else if (strcmp(status_text, "inuse") == 0) { + basic_status = LinphonePresenceBasicStatusOpen; + if (strcmp(substatus_text, "busy") == 0) { + activity = presence_activity_new(LinphonePresenceActivityBusy, NULL); + } else if (strcmp(substatus_text, "onthephone") == 0) { + activity = presence_activity_new(LinphonePresenceActivityOnThePhone, NULL); + } + } else if (strcmp(status_text, "closed") == 0) { + basic_status = LinphonePresenceBasicStatusClosed; + } + service = presence_service_new(NULL, basic_status); + if (service != NULL) { + presence_model_add_service(model, service); + } + if (activity != NULL) { + person = presence_person_new(NULL, NULL); + if (person != NULL) { + presence_person_add_activity(person, activity); + presence_model_add_person(model, person); + } + } + free_xml_text_content(status_text); + free_xml_text_content(substatus_text); + + return 0; +} + +static int process_msoldpres_presence_notification(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) { + LinphonePresenceBasicStatus basic_status; + struct _LinphonePresenceService *service; + struct _LinphonePresencePerson *person; + struct _LinphonePresenceActivity *activity = NULL; + char *status_text = NULL; + char *substatus_text = NULL; + + if (create_xml_xpath_context(xml_ctx) < 0) + return -1; + status_text = get_xml_text_content(xml_ctx, "/presence/atom/address/status/@status"); + if (status_text == NULL) + return -1; + substatus_text = get_xml_text_content(xml_ctx, "/presence/atom/address/msnsubstatus/@substatus"); + if (substatus_text == NULL) { + free_xml_text_content(status_text); + return -1; + } + + if (strcmp(status_text, "open") == 0) { + basic_status = LinphonePresenceBasicStatusOpen; + } else if (strcmp(status_text, "inuse") == 0) { + basic_status = LinphonePresenceBasicStatusOpen; + if (strcmp(substatus_text, "busy") == 0) { + activity = presence_activity_new(LinphonePresenceActivityBusy, NULL); + } else if (strcmp(substatus_text, "onthephone") == 0) { + activity = presence_activity_new(LinphonePresenceActivityOnThePhone, NULL); + } + } else if (strcmp(status_text, "inactive") == 0) { + basic_status = LinphonePresenceBasicStatusOpen; + if (strcmp(substatus_text, "berightback") == 0) { + activity = presence_activity_new(LinphonePresenceActivityInTransit, NULL); + } else if (strcmp(substatus_text, "idle") == 0) { + activity = presence_activity_new(LinphonePresenceActivityAway, NULL); + } else if (strcmp(substatus_text, "outtolunch") == 0) { + activity = presence_activity_new(LinphonePresenceActivityMeal, NULL); + } + } else if (strcmp(status_text, "closed") == 0) { + basic_status = LinphonePresenceBasicStatusClosed; + } + service = presence_service_new(NULL, basic_status); + if (service != NULL) { + presence_model_add_service(model, service); + } + if (activity != NULL) { + person = presence_person_new(NULL, NULL); + if (person != NULL) { + presence_person_add_activity(person, activity); + presence_model_add_person(model, person); + } + } + + return 0; +} + +static const char *service_prefix = "/pidf:presence/pidf:tuple"; + +static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, LinphonePresenceModel *model) { + char xpath_str[MAX_XPATH_LENGTH]; + xmlXPathObjectPtr service_object; + struct _LinphonePresenceService *service; + const char *basic_status_str; + const char *service_id_str; + LinphonePresenceBasicStatus basic_status; + int i; + + service_object = 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 = 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. */ + free_xml_text_content(basic_status_str); + return -1; + } + + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/@id", service_prefix, i); + service_id_str = get_xml_text_content(xml_ctx, xpath_str); + service = presence_service_new(service_id_str, basic_status); + if (service != NULL) { + presence_model_add_service(model, service); + } + free_xml_text_content(basic_status_str); + if (service_id_str != NULL) free_xml_text_content(service_id_str); + } + } + if (service_object != NULL) xmlXPathFreeObject(service_object); + + return 0; +} + +static const char *person_prefix = "/pidf:presence/dm:person"; + +struct _presence_activity_name_map { + const char *name; + LinphonePresenceActivity activity; +}; + +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_linphone_presence_activity(const char *name, LinphonePresenceActivity *activity) { + unsigned int i; + for (i = 0; i < (sizeof(activity_map) / sizeof(activity_map[0])); i++) { + if (strcmp(name, activity_map[i].name) == 0) { + *activity = activity_map[i].activity; + return 0; + } + } + return -1; +} + +static int process_pidf_xml_presence_person_activities(xmlparsing_context_t *xml_ctx, struct _LinphonePresencePerson *person, unsigned int person_idx) { + char xpath_str[MAX_XPATH_LENGTH]; + xmlXPathObjectPtr activities_nodes_object; + xmlXPathObjectPtr activities_object; + xmlNodePtr activity_node; + struct _LinphonePresenceActivity *activity; + const 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 = 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]/*", person_prefix, person_idx, i); + activities_object = 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) + && (activity_node->ns != NULL) + && (activity_node->ns->prefix != NULL) + && (strcmp((const char *)activity_node->ns->prefix, "rpid") == 0)) { + LinphonePresenceActivity linphone_activity; + description = (const char *)xmlNodeGetContent(activity_node); + if ((description != NULL) && (description[0] == '\0')) { + free_xml_text_content(description); + description = NULL; + } + err = activity_name_to_linphone_presence_activity((const char *)activity_node->name, &linphone_activity); + if (err < 0) break; + activity = presence_activity_new(linphone_activity, description); + presence_person_add_activity(person, activity); + if (description != NULL) free_xml_text_content(description); + } + } + } + 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, struct _LinphonePresencePerson *person, unsigned int person_idx) { + char xpath_str[MAX_XPATH_LENGTH]; + xmlXPathObjectPtr note_object; + struct _LinphonePresenceNote *note; + const char *note_str; + const char *lang; + int i; + + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:activities/rpid:note", person_prefix, person_idx); + note_object = 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 = 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 = get_xml_text_content(xml_ctx, xpath_str); + + note = presence_note_new(note_str, lang); + presence_person_add_activities_note(person, note); + if (lang != NULL) free_xml_text_content(lang); + free_xml_text_content(note_str); + } + } + if (note_object != NULL) xmlXPathFreeObject(note_object); + + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:note", person_prefix, person_idx); + note_object = 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:note[%i]", person_prefix, person_idx, i); + note_str = get_xml_text_content(xml_ctx, xpath_str); + if (note_str == NULL) continue; + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/rpid:note[%i]/@xml:lang", person_prefix, person_idx, i); + lang = get_xml_text_content(xml_ctx, xpath_str); + + note = presence_note_new(note_str, lang); + presence_person_add_note(person, note); + if (lang != NULL) free_xml_text_content(lang); + 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; + struct _LinphonePresencePerson *person; + const char *person_id_str; + const char *person_timestamp_str; + int i; + int err = 0; + + person_object = 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 = get_xml_text_content(xml_ctx, xpath_str); + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/timestamp", person_prefix, i); + person_timestamp_str = get_xml_text_content(xml_ctx, xpath_str); + person = presence_person_new(person_id_str, person_timestamp_str); + if (person != NULL) { + err = process_pidf_xml_presence_person_activities(xml_ctx, person, i); + if (err == 0) { + err = process_pidf_xml_presence_person_notes(xml_ctx, person, i); + } + if (err == 0) { + presence_model_add_person(model, person); + } else { + presence_person_delete(person); + break; + } + } + if (person_id_str != NULL) free_xml_text_content(person_id_str); + if (person_timestamp_str != NULL) 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. */ + ms_list_for_each(model->persons, (MSIterateFunc)presence_person_delete); + } + 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; + struct _LinphonePresenceNote *note; + const char *note_str; + const char *lang; + int i; + + note_object = get_xml_xpath_object_for_node_list(xml_ctx, "/pidf:presence/rpid: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/rpid:note[%i]", i); + note_str = get_xml_text_content(xml_ctx, xpath_str); + if (note_str == NULL) continue; + snprintf(xpath_str, sizeof(xpath_str), "/pidf:presence/rpid:note[%i]/@xml:lang", i); + lang = get_xml_text_content(xml_ctx, xpath_str); + + note = presence_note_new(note_str, lang); + presence_model_add_note(model, note); + if (lang != NULL) free_xml_text_content(lang); + 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 (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"); + 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_delete(model); + model = NULL; + } + + return model; +} + +static LinphonePresenceModel * process_xpidf_xml_presence_notification(xmlparsing_context_t *xml_ctx) { + LinphonePresenceModel *model = NULL; + int err = -1; + xmlDtdPtr dtd = xmlGetIntSubset(xml_ctx->doc); + + if (dtd != NULL) { + if (strcmp((const char *)dtd->name, "presence") == 0) { + model = linphone_presence_model_new(); + if ((strcmp((const char *)dtd->SystemID, "xpidf.dtd") == 0) + && (strcmp((const char *)dtd->ExternalID, "-//IETF//DTD RFCxxxx XPIDF 1.0//EN") == 0)) { + err = process_rfcxxxx_presence_notification(xml_ctx, model); + } else if (strcmp((const char *)dtd->SystemID, "http://schemas.microsoft.com/2002/09/sip/presence") == 0) { + err = process_msoldpres_presence_notification(xml_ctx, model); + } + } + } + + if ((err < 0) && (model != NULL)) { + linphone_presence_model_delete(model); + model = NULL; + } + + return model; +} + + + + void linphone_core_add_subscriber(LinphoneCore *lc, const char *subscriber, SalOp *op){ LinphoneFriend *fl=linphone_friend_new_with_addr(subscriber); if (fl==NULL) return ; @@ -100,56 +833,54 @@ void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ ms_free(tmp); } -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus sal_status){ +void linphone_notify_parse_presence(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { + xmlparsing_context_t *xml_ctx; + bool_t pidf_xml = FALSE; + bool_t xpidf_xml = FALSE; + LinphonePresenceModel *model = NULL; + + if (strcmp(content_type, "application") != 0) { + *result = NULL; + return; + } + + pidf_xml = (strcmp(content_subtype, "pidf+xml") == 0); + xpidf_xml = (strcmp(content_subtype, "xpidf+xml") == 0); + if (pidf_xml || xpidf_xml) { + xml_ctx = xmlparsing_context_new(); + xmlSetGenericErrorFunc(xml_ctx, xmlparsing_genericxml_error); + xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); + if (xml_ctx->doc != NULL) { + if (pidf_xml) + model = process_pidf_xml_presence_notification(xml_ctx); + if (xpidf_xml) + model = process_xpidf_xml_presence_notification(xml_ctx); + } else { + ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer); + } + xmlparsing_context_destroy(xml_ctx); + } + + *result = (SalPresenceModel *)model; +} + +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model){ char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; - LinphoneOnlineStatus estatus=LinphoneStatusOffline; - - switch(sal_status){ - case SalPresenceOffline: - estatus=LinphoneStatusOffline; - break; - case SalPresenceOnline: - estatus=LinphoneStatusOnline; - break; - case SalPresenceBusy: - estatus=LinphoneStatusBusy; - break; - case SalPresenceBerightback: - estatus=LinphoneStatusBeRightBack; - break; - case SalPresenceAway: - estatus=LinphoneStatusAway; - break; - case SalPresenceOnthephone: - estatus=LinphoneStatusOnThePhone; - break; - case SalPresenceOuttolunch: - estatus=LinphoneStatusOutToLunch; - break; - case SalPresenceDonotdisturb: - estatus=LinphoneStatusDoNotDisturb; - break; - case SalPresenceOnVacation: - estatus=LinphoneStatusVacation; - break; - case SalPresenceMoved: - case SalPresenceAltService: - estatus=LinphoneStatusMoved; - break; - } + lf=linphone_find_friend_by_out_subscribe(lc->friends,op); if (lf!=NULL){ friend=lf->uri; tmp=linphone_address_as_string(friend); - lf->status=estatus; + linphone_friend_set_presence(lf, (LinphonePresenceModel *)model); lf->subscribe_active=TRUE; if (lc->vtable.notify_presence_recv) lc->vtable.notify_presence_recv(lc,(LinphoneFriend*)lf); ms_free(tmp); }else{ ms_message("But this person is not part of our friend list, so we don't care."); + linphone_presence_model_delete((LinphonePresenceModel *)model); } if (ss==SalSubscribeTerminated){ sal_op_release(op); diff --git a/coreapi/private.h b/coreapi/private.h index f64a59c86..e2ecef9e8 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -290,7 +290,8 @@ SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os); void linphone_process_authentication(LinphoneCore* lc, SalOp *op); void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from); -void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status); +void linphone_notify_parse_presence(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result); +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model); void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op); void linphone_subscription_answered(LinphoneCore *lc, SalOp *op); @@ -427,7 +428,7 @@ struct _LinphoneFriend{ SalOp *insub; SalOp *outsub; LinphoneSubscribePolicy pol; - LinphoneOnlineStatus status; + LinphonePresenceModel *presence; struct _LinphoneCore *lc; BuddyInfo *info; char *refkey; diff --git a/include/sal/sal.h b/include/sal/sal.h index a96f5fb20..6eabe3def 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -292,6 +292,9 @@ typedef enum SalPresenceStatus{ SalPresenceOnVacation }SalPresenceStatus; +struct _SalPresenceModel; +typedef struct _SalPresenceModel SalPresenceModel; + const char* sal_presence_status_to_string(const SalPresenceStatus status); typedef enum SalReferStatus{ @@ -355,7 +358,8 @@ typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, Sal typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, const SalBody *body); typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *event, const SalBody *body); typedef void (*SalOnSubscribeClosed)(SalOp *salop); -typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg); +typedef void (*SalOnParsePresenceRequested)(SalOp *salop, const char *content_type, const char *content_subtype, const char *content, SalPresenceModel **result); +typedef void (*SalOnNotifyPresence)(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg); typedef void (*SalOnSubscribePresenceReceived)(SalOp *salop, const char *from); typedef void (*SalOnSubscribePresenceClosed)(SalOp *salop, const char *from); typedef void (*SalOnPingReply)(SalOp *salop); @@ -393,6 +397,7 @@ typedef struct SalCallbacks{ SalOnNotify notify; SalOnSubscribePresenceReceived subscribe_presence_received; SalOnSubscribePresenceClosed subscribe_presence_closed; + SalOnParsePresenceRequested parse_presence_requested; SalOnNotifyPresence notify_presence; SalOnPingReply ping_reply; SalOnAuthRequested auth_requested;