Пример #1
0
void ICSCFSproutletTsx::on_tx_response(pjsip_msg* rsp)
{
  if (_acr != NULL)
  {
    // Pass the transmitted response to the ACR to update the accounting
    // information.
    _acr->tx_response(rsp);
  }

  pjsip_status_code rsp_status = (pjsip_status_code)rsp->line.status.code;

  // Check if this is a terminating INVITE.  If it is then check whether we need
  // to update our session establishment stats.  We consider the session to be
  // set up as soon as we receive a final response OR a 180 Ringing (per TS
  // 32.409).
  if (!_originating &&
      (_req_type == PJSIP_INVITE_METHOD) &&
      !_session_set_up &&
      ((rsp_status == PJSIP_SC_RINGING) ||
       !PJSIP_IS_STATUS_IN_CLASS(rsp_status, 100)))
  {
    // Session is now set up.
    _session_set_up = true;
    _icscf->_session_establishment_tbl->increment_attempts();
    _icscf->_session_establishment_network_tbl->increment_attempts();

    if ((rsp_status == PJSIP_SC_RINGING) ||
         PJSIP_IS_STATUS_IN_CLASS(rsp_status, 200))
    {
      // Session has been set up successfully.
      TRC_DEBUG("Session successful");
      _icscf->_session_establishment_tbl->increment_successes();
      _icscf->_session_establishment_network_tbl->increment_successes();
    }
    else if ((rsp_status == PJSIP_SC_BUSY_HERE) ||
             (rsp_status == PJSIP_SC_BUSY_EVERYWHERE) ||
             (rsp_status == PJSIP_SC_NOT_FOUND) ||
             (rsp_status == PJSIP_SC_ADDRESS_INCOMPLETE))
    {
      // Session failed, but should be counted as successful from a network
      // perspective.
      TRC_DEBUG("Session failed but network successful");
      _icscf->_session_establishment_tbl->increment_failures();
      _icscf->_session_establishment_network_tbl->increment_successes();
    }
    else
    {
      // Session establishment failed.
      TRC_DEBUG("Session failed");
      _icscf->_session_establishment_tbl->increment_failures();
      _icscf->_session_establishment_network_tbl->increment_failures();
    }
  }

}
Пример #2
0
/*!
 * \internal
 * \brief Adds a path header to an outgoing 2XX response
 *
 * \param endpoint The endpoint to which the INVITE response is to be sent
 * \param contact The contact to which the INVITE response is to be sent
 * \param tdata The outbound INVITE response
 */
static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
{
	struct pjsip_status_line status = tdata->msg->line.status;
	pj_str_t path_dup;
	pjsip_generic_string_hdr *path_hdr;
	pjsip_contact_hdr *contact_hdr;
	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
	const pj_str_t REGISTER_METHOD = {"REGISTER", 8};

	if (!endpoint
		|| !pj_stristr(&REGISTER_METHOD, &cseq->method.name)
		|| !PJSIP_IS_STATUS_IN_CLASS(status.code, 200)) {
		return;
	}

	contact_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
	if (!contact_hdr) {
		return;
	}

	aor = find_aor(endpoint, contact_hdr->uri);
	if (!aor || !aor->support_path || add_supported(tdata)
		|| path_get_string(tdata->pool, contact, &path_dup)) {
		return;
	}

	path_hdr = pjsip_generic_string_hdr_create(tdata->pool, &PATH_NAME, &path_dup);
	if (!path_hdr) {
		return;
	}

	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)path_hdr);
}
Пример #3
0
/*!
 * \internal
 * \brief Adds a diversion header to an outgoing 3XX response
 *
 * \param session The session on which the INVITE response is to be sent
 * \param tdata The outbound INVITE response
 */
static void diversion_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
	struct pjsip_status_line status = tdata->msg->line.status;

	/* add to 302 and 181 */
	if (PJSIP_IS_STATUS_IN_CLASS(status.code, 300) || (status.code == 181)) {
		get_redirecting_add_diversion(session, tdata);
	}
}
Пример #4
0
static void send_register_cb(void* token, pjsip_event *event)
{
    ThirdPartyRegData* tsxdata = (ThirdPartyRegData*)token;
    pjsip_transaction* tsx = event->body.tsx_state.tsx;

    if ((tsxdata->default_handling == SESSION_TERMINATED) &&
            ((tsx->status_code == 408) ||
             (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 500))))
    {
        std::string error_msg = "Third-party REGISTER transaction failed with code " + std::to_string(tsx->status_code);
        TRC_INFO(error_msg.c_str());

        SAS::Event event(tsxdata->trail, SASEvent::REGISTER_AS_FAILED, 0);
        event.add_var_param(error_msg);
        SAS::report_event(event);

        third_party_register_failed(tsxdata->public_id, tsxdata->trail);
    }

    // printf("Expiry: %d, Is_initial_registration: %d\n", tsxdata->expires, tsxdata->is_initial_registration);
    if (tsx->status_code == 200)
    {
        if (tsxdata->expires == 0)
        {
            third_party_reg_stats_tables->de_reg_tbl->increment_successes();
        }
        else if (tsxdata->is_initial_registration)
        {
            third_party_reg_stats_tables->init_reg_tbl->increment_successes();
        }
        else
        {
            third_party_reg_stats_tables->re_reg_tbl->increment_successes();
        }
    }
    else
        // Count all failed registration attempts, not just ones that result in user
        // being unsubscribed.
    {
        if (tsxdata->expires == 0)
        {
            third_party_reg_stats_tables->de_reg_tbl->increment_failures();
        }
        else if (tsxdata->is_initial_registration)
        {
            third_party_reg_stats_tables->init_reg_tbl->increment_failures();
        }
        else
        {
            third_party_reg_stats_tables->re_reg_tbl->increment_failures();
        }
    }

    delete tsxdata;
    tsxdata = NULL;
}
Пример #5
0
static void send_register_cb(void* token, pjsip_event *event)
{
  ThirdPartyRegData* tsxdata = (ThirdPartyRegData*)token;
  pjsip_transaction* tsx = event->body.tsx_state.tsx;

  if ((tsxdata->default_handling == SESSION_TERMINATED) &&
      ((tsx->status_code == 408) ||
       (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 500))))
  {
    std::string error_msg = "Third-party REGISTER transaction failed with code " + std::to_string(tsx->status_code);
    LOG_INFO(error_msg.c_str());

    SAS::Event event(tsxdata->trail, SASEvent::REGISTER_AS_FAILED, 0);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    third_party_register_failed(tsxdata->public_id, tsxdata->trail);
  }
  delete tsxdata;
}
Пример #6
0
void SessionExpiresHelper::process_response(pjsip_msg* rsp,
                                            pj_pool_t* pool,
                                            SAS::TrailId trail)
{
  // Session expires is only allowed on INVITE and UPDATE methods.
  pjsip_method* method = &PJSIP_MSG_CSEQ_HDR(rsp)->method;

  if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) &&
      (pjsip_method_cmp(method, &METHOD_UPDATE) != 0))
  {
    return;
  }

  // We only need to process successful final responses.
  if (!PJSIP_IS_STATUS_IN_CLASS(rsp->line.status.code, 200))
  {
    return;
  }

  pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*)
    pjsip_msg_find_hdr_by_name(rsp, &STR_SESSION_EXPIRES, NULL);

  if (se_hdr == NULL)
  {
    // There is no session-expires header. This means we are most downstream
    // device that supports session timers, and in particular the UAS does not
    // support them.
    //
    // If the UAC does not support session timers, there's nothing more we can
    // do - session timers will not be used for this dialog.
    //
    // If the UAC *does* support session timers, re-add a session-expires header
    // that instructs the UAC to be the refresher.
    if (_uac_supports_timer)
    {
      se_hdr = pjsip_session_expires_hdr_create(pool);
      pjsip_msg_add_hdr(rsp, (pjsip_hdr*)se_hdr);
      se_hdr->expires = _se_on_req;
      se_hdr->refresher = SESSION_REFRESHER_UAC;

      // Also update (or add) the require header to force the UAC to do session
      // refreshes.
      pjsip_require_hdr* require_hdr = (pjsip_require_hdr*)
        pjsip_msg_find_hdr(rsp, PJSIP_H_REQUIRE, NULL);

      if (require_hdr == NULL)
      {
        require_hdr = (pjsip_require_hdr*)pjsip_require_hdr_create(pool);
        pjsip_msg_add_hdr(rsp, (pjsip_hdr*)require_hdr);
      }

      pj_strdup(pool, &require_hdr->values[require_hdr->count], &STR_TIMER);
      require_hdr->count++;
    }
  }

  if (_initial_request)
  {
    if (se_hdr == NULL)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_NO_UA_SUPPORT, 0);
      SAS::report_event(event);
    }
    else if (se_hdr->expires > _target_se)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_INTERVAL_TOO_LONG, 0);
      event.add_static_param(_target_se);
      event.add_static_param(se_hdr->expires);
      SAS::report_event(event);
    }
  }
}
Пример #7
0
void ICSCFSproutletRegTsx::on_rx_response(pjsip_msg* rsp, int fork_id)
{
  if (_acr != NULL)
  {
    // Pass the received response to the ACR.
    // @TODO - timestamp from response???
    _acr->rx_response(rsp);
  }

  // Check if this response is one that we are allowed to retry the HSS lookup
  // for.  See TS 24.229 - section 5.3.1.3.
  //
  // Note we support service restoration, so integrity-protected settings in
  // Authorization header are immaterial).
  pjsip_status_code rsp_status = (pjsip_status_code)rsp->line.status.code;
  const ForkState& fork_status = fork_state(fork_id);
  TRC_DEBUG("Check retry conditions for REGISTER, status = %d, S-CSCF %sresponsive",
            rsp_status,
            (fork_status.error_state != NONE) ? "not " : "");
  if ((PJSIP_IS_STATUS_IN_CLASS(rsp_status, 300)) ||
      (fork_status.error_state != NONE) ||
      (rsp_status == PJSIP_SC_TEMPORARILY_UNAVAILABLE))
  {
    // Indeed it is, first log to SAS.
    TRC_DEBUG("Attempt retry to alternate S-CSCF for REGISTER request");
    std::string st_code = std::to_string(rsp_status);
    SAS::Event event(trail(), SASEvent::SCSCF_RETRY, 0);
    std::string method = "REGISTER";
    event.add_var_param(method);
    event.add_var_param(st_code);
    SAS::report_event(event);

    // Now we can simply reuse the UA router we made on the initial request.
    pjsip_sip_uri* scscf_sip_uri = NULL;
    pjsip_msg* req = original_request();
    std::string wildcard;
    int status_code = _router->get_scscf(get_pool(req), scscf_sip_uri, wildcard);

    if (status_code == PJSIP_SC_OK)
    {
      TRC_DEBUG("Found SCSCF for REGISTER");

      req->line.req.uri = (pjsip_uri*)scscf_sip_uri;
      send_request(req);

      // We're not forwarding this response upstream.
      free_msg(rsp);
    }
    else
    {
      // In the register case the spec's are quite particular about how
      // failures are reported.
      if (status_code == PJSIP_SC_FORBIDDEN)
      {
        // The HSS has returned a negative response to the user registration
        // request - I-CSCF should respond with 403.
        rsp->line.status.code = PJSIP_SC_FORBIDDEN;
        rsp->line.status.reason =
          *pjsip_get_status_text(rsp->line.status.code);
      }
      else
      {
        // The I-CSCF can't select an S-CSCF for the REGISTER request (either
        // because there are no more S-CSCFs that meet the mandatory
        // capabilitires, or the HSS is temporarily unavailable). There was at
        // least one valid S-CSCF (as this is retry processing). The I-CSCF
        // must return 504 (TS 24.229, 5.3.1.3) in this case.
        rsp->line.status.code = PJSIP_SC_SERVER_TIMEOUT;
        rsp->line.status.reason =
          *pjsip_get_status_text(rsp->line.status.code);
      }

      // We're done, no more retries.
      free_msg(req);
      send_response(rsp);
    }
  }
  else
  {
    // Provisional, successful or non-retryable response, simply forward on
    // upstream.  If this is a final response, there will be no more retries.
    send_response(rsp);
  }
}
Пример #8
0
static void on_tsx_state(pjsip_transaction* tsx, pjsip_event* event)
{
  StatefulSendState* sss;
  bool retrying = false;

  if ((mod_sprout_util.id < 0) ||
      (event->type != PJSIP_EVENT_TSX_STATE))
  {
    return;
  }

  sss = (StatefulSendState*)tsx->mod_data[mod_sprout_util.id];

  if (sss == NULL)
  {
    return;
  }

  if (!sss->servers.empty())
  {
    // The target for the request came from the resolver, so check to see
    // if the request failed.
    if ((tsx->state == PJSIP_TSX_STATE_COMPLETED) ||
        (tsx->state == PJSIP_TSX_STATE_TERMINATED))
    {
      // Transaction has completed or terminated.  We need to look at both
      // states as
      // -  timeouts and transport errors cause an immediate transition
      //    to terminated state, bypassing completed state
      // -  a 5xx response causes a transition to completed state, with a
      //    possible delay until the transition to terminated state (5 seconds
      //    for UDP transport), which would needlessly delay any retry.
      if ((event->body.tsx_state.type == PJSIP_EVENT_TIMER) ||
          (event->body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR) ||
          (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 500)))
      {
        // Either transaction failed on a timeout, transport error or received
        // 5xx error, so blacklist the failed target.
        LOG_DEBUG("Transaction failed with retriable error");
        if ((event->body.tsx_state.type == PJSIP_EVENT_TIMER) ||
            (event->body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR))
        {
          // Either the connection failed, or the server didn't respond within
          // the timeout, so blacklist it.  We don't blacklist servers that
          // return 5xx errors as this may indicate a transient overload.
          PJUtils::blacklist_server(sss->servers[sss->current_server]);
        }

        // Can we do a retry?
        ++sss->current_server;
        if (sss->current_server < (int)sss->servers.size())
        {
          // More servers to try, so allocate a new branch ID and transaction.
          LOG_DEBUG("Attempt to resend request to next destination server");
          pjsip_tx_data* tdata = sss->tdata;
          pjsip_transaction* retry_tsx;
          PJUtils::generate_new_branch_id(tdata);
          pj_status_t status = pjsip_tsx_create_uac(&mod_sprout_util,
                                                    tdata,
                                                    &retry_tsx);

          if (status == PJ_SUCCESS)
          {
            // The new transaction has been set up.

            // Set the trail ID in the transaction from the message.
            set_trail(retry_tsx, get_trail(tdata));

            // Set up the module data for the new transaction to reference
            // the state information.
            retry_tsx->mod_data[mod_sprout_util.id] = sss;

            // Increment the reference count of the request as we are passing
            // it to a new transaction.
            pjsip_tx_data_add_ref(tdata);

            // Copy across the destination information for a retry and try to
            // resend the request.
            PJUtils::set_dest_info(tdata, sss->servers[sss->current_server]);
            status = pjsip_tsx_send_msg(retry_tsx, tdata);

            if (status == PJ_SUCCESS)
            {
              // Successfully sent a retry.  Make sure this callback isn't
              // invoked again for the previous transaction.
              tsx->mod_data[mod_sprout_util.id] = NULL;
              retrying = true;
            }
          }
        }
      }
    }
  }

  if ((!retrying) &&
      (tsx->status_code >= 200))
  {
    // Call the user callback, if any, and prevent the callback to be called again
    // by clearing the transaction's module_data.
    LOG_DEBUG("Request transaction completed, status code = %d", tsx->status_code);
    tsx->mod_data[mod_sprout_util.id] = NULL;

    if (sss->user_cb != NULL)
    {
      (*sss->user_cb)(sss->user_token, event);
    }

    // The transaction has completed, so decrement our reference to the tx_data
    // and free the state data.
    pjsip_tx_data_dec_ref(sss->tdata);
    delete sss;
  }
}
Пример #9
0
/* This function is called when we receive SUBSCRIBE request message 
 * to refresh existing subscription.
 */
static void on_received_sub_refresh( pjsip_event_sub *sub, 
				     pjsip_transaction *tsx, pjsip_rx_data *rdata)
{
    pjsip_event_hdr *e;
    pjsip_expires_hdr *expires;
    pj_str_t hname;
    int status = 200;
    pj_str_t reason_phrase = { NULL, 0 };
    int new_state = sub->state;
    int old_state = sub->state;
    int new_interval = 0;
    pjsip_tx_data *tdata;

    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", 
			 sub, state[sub->state].ptr));

    /* Check that the event matches. */
    hname = pj_str("Event");
    e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
    if (!e) {
	status = 400;
	reason_phrase = pj_str("Missing Event header");
	goto send_response;
    }
    if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
	pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
    {
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");
	goto send_response;
    }

    /* Check server state. */
    if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");
	goto send_response;
    }

    /* Check expires header. */
    expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
    if (!expires) {
	/*
	status = 400;
	reason_phrase = pj_str("Missing Expires header");
	goto send_response;
	*/
	new_interval = sub->default_interval;
    } else {
	/* Check that interval is not too short. 
	 * Note that expires time may be zero (for unsubscription).
	 */
	new_interval = expires->ivalue;
	if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
	    status = PJSIP_SC_INTERVAL_TOO_BRIEF;
	    goto send_response;
	}
    }

    /* Update interval. */
    sub->default_interval = new_interval;
    pj_gettimeofday(&sub->expiry_time);
    sub->expiry_time.sec += new_interval;

    /* Update timer only if this is not unsubscription. */
    if (new_interval > 0) {
	sub->default_interval = new_interval;
	sub_schedule_uas_expire( sub, new_interval );

	/* Call callback. */
	if (sub->cb.on_received_refresh) {
	    sub->pending_tsx++;
	    (*sub->cb.on_received_refresh)(sub, rdata);
	    sub->pending_tsx--;
	}
    }

send_response:
    tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
    if (tdata) {
	if (reason_phrase.slen)
	    tdata->msg->line.status.reason = reason_phrase;

	/* Add Expires header. */
	expires = pjsip_expires_hdr_create(tdata->pool);
	expires->ivalue = sub->default_interval;
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);

	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    pjsip_msg_add_hdr(tdata->msg, 
			      pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
	}
	/* Send down to transaction. */
	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	/* Notify application if sub is terminated. */
	new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	sub_set_state(sub, new_state);
	if (new_state!=old_state && sub->cb.on_sub_terminated) {
	    pj_str_t reason = {"", 0};
	    if (reason_phrase.slen) reason = reason_phrase;
	    else reason = *pjsip_get_status_text(status);

	    sub->pending_tsx++;
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	    sub->pending_tsx--;
	}
    }

    pj_mutex_unlock(sub->mutex);

    /* Prefer to call log when we're not holding the mutex. */
    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", 
			 sub, state[sub->state].ptr,
			 (tdata ? tdata->obj_name : "null"), status));

    /* Check if application has requested deletion. */
    if (sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    }

}
Пример #10
0
/*
 * This callback called when we receive incoming NOTIFY request.
 */
static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
{
    pjsip_event_sub *sub;
    pjsip_tx_data *tdata;
    int status = 200;
    int old_state;
    pj_str_t reason = { NULL, 0 };
    pj_str_t reason_phrase = { NULL, 0 };
    int new_state = PJSIP_EVENT_SUB_STATE_NULL;

    /* Find subscription based on Call-ID and From tag. 
     * This will also automatically lock the subscription, if it's found.
     */
    sub = find_sub(rdata);
    if (!sub) {
	/* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
	 * Answer with 481 Subscription does not exist.
	 */
	PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");

    } else {
	pj_assert(sub->role == PJSIP_ROLE_UAC);
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", 
			     sub, state[sub->state].ptr));

    }

    new_state = old_state = sub->state;

    /* RFC 3265: Section 3.2.1
     * Check that the Event header match the subscription. 
     */
    if (status == 200) {
	pjsip_event_hdr *hdr;
	pj_str_t hname = { "Event", 5 };

	hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
	if (!hdr) {
	    status = PJSIP_SC_BAD_REQUEST;
	    reason_phrase = pj_str("No Event header found");
	} else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
		   pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) 
	{
	    status = 481;
	    reason_phrase = pj_str("Subscription does not exist");
	}
    }

    /* Update subscription state and timer. */
    if (status == 200) {
	pjsip_sub_state_hdr *hdr;
	const pj_str_t hname = { "Subscription-State", 18 };
	const pj_str_t state_active = { "active", 6 },
		       state_pending = { "pending", 7},
		       state_terminated = { "terminated", 10 };

	hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
	if (!hdr) {
	    status = PJSIP_SC_BAD_REQUEST;
	    reason_phrase = pj_str("No Subscription-State header found");
	    goto process;
	} 

	/*
	 * Update subscription state.
	 */
	if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
		new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
	} else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
		new_state = PJSIP_EVENT_SUB_STATE_PENDING;
	} else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	} else {
	    new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
	}

	reason = hdr->reason_param;

	if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
	    sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 
	{
	    sub_set_state(sub, new_state);
	    if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
		pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
	    } else {
		sub->state_str = state[new_state];
	    }
	}

	/*
	 * Update timeout timer in required, just in case notifier changed the 
         * expiration to shorter time.
	 * Section 3.2.2: the expires param can only shorten the interval.
	 */
	if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || 
	     sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) 
	{
	    pj_time_val now, new_expiry;

	    pj_gettimeofday(&now);
	    new_expiry.sec = now.sec + hdr->expires_param;
	    if (sub->timer.id==0 || 
		new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 
	    {
		update_next_refresh(sub, hdr->expires_param);
	    }
	}
    }

process:
    /* Note: here we sub MAY BE NULL! */

    /* Send response to NOTIFY */
    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
    if (tdata) {
	if (reason_phrase.slen)
	    tdata->msg->line.status.reason = reason_phrase;

	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    pjsip_hdr *hdr;
	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
	    pjsip_msg_add_hdr( tdata->msg, hdr);
	}

	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    /* Call NOTIFY callback, if any. */
    if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
	sub->pending_tsx++;
	(*sub->cb.on_received_notify)(sub, rdata);
	sub->pending_tsx--;
    }

    /* Check if subscription is terminated and call callback. */
    if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
	if (sub->cb.on_sub_terminated) {
	    sub->pending_tsx++;
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	    sub->pending_tsx--;
	}
    }

    /* Check if application has requested deletion. */
    if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    } else if (sub) {
	pj_mutex_unlock(sub->mutex);
    }
}
Пример #11
0
/* This callback is called when response to SUBSCRIBE is received. */
static void on_subscribe_response(void *token, pjsip_event *event)
{
    pjsip_event_sub *sub = token;
    pjsip_transaction *tsx = event->obj.tsx;
    int new_state, old_state = sub->state;

    pj_assert(tsx->status_code >= 200);
    if (tsx->status_code < 200)
	return;

    pj_assert(sub->role == PJSIP_ROLE_UAC);

    /* Lock mutex. */
    pj_mutex_lock(sub->mutex);

    /* If request failed with 401/407 error, silently retry the request. */
    if (tsx->status_code==401 || tsx->status_code==407) {
	pjsip_tx_data *tdata;
	tdata = pjsip_auth_reinit_req(sub->endpt,
				      sub->pool, &sub->auth_sess,
				      sub->cred_cnt, sub->cred_info,
				      tsx->last_tx, event->src.rdata );
	if (tdata) {
	    int status;
	    pjsip_cseq_hdr *cseq;
	    cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
	    cseq->cseq = sub->cseq++;
	    status = pjsip_endpt_send_request( sub->endpt, tdata, 
					       -1, sub, 
					       &on_subscribe_response);
	    if (status == 0) {
		pj_mutex_unlock(sub->mutex);
		return;
	    }
	}
    }

    if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
	/* Update To tag. */
	if (sub->to->tag.slen == 0)
	    pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);

	new_state = sub->state;

    } else if (tsx->status_code == 481) {
	new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;

    } else if (tsx->status_code >= 300) {
	/* RFC 3265 Section 3.1.4.2:
         * If a SUBSCRIBE request to refresh a subscription fails 
	 * with a non-481 response, the original subscription is still 
	 * considered valid for the duration of original exires.
	 *
	 * Note:
	 * Since we normally send SUBSCRIBE for refreshing the subscription,
	 * it means the subscription already expired anyway. So we terminate
	 * the subscription now.
	 */
	if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	} else {
	    /* Use this to be compliant with Section 3.1.4.2
	      new_state = sub->state;
	     */
	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	}
    } else {
	pj_assert(0);
	new_state = sub->state;
    }

    if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
	sub_set_state(sub, new_state);
    }

    if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
	sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
    {
	/*
	 * Register timer for next subscription refresh, but only when
	 * we're not unsubscribing. Also update default_interval and Expires
	 * header.
	 */
	if (sub->default_interval > 0 && !sub->delete_flag) {
	    pjsip_expires_hdr *exp = NULL;
	    
	    /* Could be transaction timeout. */
	    if (event->src_type == PJSIP_EVENT_RX_MSG) {
		exp = pjsip_msg_find_hdr(event->src.rdata->msg,
					 PJSIP_H_EXPIRES, NULL);
	    }

	    if (exp) {
		int delay = exp->ivalue;
		if (delay > 0) {
		    pj_time_val new_expiry;
		    pj_gettimeofday(&new_expiry);
		    new_expiry.sec += delay;
		    if (sub->timer.id==0 || 
			new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 
		    {
		    //if (delay > 0 && delay < sub->default_interval) {
			sub->default_interval = delay;
			sub->uac_expires->ivalue = delay;
			update_next_refresh(sub, delay);
		    }
		}
	    }
	}
    }

    /* Call callback. */
    if (!sub->delete_flag) {
	if (sub->cb.on_received_sub_response) {
	    (*sub->cb.on_received_sub_response)(sub, event);
	}
    }

    /* Notify application if we're terminated. */
    if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
	if (sub->cb.on_sub_terminated) {
	    pj_str_t reason;
	    if (event->src_type == PJSIP_EVENT_RX_MSG)
		reason = event->src.rdata->msg->line.status.reason;
	    else
		reason = *pjsip_get_status_text(tsx->status_code);

	    (*sub->cb.on_sub_terminated)(sub, &reason);
	}
    }

    /* Decrement pending tsx count. */
    --sub->pending_tsx;
    pj_assert(sub->pending_tsx >= 0);

    if (sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    } else {
	pj_mutex_unlock(sub->mutex);
    }

    /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
}
Пример #12
0
/* This function is called when we receive SUBSCRIBE request message for 
 * a new subscription.
 */
static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
{
    package *pkg;
    pj_pool_t *pool;
    pjsip_event_sub *sub = NULL;
    pj_str_t hname;
    int status = 200;
    pj_str_t reason = { NULL, 0 };
    pjsip_tx_data *tdata;
    pjsip_expires_hdr *expires;
    pjsip_accept_hdr *accept;
    pjsip_event_hdr *evhdr;

    /* Get the Event header. */
    hname = pj_str("Event");
    evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
    if (!evhdr) {
	status = 400;
	reason = pj_str("No Event header in request");
	goto send_response;
    }

    /* Find corresponding package. 
     * We don't lock the manager's mutex since we assume the package list
     * won't change once the application is running!
     */
    pkg = mgr.pkg_list.next;
    while (pkg != &mgr.pkg_list) {
	if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
	    break;
	pkg = pkg->next;
    }

    if (pkg == &mgr.pkg_list) {
	/* Event type is not supported by any packages! */
	status = 489;
	reason = pj_str("Bad Event");
	goto send_response;
    }

    /* First check that the Accept specification matches the 
     * package's Accept types.
     */
    accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
    if (accept) {
	unsigned i;
	pj_str_t *content_type = NULL;

	for (i=0; i<accept->count && !content_type; ++i) {
	    int j;
	    for (j=0; j<pkg->accept_cnt; ++j) {
		if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
		    content_type = &pkg->accept[j];
		    break;
		}
	    }
	}

	if (!content_type) {
	    status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
	    goto send_response;
	}
    }

    /* Check whether the package wants to accept the subscription. */
    pj_assert(pkg->cb.on_query_subscribe != NULL);
    (*pkg->cb.on_query_subscribe)(rdata, &status);
    if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
	goto send_response;

    /* Create new subscription record. */
    pool = pjsip_endpt_create_pool(tsx->endpt, "esub", 
				   SUB_POOL_SIZE, SUB_POOL_INC);
    if (!pool) {
	status = 500;
	goto send_response;
    }
    sub = pj_pool_calloc(pool, 1, sizeof(*sub));
    sub->pool = pool;
    sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
    if (!sub->mutex) {
	status = 500;
	goto send_response;
    }

    PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));

    /* Start locking mutex. */
    pj_mutex_lock(sub->mutex);

    /* Init UAS subscription */
    sub->endpt = tsx->endpt;
    sub->role = PJSIP_ROLE_UAS;
    sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
    sub->state_str = state[sub->state];
    pj_list_init(&sub->auth_sess);
    pj_list_init(&sub->route_set);
    sub->from = pjsip_hdr_clone(pool, rdata->to);
    pjsip_fromto_set_from(sub->from);
    if (sub->from->tag.slen == 0) {
	pj_create_unique_string(pool, &sub->from->tag);
	rdata->to->tag = sub->from->tag;
    }
    sub->to = pjsip_hdr_clone(pool, rdata->from);
    pjsip_fromto_set_to(sub->to);
    sub->contact = pjsip_contact_hdr_create(pool);
    sub->contact->uri = sub->from->uri;
    sub->call_id = pjsip_cid_hdr_create(pool);
    pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
    sub->cseq = pj_rand() % 0xFFFF;
    
    expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
    if (expires) {
	sub->default_interval = expires->ivalue;
	if (sub->default_interval > 0 && 
	    sub->default_interval < SECONDS_BEFORE_EXPIRY) 
	{
	    status = 423; /* Interval too short. */
	    goto send_response;
	}
    } else {
	sub->default_interval = 600;
    }

    /* Clone Event header. */
    sub->event = pjsip_hdr_clone(pool, evhdr);

    /* Register to hash table. */
    create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
			  &sub->from->tag);
    pj_mutex_lock(mgr.mutex);
    pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
    pj_mutex_unlock(mgr.mutex);

    /* Set timer where subscription will expire only when expires<>0. 
     * Subscriber may send new subscription with expires==0.
     */
    if (sub->default_interval != 0) {
	sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
    }

    /* Notify application. */
    if (pkg->cb.on_subscribe) {
	pjsip_event_sub_cb *cb = NULL;
	sub->pending_tsx++;
	(*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
	sub->pending_tsx--;
	if (cb == NULL)
	    pj_memset(&sub->cb, 0, sizeof(*cb));
	else
	    pj_memcpy(&sub->cb, cb, sizeof(*cb));
    }


send_response:
    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", 
			  sub, state[sub->state].ptr, status));

    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
    if (tdata) {
	if (reason.slen) {
	    /* Customize reason text. */
	    tdata->msg->line.status.reason = reason;
	}
	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    /* Add Expires header. */
	    pjsip_expires_hdr *hdr;

	    hdr = pjsip_expires_hdr_create(tdata->pool);
	    hdr->ivalue = sub->default_interval;
	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
	}
	if (status == 423) {
	    /* Add Min-Expires header. */
	    pjsip_min_expires_hdr *hdr;

	    hdr = pjsip_min_expires_hdr_create(tdata->pool);
	    hdr->ivalue = SECONDS_BEFORE_EXPIRY;
	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
	}
	if (status == 489 || 
	    status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
	    PJSIP_IS_STATUS_IN_CLASS(status,200)) 
	{
	    /* Add Allow-Events header. */
	    pjsip_hdr *hdr;
	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
	    pjsip_msg_add_hdr(tdata->msg, hdr);

	    /* Should add Accept header?. */
	}

	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    /* If received new subscription with expires=0, terminate. */
    if (sub && sub->default_interval == 0) {
	pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
	if (sub->cb.on_sub_terminated) {
	    pj_str_t reason = { "timeout", 7 };
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	}
    }

    if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
	if (sub && sub->mutex) {
	    pjsip_event_sub_destroy(sub);
	} else if (sub) {
	    pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
	}
    } else {
	pj_assert(status >= 200);
	pj_mutex_unlock(sub->mutex);
    }
}