void SIPPresence::fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data) { if (tdata->msg->type == PJSIP_REQUEST_MSG) { const pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent"); std::string useragent(acc_->getUserAgentName()); pj_str_t pJuseragent = pj_str((char*) useragent.c_str()); pjsip_hdr *h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent); pjsip_msg_add_hdr(tdata->msg, h); } if (msg_data == NULL) return; const pjsip_hdr *hdr; hdr = msg_data->hdr_list.next; while (hdr && hdr != &msg_data->hdr_list) { pjsip_hdr *new_hdr; new_hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr); RING_DBG("adding header %p", new_hdr->name.ptr); pjsip_msg_add_hdr(tdata->msg, new_hdr); hdr = hdr->next; } if (msg_data->content_type.slen && msg_data->msg_body.slen) { pjsip_msg_body *body; const pj_str_t type = CONST_PJ_STR("application"); const pj_str_t subtype = CONST_PJ_STR("pidf+xml"); body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &msg_data->msg_body); tdata->msg->body = body; } }
void SIPPresence::updateStatus(bool status, const std::string ¬e) { //char* pj_note = (char*) pj_pool_alloc(pool_, "50"); pjrpid_element rpid = { PJRPID_ELEMENT_TYPE_PERSON, CONST_PJ_STR("0"), PJRPID_ACTIVITY_UNKNOWN, pj_str((char *) note.c_str()) }; /* fill activity if user not available. */ if (note == "away") rpid.activity = PJRPID_ACTIVITY_AWAY; else if (note == "busy") rpid.activity = PJRPID_ACTIVITY_BUSY; /* else // TODO: is there any other possibilities RING_DBG("Presence : no activity"); */ pj_bzero(&status_data_, sizeof(status_data_)); status_data_.info_cnt = 1; status_data_.info[0].basic_open = status; // at most we will have 3 digits + NULL termination char buf[4]; pj_utoa(rand() % 1000, buf); status_data_.info[0].id = pj_strdup3(pool_, buf); pj_memcpy(&status_data_.info[0].rpid, &rpid, sizeof(pjrpid_element)); /* "contact" field is optionnal */ }
void im::sendSipMessage(pjsip_inv_session* session, const std::map<std::string, std::string>& payloads) { if (payloads.empty()) { RING_WARN("the payloads argument is empty; ignoring message"); return; } const pjsip_method msg_method = {PJSIP_OTHER_METHOD, CONST_PJ_STR("MESSAGE")}; { auto dialog = session->dlg; sip_utils::PJDialogLock dialog_lock {dialog}; pjsip_tx_data* tdata = nullptr; auto status = pjsip_dlg_create_request(dialog, &msg_method, -1, &tdata); if (status != PJ_SUCCESS) { RING_ERR("pjsip_dlg_create_request failed: %s", sip_utils::sip_strerror(status).c_str()); throw InstantMessageException("Internal SIP error"); } fillPJSIPMessageBody(*tdata, payloads); status = pjsip_dlg_send_request(dialog, tdata, -1, nullptr); if (status != PJ_SUCCESS) { RING_ERR("pjsip_dlg_send_request failed: %s", sip_utils::sip_strerror(status).c_str()); throw InstantMessageException("Internal SIP error"); } } }
/* Create client publish session */ pj_status_t SIPPresence::publish(SIPPresence *pres) { pj_status_t status; const pj_str_t STR_PRESENCE = CONST_PJ_STR("presence"); SIPAccount * acc = pres->getAccount(); pjsip_endpoint *endpt = getSIPVoIPLink()->getEndpoint(); /* Create and init client publication session */ /* Create client publication */ status = pjsip_publishc_create(endpt, &my_publish_opt, pres, &publish_cb, &pres->publish_sess_); if (status != PJ_SUCCESS) { pres->publish_sess_ = NULL; RING_ERR("Failed to create a publish seesion."); return status; } /* Initialize client publication */ pj_str_t from = pj_strdup3(pres->pool_, acc->getFromUri().c_str()); status = pjsip_publishc_init(pres->publish_sess_, &STR_PRESENCE, &from, &from, &from, 0xFFFF); if (status != PJ_SUCCESS) { RING_ERR("Failed to init a publish session"); pres->publish_sess_ = NULL; return status; } /* Add credential for authentication */ if (acc->hasCredentials() and pjsip_publishc_set_credentials(pres->publish_sess_, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) { RING_ERR("Could not initialize credentials for invite session authentication"); return status; } /* Set route-set */ // FIXME: is this really necessary? pjsip_regc *regc = acc->getRegistrationInfo(); if (regc and acc->hasServiceRoute()) pjsip_regc_set_route_set(regc, sip_utils::createRouteSet(acc->getServiceRoute(), pres->getPool())); /* Send initial PUBLISH request */ status = send_publish(pres); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; }
pj_bool_t PresSubServer::pres_on_rx_subscribe_request(pjsip_rx_data *rdata) { pjsip_method *method = &rdata->msg_info.msg->line.req.method; pj_str_t *str = &method->name; std::string request(str->ptr, str->slen); // pj_str_t contact; pj_status_t status; pjsip_dialog *dlg; pjsip_evsub *sub; pjsip_evsub_user pres_cb; pjsip_expires_hdr *expires_hdr; pjsip_status_code st_code; pj_str_t reason; pres_msg_data msg_data; pjsip_evsub_state ev_state; /* Only hande incoming subscribe messages should be processed here. * Otherwise we return FALSE to let other modules handle it */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0) return PJ_FALSE; /* debug msg */ std::string name(rdata->msg_info.to->name.ptr, rdata->msg_info.to->name.slen); std::string server(rdata->msg_info.from->name.ptr, rdata->msg_info.from->name.slen); RING_DBG("Incoming pres_on_rx_subscribe_request for %s, name:%s, server:%s." , request.c_str() , name.c_str() , server.c_str()); /* get parents*/ auto account = Manager::instance().getIP2IPAccount(); auto sipaccount = static_cast<SIPAccount *>(account.get()); if (!sipaccount) { RING_ERR("Could not find account IP2IP"); return PJ_FALSE; } pjsip_endpoint *endpt = getSIPVoIPLink()->getEndpoint(); SIPPresence * pres = sipaccount->getPresence(); pres->lock(); /* Create UAS dialog: */ const pj_str_t contact(sipaccount->getContactHeader()); status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); RING_WARN("Unable to create UAS dialog for subscription: %s [status=%d]", errmsg, status); pres->unlock(); pjsip_endpt_respond_stateless(endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } /* Init callback: */ pj_bzero(&pres_cb, sizeof(pres_cb)); pres_cb.on_evsub_state = &pres_evsub_on_srv_state; /* Create server presence subscription: */ status = pjsip_pres_create_uas(dlg, &pres_cb, rdata, &sub); if (status != PJ_SUCCESS) { int code = PJSIP_ERRNO_TO_SIP_STATUS(status); pjsip_tx_data *tdata; RING_WARN("Unable to create server subscription %d", status); if (code == 599 || code > 699 || code < 300) { code = 400; } status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); if (status == PJ_SUCCESS) { pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); } pres->unlock(); return PJ_FALSE; } /* Attach our data to the subscription: */ char* remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, remote, PJSIP_MAX_URL_SIZE); if (status < 1) pj_ansi_strcpy(remote, "<-- url is too long-->"); else remote[status] = '\0'; //pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, dlg->local.info->uri, contact.ptr, PJSIP_MAX_URL_SIZE); /* Create a new PresSubServer server and wait for client approve */ PresSubServer *presSubServer = new PresSubServer(pres, sub, remote, dlg); pjsip_evsub_set_mod_data(sub, pres->getModId(), presSubServer); // Notify the client. emitSignal<DRing::PresenceSignal::NewServerSubscriptionRequest>(presSubServer->remote_); pres->addPresSubServer(presSubServer); /* Capture the value of Expires header. */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) presSubServer->setExpires(expires_hdr->ivalue); else presSubServer->setExpires(-1); st_code = (pjsip_status_code) 200; reason = CONST_PJ_STR("OK"); 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); /* Create and send 2xx response to the SUBSCRIBE request: */ status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list); if (status != PJ_SUCCESS) { RING_WARN("Unable to accept presence subscription %d", status); pjsip_pres_terminate(sub, PJ_FALSE); pres->unlock(); return PJ_FALSE; } // Unsubscribe case ev_state = PJSIP_EVSUB_STATE_ACTIVE; if (presSubServer->getExpires() == 0) { // PJSIP_EVSUB_STATE_TERMINATED pres->unlock(); return PJ_TRUE; } /*Send notify immediatly. Replace real status with fake.*/ // pjsip_pres_set_status(sub, pres->getStatus()); // real status // fake temporary status pjrpid_element rpid = { PJRPID_ELEMENT_TYPE_PERSON, CONST_PJ_STR("20"), PJRPID_ACTIVITY_UNKNOWN, CONST_PJ_STR("") // empty note by default }; pjsip_pres_status fake_status_data; pj_bzero(&fake_status_data, sizeof(pjsip_pres_status)); fake_status_data.info_cnt = 1; fake_status_data.info[0].basic_open = false; fake_status_data.info[0].id = CONST_PJ_STR("0"); /* todo: tuplie_id*/ pj_memcpy(&fake_status_data.info[0].rpid, &rpid, sizeof(pjrpid_element)); pjsip_pres_set_status(sub, &fake_status_data); /* Create and send the the first NOTIFY to active subscription: */ pj_str_t stateStr = CONST_PJ_STR(""); pjsip_tx_data *tdata = NULL; status = pjsip_pres_notify(sub, ev_state, &stateStr, &reason, &tdata); if (status == PJ_SUCCESS) { pres->fillDoc(tdata, &msg_data); status = pjsip_pres_send_request(sub, tdata); } if (status != PJ_SUCCESS) { RING_WARN("Unable to create/send NOTIFY %d", status); pjsip_pres_terminate(sub, PJ_FALSE); pres->unlock(); return status; } pres->unlock(); return PJ_TRUE; }
namespace ring { using sip_utils::CONST_PJ_STR; /* Callback called when *server* subscription state has changed. */ void PresSubServer::pres_evsub_on_srv_state(pjsip_evsub *sub, pjsip_event *event) { pjsip_rx_data *rdata = event->body.rx_msg.rdata; if (!rdata) { RING_DBG("Presence_subscription_server estate has changed but no rdata."); return; } auto account = Manager::instance().getIP2IPAccount(); auto sipaccount = static_cast<SIPAccount *>(account.get()); if (!sipaccount) { RING_ERR("Could not find account IP2IP"); return; } auto pres = sipaccount->getPresence(); if (!pres) { RING_ERR("Presence not initialized"); return; } pres->lock(); PresSubServer *presSubServer = static_cast<PresSubServer *>(pjsip_evsub_get_mod_data(sub, pres->getModId())); if (presSubServer) { RING_DBG("Presence_subscription_server to %s is %s", presSubServer->remote_, pjsip_evsub_get_state_name(sub)); pjsip_evsub_state state; state = pjsip_evsub_get_state(sub); if (state == PJSIP_EVSUB_STATE_TERMINATED) { pjsip_evsub_set_mod_data(sub, pres->getModId(), NULL); pres->removePresSubServer(presSubServer); } /* TODO check if other cases should be handled*/ } pres->unlock(); } pj_bool_t PresSubServer::pres_on_rx_subscribe_request(pjsip_rx_data *rdata) { pjsip_method *method = &rdata->msg_info.msg->line.req.method; pj_str_t *str = &method->name; std::string request(str->ptr, str->slen); // pj_str_t contact; pj_status_t status; pjsip_dialog *dlg; pjsip_evsub *sub; pjsip_evsub_user pres_cb; pjsip_expires_hdr *expires_hdr; pjsip_status_code st_code; pj_str_t reason; pres_msg_data msg_data; pjsip_evsub_state ev_state; /* Only hande incoming subscribe messages should be processed here. * Otherwise we return FALSE to let other modules handle it */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0) return PJ_FALSE; /* debug msg */ std::string name(rdata->msg_info.to->name.ptr, rdata->msg_info.to->name.slen); std::string server(rdata->msg_info.from->name.ptr, rdata->msg_info.from->name.slen); RING_DBG("Incoming pres_on_rx_subscribe_request for %s, name:%s, server:%s." , request.c_str() , name.c_str() , server.c_str()); /* get parents*/ auto account = Manager::instance().getIP2IPAccount(); auto sipaccount = static_cast<SIPAccount *>(account.get()); if (!sipaccount) { RING_ERR("Could not find account IP2IP"); return PJ_FALSE; } pjsip_endpoint *endpt = getSIPVoIPLink()->getEndpoint(); SIPPresence * pres = sipaccount->getPresence(); pres->lock(); /* Create UAS dialog: */ const pj_str_t contact(sipaccount->getContactHeader()); status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); RING_WARN("Unable to create UAS dialog for subscription: %s [status=%d]", errmsg, status); pres->unlock(); pjsip_endpt_respond_stateless(endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } /* Init callback: */ pj_bzero(&pres_cb, sizeof(pres_cb)); pres_cb.on_evsub_state = &pres_evsub_on_srv_state; /* Create server presence subscription: */ status = pjsip_pres_create_uas(dlg, &pres_cb, rdata, &sub); if (status != PJ_SUCCESS) { int code = PJSIP_ERRNO_TO_SIP_STATUS(status); pjsip_tx_data *tdata; RING_WARN("Unable to create server subscription %d", status); if (code == 599 || code > 699 || code < 300) { code = 400; } status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); if (status == PJ_SUCCESS) { pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); } pres->unlock(); return PJ_FALSE; } /* Attach our data to the subscription: */ char* remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, remote, PJSIP_MAX_URL_SIZE); if (status < 1) pj_ansi_strcpy(remote, "<-- url is too long-->"); else remote[status] = '\0'; //pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, dlg->local.info->uri, contact.ptr, PJSIP_MAX_URL_SIZE); /* Create a new PresSubServer server and wait for client approve */ PresSubServer *presSubServer = new PresSubServer(pres, sub, remote, dlg); pjsip_evsub_set_mod_data(sub, pres->getModId(), presSubServer); // Notify the client. emitSignal<DRing::PresenceSignal::NewServerSubscriptionRequest>(presSubServer->remote_); pres->addPresSubServer(presSubServer); /* Capture the value of Expires header. */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) presSubServer->setExpires(expires_hdr->ivalue); else presSubServer->setExpires(-1); st_code = (pjsip_status_code) 200; reason = CONST_PJ_STR("OK"); 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); /* Create and send 2xx response to the SUBSCRIBE request: */ status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list); if (status != PJ_SUCCESS) { RING_WARN("Unable to accept presence subscription %d", status); pjsip_pres_terminate(sub, PJ_FALSE); pres->unlock(); return PJ_FALSE; } // Unsubscribe case ev_state = PJSIP_EVSUB_STATE_ACTIVE; if (presSubServer->getExpires() == 0) { // PJSIP_EVSUB_STATE_TERMINATED pres->unlock(); return PJ_TRUE; } /*Send notify immediatly. Replace real status with fake.*/ // pjsip_pres_set_status(sub, pres->getStatus()); // real status // fake temporary status pjrpid_element rpid = { PJRPID_ELEMENT_TYPE_PERSON, CONST_PJ_STR("20"), PJRPID_ACTIVITY_UNKNOWN, CONST_PJ_STR("") // empty note by default }; pjsip_pres_status fake_status_data; pj_bzero(&fake_status_data, sizeof(pjsip_pres_status)); fake_status_data.info_cnt = 1; fake_status_data.info[0].basic_open = false; fake_status_data.info[0].id = CONST_PJ_STR("0"); /* todo: tuplie_id*/ pj_memcpy(&fake_status_data.info[0].rpid, &rpid, sizeof(pjrpid_element)); pjsip_pres_set_status(sub, &fake_status_data); /* Create and send the the first NOTIFY to active subscription: */ pj_str_t stateStr = CONST_PJ_STR(""); pjsip_tx_data *tdata = NULL; status = pjsip_pres_notify(sub, ev_state, &stateStr, &reason, &tdata); if (status == PJ_SUCCESS) { pres->fillDoc(tdata, &msg_data); status = pjsip_pres_send_request(sub, tdata); } if (status != PJ_SUCCESS) { RING_WARN("Unable to create/send NOTIFY %d", status); pjsip_pres_terminate(sub, PJ_FALSE); pres->unlock(); return status; } pres->unlock(); return PJ_TRUE; } pjsip_module PresSubServer::mod_presence_server = { NULL, NULL, /* prev, next. */ CONST_PJ_STR("mod-presence-server"), /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ &pres_on_rx_subscribe_request, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; PresSubServer::PresSubServer(SIPPresence * pres, pjsip_evsub *evsub, const char *remote, pjsip_dialog *d) : remote_(remote) , pres_(pres) , sub_(evsub) , dlg_(d) , expires_(-1) , approved_(false) {} PresSubServer::~PresSubServer() { //TODO: check if evsub needs to be forced TERMINATED. } void PresSubServer::setExpires(int ms) { expires_ = ms; } int PresSubServer::getExpires() const { return expires_; } bool PresSubServer::matches(const char *s) const { // servers match if they have the same remote uri and the account ID. return (!(strcmp(remote_, s))) ; } void PresSubServer::approve(bool flag) { approved_ = flag; RING_DBG("Approve Presence_subscription_server for %s: %s.", remote_, flag ? "true" : "false"); // attach the real status data pjsip_pres_set_status(sub_, pres_->getStatus()); } void PresSubServer::notify() { /* Only send NOTIFY once subscription is active. Some subscriptions * may still be in NULL (when app is adding a new buddy while in the * on_incoming_subscribe() callback) or PENDING (when user approval is * being requested) state and we don't send NOTIFY to these subs until * the user accepted the request. */ if ((pjsip_evsub_get_state(sub_) == PJSIP_EVSUB_STATE_ACTIVE) && (approved_)) { RING_DBG("Notifying %s.", remote_); pjsip_tx_data *tdata; pjsip_pres_set_status(sub_, pres_->getStatus()); if (pjsip_pres_current_notify(sub_, &tdata) == PJ_SUCCESS) { // add msg header and send pres_->fillDoc(tdata, NULL); pjsip_pres_send_request(sub_, tdata); } else { RING_WARN("Unable to create/send NOTIFY"); pjsip_pres_terminate(sub_, PJ_FALSE); } } } } // namespace ring