コード例 #1
0
ファイル: sippresence.cpp プロジェクト: alexzah/ring-daemon
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;
    }
}
コード例 #2
0
ファイル: sippresence.cpp プロジェクト: alexzah/ring-daemon
void SIPPresence::updateStatus(bool status, const std::string &note)
{
    //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 */
}
コード例 #3
0
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");
        }
    }
}
コード例 #4
0
ファイル: sippresence.cpp プロジェクト: alexzah/ring-daemon
/* 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;
}
コード例 #5
0
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;
}
コード例 #6
0
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