diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 61cc949fd..cf46d4f74 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -488,6 +488,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ 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.convert_presence_to_xml_requested==NULL) + ctx->callbacks.convert_presence_to_xml_requested=(SalOnConvertPresenceToXMLRequested)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_impl.h b/coreapi/bellesip_sal/sal_impl.h index 61ef731d8..7bc92e2c7 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -134,7 +134,7 @@ void sal_op_call_process_notify(SalOp *op, const belle_sip_request_event_t *even SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) ; void sal_add_pending_auth(Sal *sal, SalOp *op); void sal_remove_pending_auth(Sal *sal, SalOp *op); -void sal_add_presence_info(belle_sip_message_t *notify, SalPresenceStatus online_status); +void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence); belle_sip_response_t *sal_create_response_from_request(Sal *sal, belle_sip_request_t *req, int code); diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index 4b4476b40..321f2e0ac 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -18,404 +18,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sal_impl.h" -typedef enum { - PIDF = 0, - RFCxxxx = 1, - MSOLDPRES = 2 -} presence_type_t; -/* - * REVISIT: this static variable forces every dialog to use the same presence description type depending - * on what is received on a single dialog... - */ -static presence_type_t presence_style = PIDF; - -static void mk_presence_body (const SalPresenceStatus online_status, const char *contact_info, - char *buf, size_t buflen, presence_type_t ptype) { - switch (ptype) { - case RFCxxxx: { - /* definition from http://msdn.microsoft.com/en-us/library/cc246202%28PROT.10%29.aspx */ - int atom_id = 1000; - - if (online_status==SalPresenceOnline) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status == SalPresenceBusy || - online_status == SalPresenceDonotdisturb) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceBerightback) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status == SalPresenceAway || - online_status == SalPresenceMoved) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceOnthephone) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceOuttolunch) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - } - break; - } - case MSOLDPRES: { - /* Couldn't find schema http://schemas.microsoft.com/2002/09/sip/presence - * so messages format has been taken from Communigate that can send notify - * requests with this schema - */ - int atom_id = 1000; - - if (online_status==SalPresenceOnline) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status == SalPresenceBusy || - online_status == SalPresenceDonotdisturb) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceBerightback) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status == SalPresenceAway || - online_status == SalPresenceMoved) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceOnthephone) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==SalPresenceOuttolunch) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - } - break; - } - default: { /* use pidf+xml as default format, rfc4479, rfc4480, rfc3863 */ - - if (online_status==SalPresenceOnline) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status == SalPresenceBusy || - online_status == SalPresenceDonotdisturb) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status==SalPresenceBerightback) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status == SalPresenceAway || - online_status == SalPresenceMoved) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status == SalPresenceOnVacation) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status==SalPresenceOnthephone) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -\n\ -", -contact_info, contact_info); - } - else if (online_status==SalPresenceOuttolunch) - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -open\n\ -%s\n\ -\n\ -\n\ -\n\ -Out to lunch \n\ -\n\ -", -contact_info, contact_info); - } - else - { - snprintf(buf, buflen, "\n\ -\n\ -\n\ -closed\n\ -%s\n\ -\n\ -\n", contact_info, contact_info); - } - break; - } - } // switch - -} - -void sal_add_presence_info(belle_sip_message_t *notify, SalPresenceStatus online_status) { - char buf[1000]; +void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceModel *presence) { char *contact_info; + char *content = NULL; size_t content_length; belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(notify,belle_sip_header_from_t); contact_info=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from))); - mk_presence_body (online_status, contact_info, buf, sizeof (buf), presence_style); - + op->base.root->callbacks.convert_presence_to_xml_requested(op, presence, contact_info, &content); + if (content == NULL) { + ms_free(contact_info); + return; + } belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_TYPE); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) - ,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application",presence_style?"xpidf+xml":"pidf+xml"))); + ,BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","pidf+xml"))); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_CONTENT_LENGTH); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) - ,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(buf)))); - belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),buf,content_length); + ,BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length=strlen(content)))); + belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),content,content_length); ms_free(contact_info); } @@ -627,9 +251,9 @@ static belle_sip_request_t *create_presence_notify(SalOp *op){ return notify; } -int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message){ +int sal_notify_presence(SalOp *op, SalPresenceModel *presence){ belle_sip_request_t* notify=create_presence_notify(op); - sal_add_presence_info(BELLE_SIP_MESSAGE(notify),status); /*FIXME, what about expires ??*/ + sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),presence); /*FIXME, what about expires ??*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600))); return sal_op_send_request(op,notify); @@ -637,7 +261,7 @@ int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_ int sal_notify_presence_close(SalOp *op){ belle_sip_request_t* notify=create_presence_notify(op); - sal_add_presence_info(BELLE_SIP_MESSAGE(notify),SalPresenceOffline); /*FIXME, what about expires ??*/ + sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),NULL); /*FIXME, what about expires ??*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1))); return sal_op_send_request(op,notify); diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c index cf9dc8f54..2445d20e4 100644 --- a/coreapi/bellesip_sal/sal_op_publish.c +++ b/coreapi/bellesip_sal/sal_op_publish.c @@ -30,7 +30,7 @@ static void publish_refresher_listener ( const belle_sip_refresher_t* refresher } /*presence publish */ -int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceStatus status){ +int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceModel *presence){ belle_sip_request_t *req=NULL; if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { if (from) @@ -41,14 +41,13 @@ int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenc op->type=SalOpPublish; req=sal_op_build_request(op,"PUBLISH"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence")); - sal_add_presence_info(BELLE_SIP_MESSAGE(req),status); + sal_add_presence_info(op,BELLE_SIP_MESSAGE(req),presence); return sal_op_send_and_create_refresher(op,req,600,publish_refresher_listener); } else { - /*update status*/ + /*update presence status*/ const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher); belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans)); - /*update status*/ - sal_add_presence_info(BELLE_SIP_MESSAGE(last_publish),status); + sal_add_presence_info(op,BELLE_SIP_MESSAGE(last_publish),presence); return belle_sip_refresher_refresh(op->refresher,BELLE_SIP_REFRESHER_REUSE_EXPIRES); } } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 6d7e6d58e..9820ae4de 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -219,21 +219,31 @@ static void call_received(SalOp *h){ bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE); /* first check if we can answer successfully to this invite */ - if (lc->presence_mode==LinphoneStatusBusy || - lc->presence_mode==LinphoneStatusOffline || - lc->presence_mode==LinphoneStatusDoNotDisturb || - lc->presence_mode==LinphoneStatusMoved){ - if (lc->presence_mode==LinphoneStatusBusy ) - sal_call_decline(h,SalReasonBusy,NULL); - else if (lc->presence_mode==LinphoneStatusOffline) - sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->presence_mode==LinphoneStatusDoNotDisturb) - sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->alt_contact!=NULL && lc->presence_mode==LinphoneStatusMoved) - sal_call_decline(h,SalReasonRedirect,lc->alt_contact); - sal_op_release(h); - return; + if (linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed) { + LinphonePresenceActivity activity = LinphonePresenceActivityOffline; + if (linphone_presence_model_get_activity(lc->presence_model, &activity, NULL)) { + switch (activity) { + case LinphonePresenceActivityBusy: + sal_call_decline(h,SalReasonBusy,NULL); + break; + case LinphonePresenceActivityAppointment: + case LinphonePresenceActivityMeeting: + case LinphonePresenceActivityOffline: + case LinphonePresenceActivityWorship: + sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); + break; + case LinphonePresenceActivityPermanentAbsence: + if (lc->alt_contact != NULL) + sal_call_decline(h,SalReasonRedirect,lc->alt_contact); + break; + default: + break; + } + sal_op_release(h); + return; + } } + if (!linphone_core_can_we_add_call(lc)){/*busy*/ sal_call_decline(h,SalReasonBusy,NULL); sal_op_release(h); @@ -881,6 +891,10 @@ static void parse_presence_requested(SalOp *op, const char *content_type, const linphone_notify_parse_presence(op, content_type, content_subtype, body, result); } +static void convert_presence_to_xml_requested(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) { + linphone_notify_convert_presence_to_xml(op, presence, contact, content); +} + 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,model); @@ -1090,6 +1104,7 @@ SalCallbacks linphone_sal_callbacks={ subscribe_presence_received, subscribe_presence_closed, parse_presence_requested, + convert_presence_to_xml_requested, notify_presence, ping_reply, auth_requested, diff --git a/coreapi/friend.c b/coreapi/friend.c index df66ada73..fe33125d4 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -226,57 +226,12 @@ int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscri return 0; } -SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os){ - switch(os){ - case LinphoneStatusOffline: - return SalPresenceOffline; - break; - case LinphoneStatusOnline: - return SalPresenceOnline; - break; - case LinphoneStatusBusy: - return SalPresenceBusy; - break; - case LinphoneStatusBeRightBack: - return SalPresenceBerightback; - break; - case LinphoneStatusAway: - return SalPresenceAway; - break; - case LinphoneStatusOnThePhone: - return SalPresenceOnthephone; - break; - case LinphoneStatusOutToLunch: - return SalPresenceOuttolunch; - break; - case LinphoneStatusDoNotDisturb: - return SalPresenceDonotdisturb; - break; - case LinphoneStatusMoved: - return SalPresenceMoved; - break; - case LinphoneStatusAltService: - return SalPresenceAltService; - break; - case LinphoneStatusPending: - return SalPresenceOffline; - break; - case LinphoneStatusVacation: - return SalPresenceOnVacation; - break; - default: - return SalPresenceOffline; - break; - } - return SalPresenceOffline; -} - -void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os){ +void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){ char *addr=linphone_address_as_string(linphone_friend_get_address(lf)); ms_message("Want to notify %s, insub=%p",addr,lf->insub); ms_free(addr); if (lf->insub!=NULL){ - sal_notify_presence(lf->insub,linphone_online_status_to_sal(os),NULL); + sal_notify_presence(lf->insub,(SalPresenceModel *)presence); } } @@ -343,7 +298,7 @@ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){ nb_activities = 1; } if (nb_activities == 1) { - err = linphone_presence_model_get_activity(lf->presence, 0, &activity, &activity_description); + err = linphone_presence_model_get_activity(lf->presence, &activity, &activity_description); if (err == 0) { switch (activity) { case LinphonePresenceActivityBreakfast: @@ -389,6 +344,12 @@ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){ online_status = LinphoneStatusMoved; break; case LinphonePresenceActivityUnknown: + /* Rely on the basic status information. */ + break; + case LinphonePresenceActivityOnline: + case LinphonePresenceActivityOffline: + /* Should not happen! */ + ms_warning("LinphonePresenceActivityOnline or LinphonePresenceActivityOffline should not happen here!"); break; } } @@ -414,27 +375,30 @@ BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ } void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ + LinphonePresenceModel *model; + if (fr->uri==NULL) { ms_warning("No sip url defined."); return; } - linphone_core_write_friends_config(lc); if (fr->inc_subscribe_pending){ switch(fr->pol){ case LinphoneSPWait: - linphone_friend_notify(fr,LinphoneStatusPending); + model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOther, "Waiting for user acceptance"); + linphone_friend_notify(fr,model); + linphone_presence_model_delete(model); break; case LinphoneSPAccept: if (fr->lc!=NULL) { - linphone_friend_notify(fr,fr->lc->presence_mode); + linphone_friend_notify(fr,fr->lc->presence_model); } break; case LinphoneSPDeny: - linphone_friend_notify(fr,LinphoneStatusOffline); + linphone_friend_notify(fr,NULL); break; } fr->inc_subscribe_pending=FALSE; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index f4a9c508f..e4d1bca6a 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -412,8 +412,10 @@ static int select_random_port(LinphoneCore *lc, SalStreamType type) { } static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ + LinphonePresenceModel *model; int port_offset; int min_port, max_port; + call->magic=linphone_call_magic; call->refcnt=1; call->state=LinphoneCallIdle; @@ -422,7 +424,9 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, call->media_start_time=0; call->log=linphone_call_log_new(call, from, to); call->owns_call_log=TRUE; - linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone); + model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOnThePhone, NULL); + linphone_core_notify_all_friends(call->core,model); + linphone_presence_model_delete(model); linphone_core_get_audio_port_range(call->core, &min_port, &max_port); if (min_port == max_port) { /* Used fixed RTP audio port. */ @@ -630,7 +634,7 @@ static void linphone_call_set_terminated(LinphoneCall *call){ } if (ms_list_size(lc->calls)==0) - linphone_core_notify_all_friends(lc,lc->presence_mode); + linphone_core_notify_all_friends(lc,lc->presence_model); linphone_core_conference_check_uninit(lc); if (call->ringing_beep){ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index f0beca008..605521552 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1337,7 +1337,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta sip_config_read(lc); /* this will start eXosip*/ video_config_read(lc); //autoreplier_config_init(&lc->autoreplier_conf); - lc->presence_mode=LinphoneStatusOnline; + lc->presence_model=linphone_presence_model_new_with_activity(LinphonePresenceActivityOnline, NULL); misc_config_read(lc); ui_config_read(lc); #ifdef TUNNEL_ENABLED @@ -3568,12 +3568,12 @@ LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const c } int linphone_core_send_publish(LinphoneCore *lc, - LinphoneOnlineStatus presence_mode) + LinphonePresenceModel *presence) { const MSList *elem; for (elem=linphone_core_get_proxy_config_list(lc);elem!=NULL;elem=ms_list_next(elem)){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; - if (cfg->publish) linphone_proxy_config_send_publish(cfg,presence_mode); + if (cfg->publish) linphone_proxy_config_send_publish(cfg,presence); } return 0; } @@ -3642,10 +3642,59 @@ void linphone_core_set_delayed_timeout(LinphoneCore *lc, int seconds){ lc->sip_conf.delayed_timeout=seconds; } -void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, - const char *contact, - LinphoneOnlineStatus presence_mode) -{ +void linphone_core_set_presence_info(LinphoneCore *lc, int minutes_away, const char *contact, LinphoneOnlineStatus os) { + LinphonePresenceModel *presence = NULL; + char *description = NULL; + LinphonePresenceActivity activity = LinphonePresenceActivityUnknown; + switch (os) { + case LinphoneStatusOffline: + activity = LinphonePresenceActivityOffline; + break; + case LinphoneStatusOnline: + activity = LinphonePresenceActivityOnline; + break; + case LinphoneStatusBusy: + activity = LinphonePresenceActivityBusy; + break; + case LinphoneStatusBeRightBack: + activity = LinphonePresenceActivityInTransit; + break; + case LinphoneStatusAway: + activity = LinphonePresenceActivityAway; + break; + case LinphoneStatusOnThePhone: + activity = LinphonePresenceActivityOnThePhone; + break; + case LinphoneStatusOutToLunch: + activity = LinphonePresenceActivityLunch; + break; + case LinphoneStatusDoNotDisturb: + activity = LinphonePresenceActivityBusy; + description = "Do not disturb"; + break; + case LinphoneStatusMoved: + activity = LinphonePresenceActivityPermanentAbsence; + break; + case LinphoneStatusAltService: + activity = LinphonePresenceActivityBusy; + description = "Using another messaging service"; + break; + case LinphoneStatusPending: + activity = LinphonePresenceActivityOther; + description = "Waiting for user acceptance"; + break; + case LinphoneStatusVacation: + activity = LinphonePresenceActivityVacation; + break; + case LinphoneStatusEnd: + ms_warning("Invalid status LinphoneStatusEnd"); + return; + } + presence = linphone_presence_model_new_with_activity(activity, description); + linphone_core_set_presence_model(lc, minutes_away, contact, presence); +} + +void linphone_core_set_presence_model(LinphoneCore *lc, int minutes_away, const char *contact, LinphonePresenceModel *presence) { if (minutes_away>0) lc->minutes_away=minutes_away; if (lc->alt_contact!=NULL) { @@ -3653,20 +3702,75 @@ void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, lc->alt_contact=NULL; } if (contact) lc->alt_contact=ms_strdup(contact); - if (lc->presence_mode!=presence_mode){ - linphone_core_notify_all_friends(lc,presence_mode); + if (!linphone_presence_model_equals(lc->presence_model,presence)){ + linphone_core_notify_all_friends(lc,presence); /* Improve the use of all LINPHONE_STATUS available. !TODO Do not mix "presence status" with "answer status code".. Use correct parameter to follow sip_if_match/sip_etag. */ - linphone_core_send_publish(lc,presence_mode); + linphone_core_send_publish(lc,presence); + } + if ((lc->presence_model != NULL) && (lc->presence_model != presence)) { + linphone_presence_model_delete(lc->presence_model); + lc->presence_model = presence; } - lc->presence_mode=presence_mode; } LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc){ - return lc->presence_mode; + LinphonePresenceActivity activity = LinphonePresenceActivityOffline; + char *description = NULL; + + if ((lc->presence_model == NULL) + || (linphone_presence_model_get_activity(lc->presence_model, &activity, &description) < 0)) + return LinphoneStatusOffline; + + switch (activity) { + case LinphonePresenceActivityOffline: + return LinphoneStatusOffline; + case LinphonePresenceActivityOnline: + return LinphoneStatusOnline; + case LinphonePresenceActivityBusy: + if (description != NULL) { + if (strcmp(description, "Do not disturb") == 0) + return LinphoneStatusDoNotDisturb; + else if (strcmp(description, "Using another messaging service") == 0) + return LinphoneStatusAltService; + } + return LinphoneStatusBusy; + case LinphonePresenceActivityInTransit: + case LinphonePresenceActivitySteering: + return LinphoneStatusBeRightBack; + case LinphonePresenceActivityAway: + return LinphoneStatusAway; + case LinphonePresenceActivityOnThePhone: + return LinphoneStatusOnThePhone; + case LinphonePresenceActivityBreakfast: + case LinphonePresenceActivityDinner: + case LinphonePresenceActivityLunch: + case LinphonePresenceActivityMeal: + return LinphoneStatusOutToLunch; + case LinphonePresenceActivityPermanentAbsence: + return LinphoneStatusMoved; + case LinphonePresenceActivityOther: + if (description != NULL) { + if (strcmp(description, "Waiting for user acceptance") == 0) + return LinphoneStatusPending; + } + return LinphoneStatusBusy; + case LinphonePresenceActivityVacation: + return LinphoneStatusVacation; + case LinphonePresenceActivityAppointment: + case LinphonePresenceActivityMeeting: + case LinphonePresenceActivityWorship: + return LinphoneStatusDoNotDisturb; + default: + return LinphoneStatusBusy; + } +} + +LinphonePresenceModel * linphone_core_get_presence_model(const LinphoneCore *lc) { + return lc->presence_model; } /** diff --git a/coreapi/linphonefriend.h b/coreapi/linphonefriend.h index e01a5fe44..8ea05e7b1 100644 --- a/coreapi/linphonefriend.h +++ b/coreapi/linphonefriend.h @@ -236,13 +236,30 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss); * @param os #LinphoneOnlineStatus */ LINPHONE_PUBLIC void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *alternative_contact,LinphoneOnlineStatus os); + /** - * get my presence status + * Set my presence status + * @param lc #LinphoneCore object + * @param minutes_away how long in away + * @param alternative_contact sip uri used to redirect call in state #LinphoneStatusMoved + * @param presence #LinphonePresenceModel + */ +LINPHONE_PUBLIC void linphone_core_set_presence_model(LinphoneCore *lc, int minutes_away, const char *alternative_contact, LinphonePresenceModel *presence); + +/** + * Get my presence status * @param lc #LinphoneCore object * @return #LinphoneOnlineStatus */ LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc); +/** + * Get my presence status + * @param lc #LinphoneCore object + * @return #LinphonePresenceModel + */ +LinphonePresenceModel * linphone_core_get_presence_model(const LinphoneCore *lc); + void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result); /** * Add a friend to the current buddy list, if \link linphone_friend_enable_subscribes() subscription attribute \endlink is set, a SIP SUBSCRIBE message is sent. @@ -272,7 +289,7 @@ LINPHONE_PUBLIC const MSList * linphone_core_get_friend_list(const LinphoneCore * @param lc #LinphoneCore object * @param os #LinphoneOnlineStatus to notify * */ -void linphone_core_notify_all_friends(LinphoneCore *lc, LinphoneOnlineStatus os); +void linphone_core_notify_all_friends(LinphoneCore *lc, LinphonePresenceModel *presence); LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *addr); LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key); diff --git a/coreapi/linphonepresence.h b/coreapi/linphonepresence.h index 317e25b2e..9244c4ea7 100644 --- a/coreapi/linphonepresence.h +++ b/coreapi/linphonepresence.h @@ -33,6 +33,8 @@ typedef enum LinphonePresenceBasicStatus { /** Activities as defined in section 3.2 of RFC 4480 */ typedef enum LinphonePresenceActivity { + LinphonePresenceActivityOffline, + LinphonePresenceActivityOnline, LinphonePresenceActivityAppointment, LinphonePresenceActivityAway, LinphonePresenceActivityBreakfast, @@ -67,10 +69,17 @@ typedef struct _LinphonePresenceModel LinphonePresenceModel; LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new(void); +LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new_with_activity(LinphonePresenceActivity activity, const char *description); +LINPHONE_PUBLIC LinphonePresenceModel * linphone_presence_model_new_with_activity_and_note(LinphonePresenceActivity activity, const char *description, const char *note, const char *lang); LINPHONE_PUBLIC void linphone_presence_model_delete(LinphonePresenceModel *model); +LINPHONE_PUBLIC bool_t linphone_presence_model_equals(const LinphonePresenceModel *m1, const LinphonePresenceModel *m2); 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); +LINPHONE_PUBLIC int linphone_presence_model_get_nth_activity(const LinphonePresenceModel *model, unsigned int idx, LinphonePresenceActivity *activity, char **description); +LINPHONE_PUBLIC int linphone_presence_model_get_activity(const LinphonePresenceModel *model, LinphonePresenceActivity *activity, char **description); +LINPHONE_PUBLIC int linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivity activity, const char *description); +LINPHONE_PUBLIC const char * linphone_presence_model_get_note(const LinphonePresenceModel *model, const char *lang); +LINPHONE_PUBLIC int linphone_presence_model_set_note(LinphonePresenceModel *model, const char *note, const char *lang); #ifdef __cplusplus diff --git a/coreapi/presence.c b/coreapi/presence.c index 9ad01bc07..caab79581 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #include "private.h" #include +#include #include #include @@ -41,6 +42,9 @@ struct _LinphonePresenceNote { struct _LinphonePresenceService { char *id; LinphonePresenceBasicStatus status; + char *contact; + MSList *notes; /**< A list of _LinphonePresenceNote structures. */ + time_t timestamp; }; struct _LinphonePresenceActivity { @@ -107,6 +111,30 @@ static void xmlparsing_genericxml_error(void *ctx, const char *fmt, ...) { va_end(args); } +static char presence_id_valid_characters[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +static char * generate_presence_id(void) { + char id[7]; + int i; + + for (i = 0; i < 6; i++) { + id[i] = presence_id_valid_characters[random() % sizeof(presence_id_valid_characters)]; + } + id[6] = '\0'; + + return ms_strdup(id); +} + +static const char * presence_basic_status_to_string(LinphonePresenceBasicStatus basic_status) { + switch (basic_status) { + case LinphonePresenceBasicStatusOpen: + return "open"; + case LinphonePresenceBasicStatusClosed: + default: + return "closed"; + } +} + 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); @@ -130,6 +158,7 @@ static struct _LinphonePresenceService * presence_service_new(const char *id, Li service->id = ms_strdup(id); } service->status = status; + service->timestamp = time(NULL); return service; } @@ -137,9 +166,26 @@ static void presence_service_delete(struct _LinphonePresenceService *service) { if (service->id != NULL) { ms_free(service->id); } + if (service->contact != NULL) { + ms_free(service->contact); + } + ms_list_for_each(service->notes, (MSIterateFunc)presence_service_delete); + ms_list_free(service->notes); ms_free(service); }; +static void presence_service_set_timestamp(struct _LinphonePresenceService *service, time_t timestamp) { + service->timestamp = timestamp; +} + +static void presence_service_set_contact(struct _LinphonePresenceService *service, const char *contact) { + service->contact = ms_strdup(contact); +} + +static void presence_service_add_note(struct _LinphonePresenceService *service, struct _LinphonePresenceNote *note) { + service->notes = ms_list_append(service->notes, note); +} + static struct _LinphonePresenceActivity * presence_activity_new(LinphonePresenceActivity activity, const char *description) { struct _LinphonePresenceActivity *act = ms_new0(struct _LinphonePresenceActivity, 1); act->activity = activity; @@ -174,18 +220,29 @@ static time_t parse_timestamp(const char *timestamp) { return seconds - timezone; } -static struct _LinphonePresencePerson * presence_person_new(const char *id, const char *timestamp) { +static char * timestamp_to_string(time_t timestamp) { + char timestamp_str[22]; + struct tm *ret; +#ifndef WIN32 + struct tm gmt; + ret = gmtime_r(×tamp,&gmt); +#else + ret = gmtime(&curtime); +#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 struct _LinphonePresencePerson * presence_person_new(const char *id, time_t 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 { + if (person->timestamp == ((time_t)-1)) person->timestamp = time(NULL); - } + else + person->timestamp = timestamp; return person; } @@ -214,6 +271,11 @@ static void presence_person_add_note(struct _LinphonePresencePerson *person, str person->notes = ms_list_append(person->notes, note); } +static void presence_person_clear_activities(struct _LinphonePresencePerson *person) { + ms_list_for_each(person->activities, (MSIterateFunc)presence_activity_delete); + ms_list_free(person->activities); +} + static void presence_model_add_service(LinphonePresenceModel *model, struct _LinphonePresenceService *service) { model->services = ms_list_append(model->services, service); } @@ -226,17 +288,151 @@ static void presence_model_add_note(LinphonePresenceModel *model, struct _Linpho model->notes = ms_list_append(model->notes, note); } +static int presence_model_set_basic_status(LinphonePresenceModel *model, LinphonePresenceBasicStatus basic_status) { + struct _LinphonePresenceService *service; + char *id; + if (ms_list_size(model->services) > 0) { + ms_list_for_each(model->services, (MSIterateFunc)presence_service_delete); + ms_list_free(model->services); + } + id = generate_presence_id(); + service = presence_service_new(id, basic_status); + ms_free(id); + if (service == NULL) return -1; + presence_model_add_service(model, service); + return 0; +} + +static void presence_model_clear_activities(LinphonePresenceModel *model) { + ms_list_for_each(model->persons, (MSIterateFunc)presence_person_clear_activities); +} + +static int presence_model_add_activity(LinphonePresenceModel *model, LinphonePresenceActivity activity, const char *description) { + char *id = NULL; + struct _LinphonePresencePerson *person = NULL; + struct _LinphonePresenceActivity *act = NULL; + + /* Do not add activity for special cases Offline and Online. */ + if ((activity == LinphonePresenceActivityOffline) || (activity == LinphonePresenceActivityOnline)) + return 0; + + if (ms_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); + } else { + /* Add the activity to the first person in the model. */ + person = (struct _LinphonePresencePerson *)ms_list_nth_data(model->persons, 0); + } + act = presence_activity_new(activity, description); + if (act == NULL) + return -1; + presence_person_add_activity(person, act); + + return 0; +} + static void presence_model_find_open_basic_status(struct _LinphonePresenceService *service, LinphonePresenceBasicStatus *status) { if (service->status == LinphonePresenceBasicStatusOpen) { *status = LinphonePresenceBasicStatusOpen; } } +static bool_t presence_service_equals(const struct _LinphonePresenceService *s1, const struct _LinphonePresenceService *s2) { + if (s1->status != s2->status) + return FALSE; + return TRUE; +} + +static bool_t presence_note_equals(const struct _LinphonePresenceNote *n1, const struct _LinphonePresenceNote *n2) { + if (((n1->lang == NULL) && (n2->lang != NULL)) + || ((n1->lang != NULL) && (n2->lang == NULL))) + return FALSE; + + if (strcmp(n1->content, n2->content) != 0) + return FALSE; + if ((n1->lang != NULL) && (n2->lang != NULL)) { + if (strcmp(n1->lang, n2->lang) != 0) + return FALSE; + } + + return TRUE; +} + +static bool_t presence_activity_equals(const struct _LinphonePresenceActivity *a1, const struct _LinphonePresenceActivity *a2) { + if (((a1->description == NULL) && (a2->description != NULL)) + || ((a1->description != NULL) && (a2->description == NULL))) + return FALSE; + + if (a1->activity != a2->activity) + return FALSE; + + if ((a1->description != NULL) && (a2->description != NULL)) { + if (strcmp(a1->description, a2->description) != 0) + return FALSE; + } + + return TRUE; +} + +static bool_t presence_person_equals(const struct _LinphonePresencePerson *p1, const struct _LinphonePresencePerson *p2) { + int nb; + int i; + + if ((ms_list_size(p1->activities) != ms_list_size(p2->activities)) + || (ms_list_size(p1->activities_notes) != ms_list_size(p2->activities_notes)) + || (ms_list_size(p1->notes) != ms_list_size(p2->notes))) + return FALSE; + + nb = ms_list_size(p1->activities); + for (i = 0; i < nb; i++) { + if (presence_activity_equals(ms_list_nth_data(p1->activities, i), ms_list_nth_data(p2->activities, i)) == FALSE) + return FALSE; + } + + nb = ms_list_size(p1->activities_notes); + for (i = 0; i < nb; i++) { + if (presence_note_equals(ms_list_nth_data(p1->activities_notes, i), ms_list_nth_data(p2->activities_notes, i)) == FALSE) + return FALSE; + } + + nb = ms_list_size(p1->notes); + for (i = 0; i < nb; i++) { + if (presence_note_equals(ms_list_nth_data(p1->notes, i), ms_list_nth_data(p2->notes, i)) == FALSE) + return FALSE; + } + + return TRUE; +} + LinphonePresenceModel * linphone_presence_model_new(void) { return ms_new0(LinphonePresenceModel, 1); } +LinphonePresenceModel * linphone_presence_model_new_with_activity(LinphonePresenceActivity activity, const char *description) { + LinphonePresenceModel *model = linphone_presence_model_new(); + if (model != NULL) { + linphone_presence_model_set_activity(model, activity, description); + } + return model; +} + +LinphonePresenceModel * linphone_presence_model_new_with_activity_and_note(LinphonePresenceActivity activity, const char *description, const char *note, const char *lang) { + LinphonePresenceModel *model = linphone_presence_model_new(); + if (model != NULL) { + linphone_presence_model_set_activity(model, activity, description); + linphone_presence_model_set_note(model, note, lang); + } + return model; +} + void linphone_presence_model_delete(LinphonePresenceModel *model) { + if (model == NULL) return; + 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); @@ -246,10 +442,61 @@ void linphone_presence_model_delete(LinphonePresenceModel *model) { ms_free(model); } +bool_t linphone_presence_model_equals(const LinphonePresenceModel *m1, const LinphonePresenceModel *m2) { + LinphonePresenceActivity activity = LinphonePresenceActivityOffline; + int nb; + int i; + + /* Two null activities are considered equal. */ + if ((m1 == NULL) && (m2 == NULL)) + return TRUE; + + /* A null activity is equal to an activity with no activity but a basic status of Closed. */ + if (m1 == NULL) { + if ((linphone_presence_model_get_activity(m2, &activity, NULL) < 0) + || (activity != LinphonePresenceActivityOffline)) + return FALSE; + return TRUE; + } + if (m2 == NULL) { + if ((linphone_presence_model_get_activity(m2, &activity, NULL) < 0) + || (activity != LinphonePresenceActivityOffline)) + return FALSE; + return TRUE; + } + + if ((ms_list_size(m1->services) != ms_list_size(m2->services)) + || (ms_list_size(m1->persons) != ms_list_size(m2->persons)) + || (ms_list_size(m1->notes) != ms_list_size(m2->notes))) + return FALSE; + + nb = ms_list_size(m1->services); + for (i = 0; i < nb; i++) { + if (presence_service_equals(ms_list_nth_data(m1->services, i), ms_list_nth_data(m2->services, i)) == FALSE) + return FALSE; + } + + nb = ms_list_size(m1->persons); + for (i = 0; i < nb; i++) { + if (presence_person_equals(ms_list_nth_data(m1->persons, i), ms_list_nth_data(m2->persons, i)) == FALSE) + return FALSE; + } + + nb = ms_list_size(m1->notes); + for (i = 0; i < nb; i++) { + if (presence_note_equals(ms_list_nth_data(m1->notes, i), ms_list_nth_data(m2->notes, i)) == FALSE) + return FALSE; + } + + return TRUE; +} + /* 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); + if (model != NULL) { + ms_list_for_each2(model->services, (MSIterate2Func)presence_model_find_open_basic_status, &status); + } return status; } @@ -284,10 +531,12 @@ static void presence_model_get_activity(const struct _LinphonePresencePerson *pe } } -int linphone_presence_model_get_activity(const LinphonePresenceModel *model, unsigned int idx, LinphonePresenceActivity *activity, char **description) { +int linphone_presence_model_get_nth_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))) + + if ((model == NULL) || (activity == NULL) || (idx >= linphone_presence_model_nb_activities(model))) return -1; + memset(&st, 0, sizeof(st)); st.requested_idx = idx; st.activity = activity; @@ -296,9 +545,63 @@ int linphone_presence_model_get_activity(const LinphonePresenceModel *model, uns st.description = description; } ms_list_for_each2(model->persons, (MSIterate2Func)presence_model_get_activity, &st); + return 0; } +int linphone_presence_model_get_activity(const LinphonePresenceModel *model, LinphonePresenceActivity *activity, char **description) { + if ((model == NULL) || (activity == NULL)) + return -1; + + if (linphone_presence_model_get_nth_activity(model, 0, activity, description) < 0) { + /* There is no activities, base the result on the basic status. */ + LinphonePresenceBasicStatus basic_status = linphone_presence_model_get_basic_status(model); + if (basic_status == LinphonePresenceBasicStatusOpen) + *activity = LinphonePresenceActivityOnline; + else + *activity = LinphonePresenceActivityOffline; + } + + return 0; +} + +int linphone_presence_model_set_activity(LinphonePresenceModel *model, LinphonePresenceActivity activity, const char *description) { + LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusOpen; + + if (model == NULL) return -1; + + switch (activity) { + case LinphonePresenceActivityAppointment: + case LinphonePresenceActivityBusy: + case LinphonePresenceActivityMeeting: + case LinphonePresenceActivityPermanentAbsence: + case LinphonePresenceActivityOffline: + case LinphonePresenceActivityWorship: + basic_status = LinphonePresenceBasicStatusClosed; + break; + default: + basic_status = LinphonePresenceBasicStatusOpen; + break; + } + if (presence_model_set_basic_status(model, basic_status) < 0) + return -1; + presence_model_clear_activities(model); + if (presence_model_add_activity(model, activity, description) < 0) + return -1; + + return 0; +} + +const char * linphone_presence_model_get_note(const LinphonePresenceModel *model, const char *lang) { + // TODO + return NULL; +} + +int linphone_presence_model_set_note(LinphonePresenceModel *model, const char *note, const char *lang) { + // TODO + return -1; +} + static int create_xml_xpath_context(xmlparsing_context_t *xml_ctx) { if (xml_ctx->xpath_ctx != NULL) { xmlXPathFreeContext(xml_ctx->xpath_ctx); @@ -381,7 +684,7 @@ static int process_rfcxxxx_presence_notification(xmlparsing_context_t *xml_ctx, presence_model_add_service(model, service); } if (activity != NULL) { - person = presence_person_new(NULL, NULL); + person = presence_person_new(NULL, time(NULL)); if (person != NULL) { presence_person_add_activity(person, activity); presence_model_add_person(model, person); @@ -438,7 +741,7 @@ static int process_msoldpres_presence_notification(xmlparsing_context_t *xml_ctx presence_model_add_service(model, service); } if (activity != NULL) { - person = presence_person_new(NULL, NULL); + person = presence_person_new(NULL, time(NULL)); if (person != NULL) { presence_person_add_activity(person, activity); presence_model_add_person(model, person); @@ -450,12 +753,43 @@ static int process_msoldpres_presence_notification(xmlparsing_context_t *xml_ctx static const char *service_prefix = "/pidf:presence/pidf:tuple"; +static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx, struct _LinphonePresenceService *service, unsigned int service_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:note", service_prefix, service_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]", service_prefix, service_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", service_prefix, service_idx, i); + lang = get_xml_text_content(xml_ctx, xpath_str); + + note = presence_note_new(note_str, lang); + presence_service_add_note(service, 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_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; + const char *timestamp_str; + const char *contact_str; LinphonePresenceBasicStatus basic_status; int i; @@ -477,10 +811,25 @@ static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, Lin return -1; } + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:timestamp", service_prefix, i); + timestamp_str = get_xml_text_content(xml_ctx, xpath_str); + + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:contact", service_prefix, i); + contact_str = get_xml_text_content(xml_ctx, xpath_str); + 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) { + if (timestamp_str != NULL) { + presence_service_set_timestamp(service, parse_timestamp(timestamp_str)); + free_xml_text_content(timestamp_str); + } + if (contact_str != NULL) { + presence_service_set_contact(service, contact_str); + free_xml_text_content(contact_str); + } + process_pidf_xml_presence_service_notes(xml_ctx, service, i); presence_model_add_service(model, service); } free_xml_text_content(basic_status_str); @@ -540,6 +889,16 @@ static int activity_name_to_linphone_presence_activity(const char *name, Linphon return -1; } +static const char * presence_activity_to_string(LinphonePresenceActivity activity) { + unsigned int i; + for (i = 0; i < (sizeof(activity_map) / sizeof(activity_map[0])); i++) { + if (activity == activity_map[i].activity) { + return activity_map[i].name; + } + } + return NULL; +} + 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; @@ -638,6 +997,7 @@ static int process_pidf_xml_presence_persons(xmlparsing_context_t *xml_ctx, Linp struct _LinphonePresencePerson *person; const char *person_id_str; const char *person_timestamp_str; + time_t timestamp; int i; int err = 0; @@ -646,9 +1006,13 @@ static int process_pidf_xml_presence_persons(xmlparsing_context_t *xml_ctx, Linp 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); + snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf: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_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, i); if (err == 0) { @@ -775,13 +1139,13 @@ 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, LinphoneOnlineStatus os){ +void linphone_core_notify_all_friends(LinphoneCore *lc, LinphonePresenceModel *presence){ MSList *elem; - ms_message("Notifying all friends that we are in status %i",os); + ms_message("Notifying all friends"); for(elem=lc->friends;elem!=NULL;elem=elem->next){ LinphoneFriend *lf=(LinphoneFriend *)elem->data; if (lf->insub){ - linphone_friend_notify(lf,os); + linphone_friend_notify(lf,presence); } } } @@ -864,6 +1228,259 @@ void linphone_notify_parse_presence(SalOp *op, const char *content_type, const c *result = (SalPresenceModel *)model; } +struct _presence_service_obj_st { + xmlTextWriterPtr writer; + const char *contact; + 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_timestamp(xmlTextWriterPtr writer, time_t timestamp) { + int err; + char *timestamp_str = timestamp_to_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, struct _LinphonePresenceService *service, const char *contact) { + 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) { + LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed; + if (service != NULL) basic_status = service->status; + err = xmlTextWriterWriteElement(writer, (const xmlChar *)"basic", (const xmlChar *)presence_basic_status_to_string(basic_status)); + } + 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) { + err = xmlTextWriterWriteString(writer, (const xmlChar *)contact); + } + if (err >= 0) { + /* Close the "contact" element. */ + err = xmlTextWriterEndElement(writer); + } + 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, struct _LinphonePresenceActivity *activity) { + int err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"rpid", + (const xmlChar *)presence_activity_to_string(activity->activity), 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(struct _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_note(xmlTextWriterPtr writer, struct _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(struct _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_person(xmlTextWriterPtr writer, struct _LinphonePresencePerson *person) { + int 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; + ms_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; + ms_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; + ms_list_for_each2(person->activities_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(struct _LinphonePresenceService *service, struct _presence_service_obj_st *st) { + int err = write_xml_presence_service(st->writer, service, st->contact); + if (err < 0) *st->err = err; +} + +static void write_xml_presence_person_obj(struct _LinphonePresencePerson *person, struct _presence_person_obj_st *st) { + int err = write_xml_presence_person(st->writer, person); + if (err < 0) *st->err = err; +} + +void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) { + LinphonePresenceModel *model; + xmlBufferPtr buf; + xmlTextWriterPtr writer; + int err; + + if ((contact == NULL) || (content == NULL)) return; + + model = (LinphonePresenceModel *)presence; + buf = xmlBufferCreate(); + if (buf == NULL) { + ms_error("Error creating the XML buffer"); + return; + } + writer = xmlNewTextWriterMemory(buf, 0); + if (writer == NULL) { + ms_error("Error creating the XML writer"); + return; + } + + 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) { + 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); + } else { + struct _presence_service_obj_st st; + st.writer = writer; + st.contact = contact; + st.err = &err; + ms_list_for_each2(model->services, (MSIterate2Func)write_xml_presence_service_obj, &st); + } + } + if ((err >= 0) && (model != NULL)) { + struct _presence_person_obj_st st; + st.writer = writer; + st.err = &err; + ms_list_for_each2(model->persons, (MSIterate2Func)write_xml_presence_person_obj, &st); + } + if ((err >= 0) && (model != NULL)) { + struct _presence_note_obj_st st; + st.writer = writer; + st.ns = NULL; + st.err = &err; + ms_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); + } + + xmlFreeTextWriter(writer); + if (err > 0) { + /* xmlTextWriterEndDocument returns the size of the content. */ + *content = ms_strdup((char *)buf->content); + } + xmlBufferFree(buf); +} + void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model){ char *tmp; LinphoneFriend *lf; diff --git a/coreapi/private.h b/coreapi/private.h index e2ecef9e8..e85c169fc 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -233,7 +233,7 @@ void linphone_core_refresh_subscribes(LinphoneCore *lc); int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc); -int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphoneOnlineStatus os); +int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphonePresenceModel *presence); void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState rstate, const char *message); void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc); /* @@ -245,7 +245,7 @@ const LinphoneAddress* linphone_proxy_config_get_service_route(const LinphonePro int linphone_online_status_to_eXosip(LinphoneOnlineStatus os); void linphone_friend_close_subscriptions(LinphoneFriend *lf); -void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os); +void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence); LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op); LinphoneFriend *linphone_find_friend_by_out_subscribe(MSList *l, SalOp *op); @@ -286,11 +286,11 @@ static inline void set_string(char **dest, const char *src){ #define PAYLOAD_TYPE_ENABLED PAYLOAD_TYPE_USER_FLAG_0 -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_parse_presence(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result); +void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presence, const char *contact, char **content); void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model); void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op); @@ -599,7 +599,7 @@ struct _LinphoneCore MSList *bl_reqs; MSList *subscribers; /* unknown subscribers */ int minutes_away; - LinphoneOnlineStatus presence_mode; + LinphonePresenceModel *presence_model; char *alt_contact; void *data; char *play_file; diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 4bf6e6dc1..814c00766 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -849,8 +849,7 @@ void linphone_proxy_config_set_realm(LinphoneProxyConfig *cfg, const char *realm if (realm!=NULL) cfg->realm=ms_strdup(realm); } -int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, - LinphoneOnlineStatus presence_mode){ +int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, LinphonePresenceModel *presence){ int err; if (proxy->publish_op==NULL){ @@ -859,7 +858,7 @@ int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, sal_op_set_from(proxy->publish_op,linphone_proxy_config_get_identity(proxy)); sal_op_set_to(proxy->publish_op,linphone_proxy_config_get_identity(proxy)); } - err=sal_publish_presence(proxy->publish_op,NULL,NULL,linphone_online_status_to_sal(presence_mode)); + err=sal_publish_presence(proxy->publish_op,NULL,NULL,(SalPresenceModel *)presence); return err; } @@ -1190,7 +1189,7 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){ } } if (cfg->publish && cfg->publish_op==NULL && cfg->state==LinphoneRegistrationOk){ - linphone_proxy_config_send_publish(cfg,lc->presence_mode); + linphone_proxy_config_send_publish(cfg,lc->presence_model); } } diff --git a/include/sal/sal.h b/include/sal/sal.h index 6eabe3def..0e94faf5b 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -359,6 +359,7 @@ typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *ev typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *event, const SalBody *body); typedef void (*SalOnSubscribeClosed)(SalOp *salop); typedef void (*SalOnParsePresenceRequested)(SalOp *salop, const char *content_type, const char *content_subtype, const char *content, SalPresenceModel **result); +typedef void (*SalOnConvertPresenceToXMLRequested)(SalOp *salop, SalPresenceModel *presence, const char *contact, char **content); 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); @@ -398,6 +399,7 @@ typedef struct SalCallbacks{ SalOnSubscribePresenceReceived subscribe_presence_received; SalOnSubscribePresenceClosed subscribe_presence_closed; SalOnParsePresenceRequested parse_presence_requested; + SalOnConvertPresenceToXMLRequested convert_presence_to_xml_requested; SalOnNotifyPresence notify_presence; SalOnPingReply ping_reply; SalOnAuthRequested auth_requested; @@ -532,11 +534,11 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co /*presence Subscribe/notify*/ int sal_subscribe_presence(SalOp *op, const char *from, const char *to); -int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message); +int sal_notify_presence(SalOp *op, SalPresenceModel *presence); int sal_notify_presence_close(SalOp *op); /*presence publish */ -int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceStatus status); +int sal_publish_presence(SalOp *op, const char *from, const char *to, SalPresenceModel *presence); /*ping: main purpose is to obtain its own contact address behind firewalls*/