diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c index 75f87a2d9..0c93ff2fa 100644 --- a/coreapi/bellesip_sal/sal_op_events.c +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -236,15 +236,6 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event return -1; } -int sal_subscribe_refresh(SalOp *op) { - if (op->refresher) { - belle_sip_refresher_refresh(op->refresher,belle_sip_refresher_get_expires(op->refresher)); - return 0; - } - ms_warning("sal_refresh_subscribe(): no refresher"); - return -1; -} - int sal_unsubscribe(SalOp *op){ if (op->refresher){ const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher); diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 88d6cf0f9..9fcca24a6 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -789,3 +789,11 @@ bool_t sal_op_cnx_ip_to_0000_if_sendonly_enabled(SalOp *op) { bool_t sal_op_is_forked_of(const SalOp *op1, const SalOp *op2){ return op1->base.call_id && op2->base.call_id && strcmp(op1->base.call_id, op2->base.call_id) == 0; } +int sal_op_refresh(SalOp *op) { + if (op->refresher) { + belle_sip_refresher_refresh(op->refresher,belle_sip_refresher_get_expires(op->refresher)); + return 0; + } + ms_warning("sal_refresh on op [%p] of type [%s] no refresher",op,sal_op_type_to_string(op)); + return -1; +} diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c index a1dc85204..d5c16ff66 100644 --- a/coreapi/bellesip_sal/sal_op_publish.c +++ b/coreapi/bellesip_sal/sal_op_publish.c @@ -25,17 +25,17 @@ static void publish_refresher_listener (belle_sip_refresher_t* refresher ,const char* reason_phrase) { SalOp* op = (SalOp*)user_pointer; 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)); belle_sip_response_t *response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(last_publish_trans)); - /*belle_sip_response_t* response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)));*/ ms_message("Publish refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase?reason_phrase:"none",sal_op_get_proxy(op)); - if (status_code==412){ - /*resubmit the request after removing the SIP-If-Match*/ - belle_sip_message_remove_header((belle_sip_message_t*)last_publish,"SIP-If-Match"); - belle_sip_refresher_refresh(op->refresher,BELLE_SIP_REFRESHER_REUSE_EXPIRES); - }else if (status_code==0){ + if (status_code==0){ op->base.root->callbacks.on_expire(op); }else if (status_code>=200){ + belle_sip_header_t *sip_etag; + const char *sip_etag_string = NULL; + if ((sip_etag = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response), "SIP-ETag"))) { + sip_etag_string = belle_sip_header_get_unparsed_value(sip_etag); + } + sal_op_set_entity_tag(op, sip_etag_string); sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL); sal_op_assign_recv_headers(op,(belle_sip_message_t*)response); op->base.root->callbacks.on_publish_response(op); @@ -60,42 +60,6 @@ void sal_op_publish_fill_cbs(SalOp *op) { op->type=SalOpPublish; } -/* - * Sending a publish with 0 expires removes the event state and such request shall not contain a body. - * See RFC3903, section 4.5 - */ - -/*presence publish */ -int sal_publish_presence(SalOp *op, const char *from, const char *to, int expires, SalPresenceModel *presence){ - belle_sip_request_t *req=NULL; - if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { - if (from) - sal_op_set_from(op,from); - if (to) - sal_op_set_to(op,to); - - op->type=SalOpPublish; - req=sal_op_build_request(op,"PUBLISH"); - - if( req == NULL ){ - return -1; - } - - if (sal_op_get_contact_address(op)){ - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); - } - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence")); - sal_add_presence_info(op,BELLE_SIP_MESSAGE(req),presence); - return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener); - } else { - /*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)); - sal_add_presence_info(op,BELLE_SIP_MESSAGE(last_publish),expires!=0 ? presence : NULL); - return belle_sip_refresher_refresh(op->refresher,expires); - } -} - int sal_publish(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBodyHandler *body_handler){ belle_sip_request_t *req=NULL; if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) { @@ -109,7 +73,11 @@ int sal_publish(SalOp *op, const char *from, const char *to, const char *eventna if( req == NULL ){ return -1; } - + + if (sal_op_get_entity_tag(op)) { + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("SIP-If-Match", sal_op_get_entity_tag(op))); + } + if (sal_op_get_contact_address(op)){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op))); } @@ -131,3 +99,14 @@ int sal_publish(SalOp *op, const char *from, const char *to, const char *eventna return belle_sip_refresher_refresh(op->refresher,expires==-1 ? BELLE_SIP_REFRESHER_REUSE_EXPIRES : expires); } } + +int sal_op_unpublish(SalOp *op){ + if (op->refresher){ + const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher); + belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr); + belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0); + belle_sip_refresher_refresh(op->refresher,0); + return 0; + } + return -1; +} diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 4b55f474e..b884bf224 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -1038,10 +1038,11 @@ static void register_failure(SalOp *op){ } else { linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details); } - if (cfg->publish_op){ + if (cfg->long_term_event){ /*prevent publish to be sent now until registration gets successful*/ - sal_op_release(cfg->publish_op); - cfg->publish_op=NULL; + linphone_event_terminate(cfg->long_term_event); + linphone_event_unref(cfg->long_term_event); + cfg->long_term_event=NULL; cfg->send_publish=cfg->publish; } } @@ -1137,7 +1138,13 @@ static void parse_presence_requested(SalOp *op, const char *content_type, const } 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); + /*for backward compatibility because still used by notify. No loguer used for publish*/ + + if(linphone_presence_model_get_presentity((LinphonePresenceModel*)presence) == NULL) { + LinphoneAddress * presentity = linphone_address_new(contact); + linphone_presence_model_set_presentity((LinphonePresenceModel*)presence, presentity); + } + *content = linphone_presence_model_to_xml((LinphonePresenceModel*)presence); } static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){ diff --git a/coreapi/event.c b/coreapi/event.c index 2d6c8f270..07e373b73 100644 --- a/coreapi/event.c +++ b/coreapi/event.c @@ -201,7 +201,7 @@ int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *b } int linphone_event_refresh_subscribe(LinphoneEvent *lev) { - return sal_subscribe_refresh(lev->op); + return sal_op_refresh(lev->op); } int linphone_event_accept_subscription(LinphoneEvent *lev){ @@ -291,6 +291,16 @@ int linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* bod return linphone_event_send_publish(lev,body); } +int linphone_event_refresh_publish(LinphoneEvent *lev) { + return sal_op_refresh(lev->op); +} +void linphone_event_pause_publish(LinphoneEvent *lev) { + if (lev->op) sal_op_stop_refreshing(lev->op); +} +void linphone_event_unpublish(LinphoneEvent *lev) { + lev->terminating = TRUE; /* needed to get clear event*/ + if (lev->op) sal_op_unpublish(lev->op); +} void linphone_event_set_user_data(LinphoneEvent *ev, void *up){ ev->userdata=up; } @@ -320,7 +330,7 @@ void linphone_event_terminate(LinphoneEvent *lev){ if (lev->publish_state!=LinphonePublishNone){ if (lev->publish_state==LinphonePublishOk && lev->expires!=-1){ sal_publish(lev->op,NULL,NULL,NULL,0,NULL); - }else sal_op_stop_refreshing(lev->op); + }else sal_op_unpublish(lev->op); linphone_event_set_publish_state(lev,LinphonePublishCleared); return; } diff --git a/coreapi/event.h b/coreapi/event.h index 5200beb66..b9033f8c7 100644 --- a/coreapi/event.h +++ b/coreapi/event.h @@ -208,6 +208,21 @@ LINPHONE_PUBLIC int linphone_event_send_publish(LinphoneEvent *lev, const Linpho **/ LINPHONE_PUBLIC int linphone_event_update_publish(LinphoneEvent *lev, const LinphoneContent *body); +/** + * Refresh an outgoing publish keeping the same body. + * @param lev LinphoneEvent object. + * @return 0 if successful, -1 otherwise. + */ +LINPHONE_PUBLIC int linphone_event_refresh_publish(LinphoneEvent *lev); + +/** + * Prevent an event from refreshing its publish. + * This is useful to let registrations to expire naturally (or) when the application wants to keep control on when + * refreshes are sent. + * The refreshing operations can be resumed with linphone_proxy_config_refresh_register(). + * @param[in] cfg #LinphoneEvent object. + **/ +LINPHONE_PUBLIC void linphone_event_pause_publish(LinphoneEvent *lev); /** * Return reason code (in case of error state reached). diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 2b65fb5cd..b4523d41c 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6184,6 +6184,7 @@ void sip_config_uninit(LinphoneCore *lc) if (lc->sip_network_reachable) { for(elem=config->proxies;elem!=NULL;elem=ms_list_next(elem)){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data); + _linphone_proxy_config_unpublish(cfg); /* to unpublish without changing the stored flag enable_publish */ _linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */ } diff --git a/coreapi/linphonepresence.h b/coreapi/linphonepresence.h index c0168cb8b..a4a451ad6 100644 --- a/coreapi/linphonepresence.h +++ b/coreapi/linphonepresence.h @@ -261,6 +261,21 @@ LINPHONE_PUBLIC char * linphone_presence_model_get_contact(const LinphonePresenc */ LINPHONE_PUBLIC int linphone_presence_model_set_contact(LinphonePresenceModel *model, const char *contact); +/** + * Sets the presentity of a presence model. + * @param[in] model The #LinphonePresenceModel object for which to set the contact. + * @param[in] presentity The presentity address to set (presentity is copied). + * @return 0 if successful, a value < 0 in case of error. + */ +LINPHONE_PUBLIC int linphone_presence_model_set_presentity(LinphonePresenceModel *model, const LinphoneAddress *presentity); +/** + * Gets the presentity of a presence model. + * @param[in] model The #LinphonePresenceModel object to get the contact from. + * @return A pointer to a const LinphoneAddress, or NULL if no contact is found. + * + */ +LINPHONE_PUBLIC const LinphoneAddress * linphone_presence_model_get_presentity(const LinphonePresenceModel *model); + /** * Gets the first activity of a presence model (there is usually only one). * @param[in] model The #LinphonePresenceModel object to get the activity from. diff --git a/coreapi/presence.c b/coreapi/presence.c index cec2b1547..b4d46f934 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -67,6 +67,7 @@ struct _LinphonePresencePerson { * This model is not complete. For example, it does not handle devices. */ struct _LinphonePresenceModel { + LinphoneAddress *presentity; /* "The model seeks to describe the presentity, identified by a presentity URI.*/ void *user_data; int refcnt; MSList *services; /**< A list of _LinphonePresenceService structures. Also named tuples in the RFC. */ @@ -246,7 +247,8 @@ static void presence_model_find_open_basic_status(LinphonePresenceService *servi static void presence_model_delete(LinphonePresenceModel *model) { if (model == NULL) return; - + if (model->presentity) + linphone_address_unref(model->presentity); ms_list_for_each(model->services, (MSIterateFunc)linphone_presence_service_unref); ms_list_free(model->services); ms_list_for_each(model->persons, (MSIterateFunc)linphone_presence_person_unref); @@ -671,7 +673,22 @@ int linphone_presence_model_clear_persons(LinphonePresenceModel *model) { return 0; } +int linphone_presence_model_set_presentity(LinphonePresenceModel *model, const LinphoneAddress *presentity) { + + if (model->presentity) { + linphone_address_unref(model->presentity); + model->presentity = NULL; + } + if (presentity) { + model->presentity=linphone_address_clone(presentity); + linphone_address_clean(model->presentity); + } + return 0; +} +const LinphoneAddress * linphone_presence_model_get_presentity(const LinphonePresenceModel *model) { + return model->presentity; +} /***************************************************************************** * PRESENCE SERVICE FUNCTIONS TO GET ACCESS TO ALL FUNCTIONALITIES * @@ -1772,24 +1789,28 @@ static void write_xml_presence_person_obj(LinphonePresencePerson *person, struct 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; +char *linphone_presence_model_to_xml(LinphonePresenceModel *model) { + xmlBufferPtr buf = NULL; + xmlTextWriterPtr writer = NULL; int err; - - if ((contact == NULL) || (content == NULL)) return; - - model = (LinphonePresenceModel *)presence; + char *contact = NULL; + char * content = NULL; + + if (model->presentity) { + contact = linphone_address_as_string_uri_only(model->presentity); + } else { + ms_error("Cannot convert presence model [%p] to xml because no presentity set", model); + goto end; + } buf = xmlBufferCreate(); if (buf == NULL) { ms_error("Error creating the XML buffer"); - return; + goto end; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ms_error("Error creating the XML writer"); - return; + goto end; } xmlTextWriterSetIndent(writer,1); @@ -1815,7 +1836,7 @@ void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presen } else { struct _presence_service_obj_st st={0}; st.writer = writer; - st.contact = contact; + st.contact = contact; /*default value*/ st.err = &err; ms_list_for_each2(model->services, (MSIterate2Func)write_xml_presence_service_obj, &st); } @@ -1842,10 +1863,14 @@ void linphone_notify_convert_presence_to_xml(SalOp *op, SalPresenceModel *presen } if (err > 0) { /* xmlTextWriterEndDocument returns the size of the content. */ - *content = ms_strdup((char *)buf->content); + content = ms_strdup((char *)buf->content); } - xmlFreeTextWriter(writer); - xmlBufferFree(buf); + +end: + if (contact) ms_free(contact); + if (writer) xmlFreeTextWriter(writer); + if (buf) xmlBufferFree(buf); + return content; } void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model){ diff --git a/coreapi/private.h b/coreapi/private.h index 7236d025f..9c3b07f8f 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -398,6 +398,8 @@ void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrat void linphone_proxy_config_stop_refreshing(LinphoneProxyConfig *obj); void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc); void _linphone_proxy_config_release(LinphoneProxyConfig *cfg); +void _linphone_proxy_config_unpublish(LinphoneProxyConfig *obj); + /* * returns service route as defined in as defined by rfc3608, might be a list instead of just one. * Can be NULL @@ -595,7 +597,6 @@ struct _LinphoneProxyConfig int auth_failures; char *dial_prefix; LinphoneRegistrationState state; - SalOp *publish_op; LinphoneAVPFMode avpf_mode; bool_t commit; @@ -615,6 +616,8 @@ struct _LinphoneProxyConfig LinphoneAddress *saved_identity; /*---*/ LinphoneAddress *pending_contact; /*use to store previous contact in case of network failure*/ + LinphoneEvent *long_term_event; + unsigned long long previous_publish_config_hash[2]; }; @@ -1108,6 +1111,7 @@ SalReason linphone_reason_to_sal(LinphoneReason reason); LinphoneReason linphone_reason_from_sal(SalReason reason); LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, int expires); LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name); +void linphone_event_unpublish(LinphoneEvent *lev); /** * Useful for out of dialog notify * */ @@ -1438,6 +1442,8 @@ BELLE_SIP_DECLARE_VPTR(LinphoneTunnelConfig); int linphone_core_get_default_proxy_config_index(LinphoneCore *lc); +char *linphone_presence_model_to_xml(LinphonePresenceModel *model) ; + #ifdef __cplusplus } #endif diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 01c8ddcb7..cf16fddea 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -130,6 +130,50 @@ LinphoneProxyConfig *linphone_proxy_config_new() { return linphone_core_create_proxy_config(NULL); } +static char * append_linphone_address(LinphoneAddress *addr,char *out) { + char *res = out; + if (addr) { + char *tmp; + tmp = linphone_address_as_string(addr); + res = ms_strcat_printf(out, "%s",tmp); + ms_free(tmp); + } + return res; +}; +static char * append_string(const char * string,char *out) { + char *res = out; + if (string) { + res = ms_strcat_printf(out, "%s",string); + } + return res; +} +/* + * return true if computed value has changed + */ +bool_t linphone_proxy_config_compute_publish_params_hash(LinphoneProxyConfig * cfg) { + char * source = NULL; + char hash[33]; + char saved; + unsigned long long previous_hash[2]; + previous_hash[0] = cfg->previous_publish_config_hash[0]; + previous_hash[1] = cfg->previous_publish_config_hash[1]; + + source = ms_strcat_printf(source, "%i",cfg->privacy); + source=append_linphone_address(cfg->identity_address, source); + source=append_string(cfg->reg_proxy,source); + source=append_string(cfg->reg_route,source); + source=append_string(cfg->realm,source); + source = ms_strcat_printf(source, "%i",cfg->publish_expires); + source = ms_strcat_printf(source, "%i",cfg->publish); + belle_sip_auth_helper_compute_ha1(source, "dummy", "dummy", hash); + ms_free(source); + saved = hash[16]; + hash[16] = '\0'; + cfg->previous_publish_config_hash[0] = strtoull(hash, (char **)NULL, 16); + hash[16] = saved; + cfg->previous_publish_config_hash[1] = strtoull(&hash[16], (char **)NULL, 16); + return previous_hash[0] != cfg->previous_publish_config_hash[0] || previous_hash[1] != cfg->previous_publish_config_hash[1]; +} static void _linphone_proxy_config_destroy(LinphoneProxyConfig *cfg); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneProxyConfig); @@ -152,9 +196,9 @@ void _linphone_proxy_config_release_ops(LinphoneProxyConfig *cfg){ sal_op_release(cfg->op); cfg->op=NULL; } - if (cfg->publish_op){ - sal_op_release(cfg->publish_op); - cfg->publish_op=NULL; + if (cfg->long_term_event){ + linphone_event_unref(cfg->long_term_event); + cfg->long_term_event=NULL; } } @@ -320,15 +364,13 @@ void linphone_proxy_config_pause_register(LinphoneProxyConfig *cfg){ } void linphone_proxy_config_edit(LinphoneProxyConfig *cfg){ - if (cfg->publish && cfg->publish_op){ - /*unpublish*/ - sal_publish_presence(cfg->publish_op,NULL,NULL,0,(SalPresenceModel *)NULL); - sal_op_release(cfg->publish_op); - cfg->publish_op=NULL; - } /*store current config related to server location*/ linphone_proxy_config_store_server_config(cfg); - + linphone_proxy_config_compute_publish_params_hash(cfg); + + if (cfg->publish && cfg->long_term_event){ + linphone_event_pause_publish(cfg->long_term_event); + } /*stop refresher in any case*/ linphone_proxy_config_pause_register(cfg); } @@ -351,9 +393,10 @@ void linphone_proxy_config_stop_refreshing(LinphoneProxyConfig * cfg){ cfg->pending_contact=contact_addr; } - if (cfg->publish_op){ - sal_op_release(cfg->publish_op); - cfg->publish_op=NULL; + if (cfg->long_term_event){ /*might probably do better*/ + linphone_event_terminate(cfg->long_term_event); + linphone_event_unref(cfg->long_term_event); + cfg->long_term_event=NULL; } if (cfg->op){ sal_op_release(cfg->op); @@ -1001,6 +1044,26 @@ int linphone_proxy_config_done(LinphoneProxyConfig *cfg) sal_op_unref(cfg->op); /*but we keep refresher to handle authentication if needed*/ cfg->op=NULL; } + if (cfg->long_term_event) { + if (res == LinphoneProxyConfigAddressDifferent) { + _linphone_proxy_config_unpublish(cfg); + } + + } + } + if (linphone_proxy_config_compute_publish_params_hash(cfg)) { + ms_message("Publish params have changed on proxy config [%p]",cfg); + if (cfg->long_term_event) { + if (!cfg->publish) { + /*publish is terminated*/ + linphone_event_terminate(cfg->long_term_event); + } + linphone_event_unref(cfg->long_term_event); + cfg->long_term_event = NULL; + } + if (cfg->publish) cfg->send_publish=TRUE; + } else { + ms_message("Publish params have not changed on proxy config [%p]",cfg); } cfg->commit=TRUE; linphone_proxy_config_write_all_to_config_file(cfg->lc); @@ -1021,28 +1084,46 @@ void linphone_proxy_config_set_realm(LinphoneProxyConfig *cfg, const char *realm int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, LinphonePresenceModel *presence){ int err=0; - + if (proxy->state==LinphoneRegistrationOk || proxy->state==LinphoneRegistrationCleared){ - if (proxy->publish_op==NULL){ - const LinphoneAddress *to=linphone_proxy_config_get_identity_address(proxy); - proxy->publish_op=sal_op_new(proxy->lc->sal); - - linphone_configure_op(proxy->lc, proxy->publish_op, - to, NULL, FALSE); - - if (lp_config_get_int(proxy->lc->config,"sip","publish_msg_with_contact",0)){ - sal_op_set_contact_address(proxy->publish_op,linphone_proxy_config_get_identity_address(proxy)); - } + LinphoneContent *content; + char *presence_body; + if (proxy->long_term_event==NULL){ + proxy->long_term_event = linphone_core_create_publish(proxy->lc + , linphone_proxy_config_get_identity_address(proxy) + , "presence" + , linphone_proxy_config_get_publish_expires(proxy)); } - err=sal_publish_presence(proxy->publish_op - ,NULL - ,NULL - ,linphone_proxy_config_get_publish_expires(proxy) - ,(SalPresenceModel *)presence); + proxy->long_term_event->internal = TRUE; + + if (linphone_presence_model_get_presentity(presence) == NULL) { + ms_message("No presentity set for model [%p], using identity from proxy config [%p]", presence, proxy); + linphone_presence_model_set_presentity(presence,linphone_proxy_config_get_identity_address(proxy)); + } + + if (!(presence_body = linphone_presence_model_to_xml(presence))) { + ms_error("Cannot publish presence model [%p] for proxy config [%p] because of xml serilization error",presence,proxy); + return -1; + } + + content = linphone_content_new(); + linphone_content_set_buffer(content,presence_body,strlen(presence_body)); + linphone_content_set_type(content, "application"); + linphone_content_set_subtype(content,"pidf+xml"); + err = linphone_event_send_publish(proxy->long_term_event, content); + linphone_content_unref(content); }else proxy->send_publish=TRUE; /*otherwise do not send publish if registration is in progress, this will be done later*/ return err; } +void _linphone_proxy_config_unpublish(LinphoneProxyConfig *obj) { + if (obj->long_term_event + && (linphone_event_get_publish_state(obj->long_term_event) == LinphonePublishOk || + (linphone_event_get_publish_state(obj->long_term_event) == LinphoneIceStateInProgress && obj->publish_expires != 0))) { + linphone_event_unpublish(obj->long_term_event); + } +} + const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *cfg){ return cfg->reg_route; } @@ -1376,7 +1457,6 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){ if (can_register(cfg)){ linphone_proxy_config_register(cfg); cfg->commit=FALSE; - if (cfg->publish) cfg->send_publish=TRUE; } } if (cfg->send_publish && (cfg->state==LinphoneRegistrationOk || cfg->state==LinphoneRegistrationCleared)){ diff --git a/coreapi/sal.c b/coreapi/sal.c index 701d68b90..97d45f612 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -570,13 +570,6 @@ const SalAddress *sal_op_get_to_address(const SalOp *op){ return ((SalOpBase*)op)->to_address; } -const char *sal_op_get_route(const SalOp *op){ -#ifdef BELLE_SIP -ms_fatal("sal_op_get_route not supported, use sal_op_get_route_addresses instead"); -#endif - return ((SalOpBase*)op)->route; -} - const char *sal_op_get_remote_ua(const SalOp *op){ return ((SalOpBase*)op)->remote_ua; } @@ -696,6 +689,11 @@ void __sal_op_free(SalOp *op){ sal_custom_header_free(b->recv_custom_headers); if (b->sent_custom_headers) sal_custom_header_free(b->sent_custom_headers); + + if (b->entity_tag != NULL){ + ms_free(b->entity_tag); + b->entity_tag = NULL; + } ms_free(op); } @@ -1007,3 +1005,17 @@ char* sal_op_get_public_uri(SalOp *op) { } return NULL; } +const char *sal_op_get_entity_tag(const SalOp* op) { + SalOpBase* op_base = (SalOpBase*)op; + return op_base->entity_tag; +} +void sal_op_set_entity_tag(SalOp *op, const char* entity_tag) { + SalOpBase* op_base = (SalOpBase*)op; + if (op_base->entity_tag != NULL){ + ms_free(op_base->entity_tag); + } + if (entity_tag) + op_base->entity_tag = ms_strdup(entity_tag); + else + op_base->entity_tag = NULL; +} diff --git a/coreapi/vtables.c b/coreapi/vtables.c index a8840b5eb..7b1f6dd60 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -250,7 +250,7 @@ void linphone_core_notify_subscription_state_changed(LinphoneCore *lc, LinphoneE } void linphone_core_notify_publish_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphonePublishState state) { - NOTIFY_IF_EXIST(publish_state_changed, lc,lev,state); + NOTIFY_IF_EXIST_INTERNAL(publish_state_changed, linphone_event_is_internal(lev), lc, lev, state); cleanup_dead_vtable_refs(lc); } diff --git a/include/sal/sal.h b/include/sal/sal.h index 07483faa0..fa42fe24c 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -354,6 +354,7 @@ typedef struct SalOpBase{ SalAddress* service_route; /*as defined by rfc3608, might be a list*/ SalCustomHeader *sent_custom_headers; SalCustomHeader *recv_custom_headers; + char* entity_tag; /*as defined by rfc3903 (I.E publih)*/ } SalOpBase; @@ -641,6 +642,8 @@ void sal_op_set_to(SalOp *op, const char *to); void sal_op_set_to_address(SalOp *op, const SalAddress *to); SalOp *sal_op_ref(SalOp* h); void sal_op_stop_refreshing(SalOp *op); +int sal_op_refresh(SalOp *op); + void sal_op_release(SalOp *h); /*same as release, but does not stop refresher if any*/ void* sal_op_unref(SalOp* op); @@ -654,7 +657,6 @@ const SalAddress *sal_op_get_from_address(const SalOp *op); const char *sal_op_get_to(const SalOp *op); const SalAddress *sal_op_get_to_address(const SalOp *op); const SalAddress *sal_op_get_contact_address(const SalOp *op); -const char *sal_op_get_route(const SalOp *op); const MSList* sal_op_get_route_addresses(const SalOp *op); const char *sal_op_get_proxy(const SalOp *op); /*raw contact header value with header params*/ @@ -684,6 +686,10 @@ const SalErrorInfo *sal_op_get_error_info(const SalOp *op); void sal_error_info_reset(SalErrorInfo *ei); void sal_error_info_set(SalErrorInfo *ei, SalReason reason, int code, const char *status_string, const char *warning); +/*entity tag used for publish (see RFC 3903)*/ +const char *sal_op_get_entity_tag(const SalOp* op); +void sal_op_set_entity_tag(SalOp *op, const char* entity_tag); + /*Call API*/ int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc); int sal_call(SalOp *h, const char *from, const char *to); @@ -740,8 +746,9 @@ 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, int expires, SalPresenceModel *presence); - +//int sal_publish_presence(SalOp *op, const char *from, const char *to, int expires, SalPresenceModel *presence); +SalBodyHandler *sal_presence_model_create_body_handler(SalPresenceModel *presence); + /*ping: main purpose is to obtain its own contact address behind firewalls*/ int sal_ping(SalOp *op, const char *from, const char *to); @@ -754,11 +761,11 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event int sal_unsubscribe(SalOp *op); int sal_subscribe_accept(SalOp *op); int sal_subscribe_decline(SalOp *op, SalReason reason); -int sal_subscribe_refresh(SalOp *op); int sal_notify(SalOp *op, const SalBodyHandler *body); int sal_notify_close(SalOp *op); int sal_publish(SalOp *op, const char *from, const char *to, const char*event_name, int expires, const SalBodyHandler *body); - +int sal_op_unpublish(SalOp *op); + /*privacy, must be in sync with LinphonePrivacyMask*/ typedef enum _SalPrivacy { SalPrivacyNone=0x0, diff --git a/tester/presence_tester.c b/tester/presence_tester.c index ebb4350d0..32d3e7255 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -130,7 +130,10 @@ static void simple_publish_with_expire(int expires) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneProxyConfig* proxy; LinphonePresenceModel* presence; - + LinphoneCoreVTable *vtable = linphone_core_v_table_new(); + vtable->publish_state_changed = linphone_publish_state_changed; + _linphone_core_add_listener(marie->lc, vtable, TRUE, TRUE ); + proxy = linphone_core_get_default_proxy_config(marie->lc); linphone_proxy_config_edit(proxy); if (expires >0) { @@ -138,11 +141,44 @@ static void simple_publish_with_expire(int expires) { } linphone_proxy_config_enable_publish(proxy,TRUE); linphone_proxy_config_done(proxy); - wait_core(marie->lc); + + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,1)); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishOk,1)); + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,NULL); linphone_core_set_presence_model(marie->lc,presence); - wait_core(marie->lc); + + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,2)); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishOk,2)); + + linphone_proxy_config_edit(proxy); + linphone_proxy_config_done(proxy); + /*make sure no publish is sent*/ + BC_ASSERT_FALSE(wait_for_until(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,3,expires*1000/2)); + + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,FALSE); + linphone_proxy_config_done(proxy); + + /*BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,3));*/ + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishCleared,1)); + + linphone_proxy_config_edit(proxy); + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_done(proxy); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,3)); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishOk,3)); + + linphone_proxy_config_edit(proxy); + linphone_proxy_config_set_publish_expires(proxy, linphone_proxy_config_get_publish_expires(proxy)+1); + linphone_proxy_config_done(proxy); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishProgress,4)); + BC_ASSERT_TRUE(wait_for(marie->lc,marie->lc,&marie->stat.number_of_LinphonePublishOk,4)); + linphone_core_manager_destroy(marie); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishCleared,2,int,"%i"); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphonePublishOk,4,int,"%i"); + } static void simple_publish(void) { @@ -150,7 +186,7 @@ static void simple_publish(void) { } static void publish_with_expires(void) { - simple_publish_with_expire(1); + simple_publish_with_expire(2); } static bool_t subscribe_to_callee_presence(LinphoneCoreManager* caller_mgr,LinphoneCoreManager* callee_mgr) { @@ -489,7 +525,7 @@ static void test_subscribe_notify_publish(void) { LpConfig *pauline_lp; char* lf_identity; LinphoneFriend *lf; - + linphone_core_set_user_agent(marie->lc, "full-presence-support", NULL); linphone_core_set_user_agent(pauline->lc, "full-presence-support", NULL); pauline_lp = linphone_core_get_config(pauline->lc); @@ -501,7 +537,7 @@ static void test_subscribe_notify_publish(void) { linphone_core_add_friend(pauline->lc,lf); /*wait for subscribe acknowledgment*/ - wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,1,2000); + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,1,2000)); BC_ASSERT_EQUAL(LinphoneStatusOffline,linphone_friend_get_status(lf), int, "%d"); /*enable publish*/ @@ -514,18 +550,18 @@ static void test_subscribe_notify_publish(void) { linphone_proxy_config_done(proxy); /*wait for marie status*/ - wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,2,2000); + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,2,2000)); BC_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf), int, "%d"); presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityBusy,NULL); linphone_core_set_presence_model(marie->lc,presence); /*wait for new status*/ - wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,3,2000); + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,3,2000)); BC_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf), int, "%d"); /*wait for refresh*/ - wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,4,5000); + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyPresenceReceived,4,5000)); BC_ASSERT_EQUAL(LinphoneStatusBusy,linphone_friend_get_status(lf), int, "%d"); /*linphone_core_remove_friend(pauline->lc,lf);*/ @@ -534,10 +570,24 @@ static void test_subscribe_notify_publish(void) { BC_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf), int, "%d"); */ - /*Expect a notify at publication expiration*/ - wait_for_until(pauline->lc,pauline->lc,&pauline->stat.number_of_NotifyPresenceReceived,6,5000); + /*Expect a notify at publication expiration because marie is no longuer scheduled*/ + BC_ASSERT_TRUE(wait_for_until(pauline->lc,pauline->lc,&pauline->stat.number_of_NotifyPresenceReceived,6,5000)); BC_ASSERT_EQUAL(LinphoneStatusOffline,linphone_friend_get_status(lf), int, "%d"); + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphonePresenceActivityBusy,4,5000));/*re- schedule marie to clean up things*/ + + /*simulate a rapid presence change to make sure only first and last are transmited*/ + linphone_core_set_presence_model(marie->lc,linphone_presence_model_new_with_activity(LinphonePresenceActivityAway,NULL)); + linphone_core_set_presence_model(marie->lc,linphone_presence_model_new_with_activity(LinphonePresenceActivityBreakfast,NULL)); + linphone_core_set_presence_model(marie->lc,linphone_presence_model_new_with_activity(LinphonePresenceActivityAppointment,NULL)); + + BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphonePresenceActivityAppointment,1,5000)); + + BC_ASSERT_EQUAL(pauline->stat.number_of_LinphonePresenceActivityAway, 1, int,"%i"); + BC_ASSERT_EQUAL(pauline->stat.number_of_LinphonePresenceActivityBreakfast, 0, int,"%i"); + BC_ASSERT_EQUAL(pauline->stat.number_of_LinphonePresenceActivityAppointment, 1, int,"%i"); + + linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -727,6 +777,7 @@ static void test_presence_list(void) { lf = linphone_friend_list_find_friend_by_uri(pauline->lc->friendlist, marie_identity); BC_ASSERT_EQUAL(linphone_friend_get_status(lf), LinphoneStatusOnThePhone, int, "%d"); + ms_message("Disabling publish"); enable_publish(laure, FALSE); enable_publish(marie, FALSE); enable_publish(pauline, FALSE); @@ -829,7 +880,7 @@ static void test_presence_list_subscribe_with_error(bool_t io_error) { const char *pauline_identity; MSList* lcs = NULL; int dummy = 0; - lp_config_set_int(laure->lc->config, "sip", "rls_presence_expires", 3); + lp_config_set_int(laure->lc->config, "sip", "rls_presence_expires", 5); pauline_identity = get_identity(pauline); @@ -853,7 +904,7 @@ static void test_presence_list_subscribe_with_error(bool_t io_error) { wait_for_list(lcs, &dummy, 1, 2000); /* Wait a little bit for the subscribe to happen */ enable_publish(pauline, TRUE); - wait_for_list(lcs, &pauline->stat.number_of_NotifyPresenceReceived, 1, 2000); + BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_LinphonePresenceActivityVacation, 1, 6000)); BC_ASSERT_GREATER(laure->stat.number_of_NotifyPresenceReceived, 1, int, "%d"); BC_ASSERT_GREATER(laure->lc->friendlist->expected_notification_version, 1, int, "%d"); lf = linphone_friend_list_find_friend_by_uri(laure->lc->friendlist, pauline_identity); @@ -865,25 +916,26 @@ static void test_presence_list_subscribe_with_error(bool_t io_error) { BC_ASSERT_EQUAL(lf->presence_received, FALSE, int, "%d"); BC_ASSERT_EQUAL(lf->subscribe_active, TRUE, int, "%d"); - BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_NotifyPresenceReceived, 2, 5000)); + BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_LinphonePresenceActivityVacation, 2, 6000)); if (io_error) { ms_message("Simulating socket error"); sal_set_recv_error(laure->lc->sal, -1); - wait_for_list(lcs, &dummy, 1, 1000); /* just time for socket to be closed */ - + wait_for_list(lcs, &dummy, 1, 500); /* just time for socket to be closed */ } else { ms_message("Simulating in/out packets losses"); sal_set_send_error(laure->lc->sal,1500); /*make sure no refresh is sent, trash the message without generating error*/ sal_set_recv_error(laure->lc->sal, 1500); /*make sure server notify to close the dialog is also ignored*/ - wait_for_list(lcs, &dummy, 1, 3000); /* Wait a little bit for the subscribe to happen */ + wait_for_list(lcs, &dummy, 1, 5000); /* Wait a little bit for the subscribe to happen */ } /*restart normal behavior*/ sal_set_send_error(laure->lc->sal,0); sal_set_recv_error(laure->lc->sal, 1); + BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_LinphonePresenceActivityVacation, 3, 6000)); /* give time for subscription to recover to avoid to receive 491 Request pending*/ + linphone_core_set_presence_model(pauline->lc, linphone_core_create_presence_model_with_activity(pauline->lc, LinphonePresenceActivityAway, NULL)); - BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_NotifyPresenceReceived, 3, 5000)); + BC_ASSERT_TRUE(wait_for_until(laure->lc, pauline->lc, &laure->stat.number_of_LinphonePresenceActivityAway, 1, 6000)); lf = linphone_friend_list_find_friend_by_uri(laure->lc->friendlist, pauline_identity); BC_ASSERT_EQUAL(linphone_friend_get_status(lf), LinphoneStatusAway, int, "%d"); diff --git a/tester/register_tester.c b/tester/register_tester.c index a8fa71a82..4c062af9e 100644 --- a/tester/register_tester.c +++ b/tester/register_tester.c @@ -235,7 +235,7 @@ static void change_expires(void){ LinphoneProxyConfig* proxy_config; register_with_refresh_base(lcm->lc,FALSE,NULL,NULL); - linphone_core_get_default_proxy(lcm->lc,&proxy_config); + proxy_config = linphone_core_get_default_proxy_config(lcm->lc); linphone_proxy_config_edit(proxy_config); reset_counters(counters); /*clear stats*/