/* * Client presence publication callback. */ void SIPPresence::publish_cb(struct pjsip_publishc_cbparam *param) { SIPPresence *pres = (SIPPresence*) param->token; if (param->code / 100 != 2 || param->status != PJ_SUCCESS) { pjsip_publishc_destroy(param->pubc); pres->publish_sess_ = NULL; std::ostringstream os; os << param->code; const std::string error = os.str() + " / "+ std::string(param->reason.ptr, param->reason.slen); if (param->status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(param->status, errmsg, sizeof(errmsg)); ERROR("Client (PUBLISH) failed, status=%d, msg=%s", param->status, errmsg); Manager::instance().getClient()->getPresenceManager()->serverError( pres->getAccount()->getAccountID(), error, errmsg); } else if (param->code == 412) { /* 412 (Conditional Request Failed) * The PUBLISH refresh has failed, retry with new one. */ WARN("Publish retry."); publish(pres); } else if ((param->code == PJSIP_SC_BAD_EVENT) || (param->code == PJSIP_SC_NOT_IMPLEMENTED)){ //489 or 501 WARN("Client (PUBLISH) failed (%s)",error.c_str()); Manager::instance().getClient()->getPresenceManager()->serverError( pres->getAccount()->getAccountID(), error, "Publish not supported."); pres->getAccount()->supportPresence(PRESENCE_FUNCTION_PUBLISH, false); } } else { if (param->expiration < 1) { /* Could happen if server "forgot" to include Expires header * in the response. We will not renew, so destroy the pubc. */ pjsip_publishc_destroy(param->pubc); pres->publish_sess_ = NULL; } pres->getAccount()->supportPresence(PRESENCE_FUNCTION_PUBLISH, true); } }
/* * Client presence publication callback. */ static void publish_cb(struct pjsip_publishc_cbparam *param) { pjsua_acc *acc = (pjsua_acc*) param->token; if (param->code/100 != 2 || param->status != PJ_SUCCESS) { pjsip_publishc_destroy(param->pubc); acc->publish_sess = NULL; if (param->status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(param->status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "Client publication (PUBLISH) failed, status=%d, msg=%s", param->status, errmsg)); } else if (param->code == 412) { /* 412 (Conditional Request Failed) * The PUBLISH refresh has failed, retry with new one. */ pjsua_pres_init_publish_acc(acc->index); } else { PJ_LOG(1,(THIS_FILE, "Client publication (PUBLISH) failed (%d/%.*s)", param->code, (int)param->reason.slen, param->reason.ptr)); } } else { if (param->expiration == -1) { /* Could happen if server "forgot" to include Expires header * in the response. We will not renew, so destroy the pubc. */ pjsip_publishc_destroy(param->pubc); acc->publish_sess = NULL; } } }
/* Terminate server subscription for the account */ void pjsua_pres_delete_acc(int acc_id) { pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_srv_pres *uapres; uapres = pjsua_var.acc[acc_id].pres_srv_list.next; /* Notify all subscribers that we're no longer available */ while (uapres != &acc->pres_srv_list) { pjsip_pres_status pres_status; pj_str_t reason = { "noresource", 10 }; pjsua_srv_pres *next; pjsip_tx_data *tdata; next = uapres->next; pjsip_pres_get_status(uapres->sub, &pres_status); pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; pjsip_pres_set_status(uapres->sub, &pres_status); if (pjsip_pres_notify(uapres->sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &reason, &tdata)==PJ_SUCCESS) { pjsip_pres_send_request(uapres->sub, tdata); } uapres = next; } /* Clear server presence subscription list because account might be reused * later. */ pj_list_init(&acc->pres_srv_list); /* Terminate presence publication, if any */ if (acc->publish_sess) { acc->online_status = PJ_FALSE; send_publish(acc_id, PJ_FALSE); if (acc->publish_sess) { pjsip_publishc_destroy(acc->publish_sess); acc->publish_sess = NULL; } acc_cfg->publish_enabled = PJ_FALSE; } }
/* * Client presence publication callback. */ static void publish_cb(struct pjsip_publishc_cbparam *param) { pjsua_acc *acc = (pjsua_acc*) param->token; if (param->code/100 != 2 || param->status != PJ_SUCCESS) { if (param->status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(param->status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "Client publication (PUBLISH) failed, status=%d, msg=%s", param->status, errmsg)); } else { PJ_LOG(1,(THIS_FILE, "Client publication (PUBLISH) failed (%d/%.*s)", param->code, (int)param->reason.slen, param->reason.ptr)); } pjsip_publishc_destroy(param->pubc); acc->publish_sess = NULL; } }
/* * Send PUBLISH request. */ pj_status_t SIPPresence::send_publish(SIPPresence * pres) { pjsip_tx_data *tdata; pj_status_t status; DEBUG("Send PUBLISH (%s).", pres->getAccount()->getAccountID().c_str()); SIPAccount * acc = pres->getAccount(); std::string contactWithAngles = acc->getFromUri(); contactWithAngles.erase(contactWithAngles.find('>')); int semicolon = contactWithAngles.find_first_of(":"); std::string contactWithoutAngles = contactWithAngles.substr(semicolon + 1); // pj_str_t contact = pj_str(strdup(contactWithoutAngles.c_str())); // pj_memcpy(&status_data.info[0].contact, &contt, sizeof(pj_str_t));; /* Create PUBLISH request */ char *bpos; pj_str_t entity; status = pjsip_publishc_publish(pres->publish_sess_, PJ_TRUE, &tdata); pj_str_t from = pj_strdup3(pres->pool_, acc->getFromUri().c_str()); if (status != PJ_SUCCESS) { ERROR("Error creating PUBLISH request", status); goto on_error; } if ((bpos = pj_strchr(&from, '<')) != NULL) { char *epos = pj_strchr(&from, '>'); if (epos - bpos < 2) { pj_assert(!"Unexpected invalid URI"); status = PJSIP_EINVALIDURI; goto on_error; } entity.ptr = bpos + 1; entity.slen = epos - bpos - 1; } else { entity = from; } /* Create and add PIDF message body */ status = pjsip_pres_create_pidf(tdata->pool, pres->getStatus(), &entity, &tdata->msg->body); pres_msg_data msg_data; if (status != PJ_SUCCESS) { ERROR("Error creating PIDF for PUBLISH request"); pjsip_tx_data_dec_ref(tdata); goto on_error; } pj_bzero(&msg_data, sizeof(msg_data)); pj_list_init(&msg_data.hdr_list); pjsip_media_type_init(&msg_data.multipart_ctype, NULL, NULL); pj_list_init(&msg_data.multipart_parts); pres->fillDoc(tdata, &msg_data); /* Send the PUBLISH request */ status = pjsip_publishc_send(pres->publish_sess_, tdata); if (status == PJ_EPENDING) { WARN("Previous request is in progress, "); } else if (status != PJ_SUCCESS) { ERROR("Error sending PUBLISH request"); goto on_error; } return PJ_SUCCESS; on_error: if (pres->publish_sess_) { pjsip_publishc_destroy(pres->publish_sess_); pres->publish_sess_ = NULL; } return status; }
static void tsx_callback(void *token, pjsip_event *event) { pj_status_t status; pjsip_publishc *pubc = (pjsip_publishc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; /* Decrement pending transaction counter. */ pj_assert(pubc->pending_tsx > 0); --pubc->pending_tsx; /* If publication data has been deleted by user then remove publication * data from transaction's callback, and don't call callback. */ if (pubc->_delete_flag) { /* Nothing to do */ ; } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &pubc->auth_sess, rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { call_callback(pubc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1); } else { status = pjsip_publishc_send(pubc, tdata); } } else { pjsip_rx_data *rdata; pj_int32_t expiration = 0xFFFF; if (tsx->status_code/100 == 2) { pjsip_msg *msg; pjsip_expires_hdr *expires; pjsip_generic_string_hdr *etag_hdr; const pj_str_t STR_ETAG = { "SIP-ETag", 8 }; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; /* Save ETag value */ etag_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL); if (etag_hdr) { pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue); } else { pubc->etag.slen = 0; } /* Update expires value */ expires = (pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (expires) expiration = expires->ivalue; if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) { pj_time_val delay = { 0, 0}; delay.sec = expiration - DELAY_BEFORE_REFRESH; if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED && delay.sec > (pj_int32_t)pubc->expires) { delay.sec = pubc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; pubc->timer.cb = &pubc_refresh_timer_cb; pubc->timer.id = REFRESH_TIMER; pubc->timer.user_data = pubc; pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay); pj_gettimeofday(&pubc->last_refresh); pubc->next_refresh = pubc->last_refresh; pubc->next_refresh.sec += delay.sec; } } else { rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL; } /* Call callback. */ if (expiration == 0xFFFF) expiration = -1; /* Temporarily increment pending_tsx to prevent callback from * destroying pubc. */ ++pubc->pending_tsx; call_callback(pubc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : pjsip_get_status_text(tsx->status_code)), rdata, expiration); --pubc->pending_tsx; } /* Delete the record if user destroy pubc during the callback. */ if (pubc->_delete_flag && pubc->pending_tsx==0) { pjsip_publishc_destroy(pubc); } }
/* * Send PUBLISH request. */ static pj_status_t send_publish(int acc_id, pj_bool_t active) { pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_pres_status pres_status; pjsip_tx_data *tdata; pj_status_t status; /* Create PUBLISH request */ if (active) { char *bpos; pj_str_t entity; status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } /* Set our online status: */ pj_bzero(&pres_status, sizeof(pres_status)); pres_status.info_cnt = 1; pres_status.info[0].basic_open = acc->online_status; pres_status.info[0].id = acc->cfg.pidf_tuple_id; /* .. including RPID information */ pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, sizeof(pjrpid_element)); /* Be careful not to send PIDF with presence entity ID containing * "<" character. */ if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) { char *epos = pj_strchr(&acc_cfg->id, '>'); if (epos - bpos < 2) { pj_assert(!"Unexpected invalid URI"); status = PJSIP_EINVALIDURI; goto on_error; } entity.ptr = bpos+1; entity.slen = epos - bpos - 1; } else { entity = acc_cfg->id; } /* Create and add PIDF message body */ status = pjsip_pres_create_pidf(tdata->pool, &pres_status, &entity, &tdata->msg->body); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request", status); pjsip_tx_data_dec_ref(tdata); goto on_error; } } else { status = pjsip_publishc_unpublish(acc->publish_sess, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } } /* Add headers etc */ pjsua_process_msg_data(tdata, NULL); /* Send the PUBLISH request */ status = pjsip_publishc_send(acc->publish_sess, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status); goto on_error; } acc->publish_state = acc->online_status; return PJ_SUCCESS; on_error: if (acc->publish_sess) { pjsip_publishc_destroy(acc->publish_sess); acc->publish_sess = NULL; } return status; }
static void tsx_callback(void *token, pjsip_event *event) { pj_status_t status; pjsip_publishc *pubc = (pjsip_publishc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; /* Decrement pending transaction counter. */ pj_assert(pubc->pending_tsx > 0); --pubc->pending_tsx; /* Mark that we're in callback to prevent deletion (#1164) */ ++pubc->in_callback; /* If publication data has been deleted by user then remove publication * data from transaction's callback, and don't call callback. */ if (pubc->_delete_flag) { /* Nothing to do */ ; } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &pubc->auth_sess, rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { call_callback(pubc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1); } else { status = pjsip_publishc_send(pubc, tdata); } } else { pjsip_rx_data *rdata; pj_int32_t expiration = 0xFFFF; if (tsx->status_code/100 == 2) { pjsip_msg *msg; pjsip_expires_hdr *expires; pjsip_generic_string_hdr *etag_hdr; const pj_str_t STR_ETAG = { "SIP-ETag", 8 }; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; /* Save ETag value */ etag_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL); if (etag_hdr) { pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue); } else { pubc->etag.slen = 0; } /* Update expires value */ expires = (pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (pubc->auto_refresh && expires) expiration = expires->ivalue; if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) { pj_time_val delay = { 0, 0}; /* Cancel existing timer, if any */ if (pubc->timer.id != 0) { pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); pubc->timer.id = 0; } delay.sec = expiration - DELAY_BEFORE_REFRESH; if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED && delay.sec > (pj_int32_t)pubc->expires) { delay.sec = pubc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; pubc->timer.cb = &pubc_refresh_timer_cb; pubc->timer.id = REFRESH_TIMER; pubc->timer.user_data = pubc; pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay); pj_gettimeofday(&pubc->last_refresh); pubc->next_refresh = pubc->last_refresh; pubc->next_refresh.sec += delay.sec; } } else { rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL; } /* Call callback. */ if (expiration == 0xFFFF) expiration = -1; /* Temporarily increment pending_tsx to prevent callback from * destroying pubc. */ ++pubc->pending_tsx; call_callback(pubc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : pjsip_get_status_text(tsx->status_code)), rdata, expiration); --pubc->pending_tsx; /* If we have pending request(s), send them now */ pj_mutex_lock(pubc->mutex); while (!pj_list_empty(&pubc->pending_reqs)) { pjsip_tx_data *tdata = pubc->pending_reqs.next; pj_list_erase(tdata); /* Add SIP-If-Match if we have etag and the request doesn't have * one (http://trac.pjsip.org/repos/ticket/996) */ if (pubc->etag.slen) { const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; pjsip_generic_string_hdr *sim_hdr; sim_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL); if (!sim_hdr) { /* Create the header */ sim_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &pubc->etag); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr); } else { /* Update */ if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue)) pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag); } } status = pjsip_publishc_send(pubc, tdata); if (status == PJ_EPENDING) { pj_assert(!"Not expected"); pj_list_erase(tdata); pjsip_tx_data_dec_ref(tdata); } else if (status == PJ_SUCCESS) { break; } } pj_mutex_unlock(pubc->mutex); } /* No longer in callback. */ --pubc->in_callback; /* Delete the record if user destroy pubc during the callback. */ if (pubc->_delete_flag && pubc->pending_tsx==0) { pjsip_publishc_destroy(pubc); } }