static int nua_publish_client_response(nua_client_request_t *cr,
				       int status, char const *phrase,
				       sip_t const *sip)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;

  if (!cr->cr_terminated && du && sip) {
    struct publish_usage *pu = nua_dialog_usage_private(du);
    sip_expires_t const *ex = sip->sip_expires;

    /* Reset state */
    pu->pu_published = 0;
    if (pu->pu_etag)
      su_free(nh->nh_home, pu->pu_etag), pu->pu_etag = NULL;

    if (status < 300) {
      pu->pu_published = 1;
      pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_etag);

      if (!ex || ex->ex_delta == 0 || !pu->pu_etag) {
	cr->cr_terminated = 1;

	if (!ex || ex->ex_delta == 0)
	  SET_STATUS(900, "Received Invalid Expiration Time");
	else
	  SET_STATUS1(NUA_ERROR_AT(__FILE__, __LINE__));
      }
      else
	nua_dialog_usage_set_refresh(du, ex->ex_delta);
    }
  }

  return nua_base_client_response(cr, status, phrase, sip, NULL);
}
static void nua_notify_usage_refresh(nua_handle_t *nh,
				     nua_dialog_state_t *ds,
				     nua_dialog_usage_t *du,
				     sip_time_t now)
{
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  nua_client_request_t *cr = du->du_cr;
  nua_event_t e = nua_r_notify;

  if (cr) {
    int terminating = 0;

    if (nu->nu_expires && nu->nu_expires <= now)
      terminating = 1;
    else if (nu->nu_requested && nu->nu_requested <= now)
      terminating = 1;

    if (nua_client_resend_request(cr, terminating) >= 0)
      return;
  }
  else {
    if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0)
      return;
  }

  nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__),
		   NUTAG_SUBSTATE(nua_substate_terminated),
		   TAG_END());

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
}
Exemple #3
0
/** Send a request message.
 *
 * If an error occurs, send error event to the application.
 *
 * @retval 0 if request is pending
 * @retval >=1 if error event has been sent
 */
static
int nua_client_request_try(nua_client_request_t *cr)
{
  int error = nua_client_request_sendmsg(cr);

  if (error < 0)
    error = nua_client_response(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);

  return error;
}
/** Refresh subscription */
static void nua_subscribe_usage_refresh(nua_handle_t *nh,
					nua_dialog_state_t *ds,
					nua_dialog_usage_t *du,
					sip_time_t now)
{
  nua_client_request_t *cr = du->du_cr;
  struct event_usage *eu = nua_dialog_usage_private(du);

  assert(eu);

  if (eu->eu_final_wait) {
    /* Did not receive NOTIFY for fetch */
    sip_event_t const *o = du->du_event;
    char const *id = o ? o->o_id : NULL;

    SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n",
		(void *)nh, o ? o->o_type : "(empty)",
		id ? "; id=" : "", id ? id : ""));

    nua_stack_tevent(nh->nh_nua, nh,  NULL,
		     nua_i_notify, 408, "Fetch Timeouts without NOTIFY",
		     NUTAG_SUBSTATE(nua_substate_terminated),
		     SIPTAG_EVENT(du->du_event),
		     TAG_END());
    nua_dialog_usage_remove(nh, ds, du, NULL, NULL);

    return;
  }

  if (cr) {
    if (nua_client_resend_request(cr, 0) >= 0)
      return;
  }
  else if (eu->eu_refer) {
    /*
     * XXX - If we have received a NOTIFY, we should try to terminate
     * subscription
     */
  }

  if (!eu->eu_unsolicited)
    nua_stack_tevent(nh->nh_nua, nh, NULL,
		     nua_i_notify, NUA_ERROR_AT(__FILE__, __LINE__),
		     NUTAG_SUBSTATE(nua_substate_terminated),
		     SIPTAG_EVENT(du->du_event),
		     TAG_END());

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
}
static void nua_publish_usage_refresh(nua_handle_t *nh,
				     nua_dialog_state_t *ds,
				     nua_dialog_usage_t *du,
				     sip_time_t now)
{
  nua_client_request_t *cr = du->du_cr;

  if (cr) {
    if (nua_client_resend_request(cr, 0) >= 0)
      return;
  }

  nua_stack_event(nh->nh_nua, nh, NULL,
		  nua_r_publish, NUA_ERROR_AT(__FILE__, __LINE__),
		  NULL);

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
}
void nua_stack_authorize(nua_t *nua,
			 nua_handle_t *nh,
			 nua_event_t e,
			 tagi_t const *tags)
{
  nea_sub_t *sub = NULL;
  int state = nea_extended;

  tl_gets(tags,
	  NEATAG_SUB_REF(sub),
	  NUTAG_SUBSTATE_REF(state),
	  TAG_END());

  if (sub && state > 0) {
    nea_sub_auth(sub, (nea_state_t)state, TAG_NEXT(tags));
    nua_stack_event(nua, nh, NULL, e, SIP_200_OK, NULL);
  }
  else {
    nua_stack_event(nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NULL);
  }
}
static void nua_notify_usage_refresh(nua_handle_t *nh,
				     nua_dialog_state_t *ds,
				     nua_dialog_usage_t *du)
{
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  nua_client_request_t *cr = du->du_cr;
  nua_event_t e = nua_r_notify;

  if (cr) {
    int terminating = 0;
    sip_time_t now = sip_now();

    SU_DEBUG_7(("%s(%p, %p, %p): using existing cr=%p\n",
		"nua_notify_usage_refresh",
		(void *)nh, (void *)ds, (void *)du, (void *)cr));

    if (nu->nu_expires && nu->nu_expires <= now)
      terminating = 1;
    else if (nu->nu_requested && nu->nu_requested <= now)
      terminating = 1;

    if (nua_client_resend_request(cr, terminating) >= 0)
      return;
  }
  else {
    SU_DEBUG_7(("%s(%p, %p, %p): new NOTIFY cr for %s\n",
		"nua_notify_usage_refresh",
		(void *)nh, (void *)ds, (void *)du,
		du->du_event ? du->du_event->o_type : "<implicit>"));

    if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0)
      return;
  }

  nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__),
		   NUTAG_SUBSTATE(nua_substate_terminated),
		   TAG_END());

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
}
/* ----------------------------------------------------------------------
 * Receiving events from client
 */
static
void nua_stack_signal(nua_t *nua, su_msg_r msg, nua_ee_data_t *ee)
{
  nua_event_data_t *e = ee->ee_data;
  nua_handle_t *nh = e->e_nh;
  tagi_t *tags = e->e_tags;
  nua_event_t event;
  int error = 0;

  if (nh) {
    if (!nh->nh_prev)
      nh_append(nua, nh);
    if (!nh->nh_ref_by_stack) {
      /* Mark handle as used by stack */
      nh->nh_ref_by_stack = 1;
      nua_handle_ref(nh);
    }
  }

  if (nua_log->log_level >= 5) {
    char const *name = nua_event_name((enum nua_event_e)e->e_event);

    if (e->e_status == 0)
      SU_DEBUG_5(("nua(%p): %s signal %s\n", (void *)nh, "recv", name + 4));
    else
      SU_DEBUG_5(("nua(%p): recv signal %s %u %s\n",
		  (void *)nh, name + 4,
		  e->e_status, e->e_phrase ? e->e_phrase : ""));
  }

  su_msg_save(nua->nua_signal, msg);

  event = (enum nua_event_e)e->e_event;

  if (nua->nua_shutdown && !e->e_always) {
    /* Shutting down */
    nua_stack_event(nua, nh, NULL, event,
		    901, "Stack is going down",
		    NULL);
  }
  else switch (event) {
  case nua_r_get_params:
    nua_stack_get_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
    break;
  case nua_r_set_params:
    nua_stack_set_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
    break;
  case nua_r_shutdown:
    nua_stack_shutdown(nua);
    break;
  case nua_r_register:
  case nua_r_unregister:
    nua_stack_register(nua, nh, event, tags);
    break;
  case nua_r_invite:
    error = nua_stack_invite(nua, nh, event, tags);
    break;
  case nua_r_cancel:
    error = nua_stack_cancel(nua, nh, event, tags);
    break;
  case nua_r_bye:
    error = nua_stack_bye(nua, nh, event, tags);
    break;
  case nua_r_options:
    error = nua_stack_options(nua, nh, event, tags);
    break;
  case nua_r_refer:
    error = nua_stack_refer(nua, nh, event, tags);
    break;
  case nua_r_publish:
  case nua_r_unpublish:
    error = nua_stack_publish(nua, nh, event, tags);
    break;
  case nua_r_info:
    error = nua_stack_info(nua, nh, event, tags);
    break;
  case nua_r_prack:
    error = nua_stack_prack(nua, nh, event, tags);
    break;
  case nua_r_update:
    error = nua_stack_update(nua, nh, event, tags);
    break;
  case nua_r_message:
    error = nua_stack_message(nua, nh, event, tags);
    break;
  case nua_r_subscribe:
  case nua_r_unsubscribe:
    error = nua_stack_subscribe(nua, nh, event, tags);
    break;
  case nua_r_notify:
    error = nua_stack_notify(nua, nh, event, tags);
    break;
  case nua_r_notifier:
    nua_stack_notifier(nua, nh, event, tags);
    break;
  case nua_r_terminate:
    nua_stack_terminate(nua, nh, event, tags);
    break;
  case nua_r_method:
    error = nua_stack_method(nua, nh, event, tags);
    break;
  case nua_r_authenticate:
    nua_stack_authenticate(nua, nh, event, tags);
    break;
  case nua_r_authorize:
    nua_stack_authorize(nua, nh, event, tags);
    break;
  case nua_r_ack:
    error = nua_stack_ack(nua, nh, event, tags);
    break;
  case nua_r_respond:
    nua_stack_respond(nua, nh, e->e_status, e->e_phrase, tags);
    break;
  case nua_r_destroy:
    nua_stack_destroy_handle(nua, nh, tags);
    su_msg_destroy(nua->nua_signal);
    return;
  default:
    break;
  }

  if (error < 0) {
    nua_stack_event(nh->nh_nua, nh, NULL, event,
		    NUA_ERROR_AT(__FILE__, __LINE__), NULL);
  }

  su_msg_destroy(nua->nua_signal);
}
Exemple #9
0
/**Initialize client request for sending.
 *
 * This function is called when the request is taken from queue and sent.
 *
 * @retval 0 if request is pending
 * @retval >=1 if error event has been sent
 */
static
int nua_client_init_request0(nua_client_request_t *cr)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_t *nua = nh->nh_nua;
  nua_dialog_state_t *ds = nh->nh_ds;
  msg_t *msg = NULL;
  sip_t *sip;
  url_string_t const *url = NULL;
  tagi_t const *t;
  int has_contact = 0;
  int error = 0;

  if (!cr->cr_method_name)
    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);

  if (cr->cr_msg)
    return nua_client_request_try(cr);

  cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;
  cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;
  cr->cr_terminated = 0, cr->cr_graceful = 0;

  nua_stack_init_handle(nua, nh, cr->cr_tags);

  if (cr->cr_method == sip_method_cancel) {
    if (cr->cr_methods->crm_init) {
      error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags);
      if (error)
	return error;
    }

    if (cr->cr_methods->crm_send)
      return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags);
    else
      return nua_base_client_request(cr, NULL, NULL, cr->cr_tags);
  }

  if (!cr->cr_methods->crm_template ||
      cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0)
    msg = nua_client_request_template(cr);

  sip = sip_object(msg);
  if (!sip)
    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);

  if (nh->nh_tags) {
    for (t = nh->nh_tags; t; t = t_next(t)) {
      if (t->t_tag == siptag_contact ||
	  t->t_tag == siptag_contact_str)
	has_contact = 1;
      else if (t->t_tag == nutag_url)
	url = (url_string_t const *)t->t_value;
    }
  }

  /**@par Populating SIP Request Message with Tagged Arguments
   *
   * The tagged arguments can be used to pass values for any SIP headers
   * to the stack. When the INVITE message (or any other SIP message) is
   * created, the tagged values saved with nua_handle() are used first,
   * next the tagged values given with the operation (nua_invite()) are
   * added.
   *
   * When multiple tags for the same header are specified, the behaviour
   * depends on the header type. If only a single header field can be
   * included in a SIP message, the latest non-NULL value is used, e.g.,
   * @Subject. However, if the SIP header can consist of multiple lines or
   * header fields separated by comma, e.g., @Accept, all the tagged
   * values are concatenated.
   *
   * However, if a tag value is #SIP_NONE (-1 casted as a void pointer),
   * the values from previous tags are ignored.
   */
  for (t = cr->cr_tags; t; t = t_next(t)) {
    if (t->t_tag == siptag_contact ||
	t->t_tag == siptag_contact_str)
      has_contact = 1;
    else if (t->t_tag == nutag_url)
      url = (url_string_t const *)t->t_value;
    else if (t->t_tag == nutag_dialog) {
      cr->cr_dialog = t->t_value > 1;
      cr->cr_contactize = t->t_value >= 1;
    }
    else if (t->t_tag == nutag_auth && t->t_value) {
      /* XXX ignoring errors */
      if (nh->nh_auth)
	auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value);
    }
  }

  if (cr->cr_method == sip_method_register && url == NULL)
    url = (url_string_t const *)NH_PGET(nh, registrar);

  if ((t = cr->cr_tags)) {
    if (sip_add_tagis(msg, sip, &t) < 0)
      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
  }

  /**
   * Now, the target URI for the request needs to be determined.
   *
   * For initial requests, values from tags are used. If NUTAG_URL() is
   * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given,
   * it is used as target URI. If neither is given, the complete request
   * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR()
   * is used. At this point, the target URI is stored in the request line,
   * together with method name and protocol version ("SIP/2.0"). The
   * initial dialog information is also created: @CallID, @CSeq headers
   * are generated, if they do not exist, and a tag is added to the @From
   * header.
   */

  if (!ds->ds_leg) {
    if (ds->ds_remote_tag && ds->ds_remote_tag[0] &&
	sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0)
      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);

    if (sip->sip_from == NULL &&
	sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0)
      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);

    if (sip->sip_to == NULL && cr->cr_method == sip_method_register &&
      sip_add_dup_as(msg, sip, sip_to_class,
		     (sip_header_t *)sip->sip_from) < 0) {
      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
    }
  }
  else {
    if (ds->ds_route)
      url = NULL;
  }

  if (url && nua_client_set_target(cr, (url_t *)url) < 0)
    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);

  cr->cr_has_contact = has_contact;

  if (cr->cr_methods->crm_init) {
    error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags);
    if (error < -1)
      msg = NULL;
    if (error < 0)
      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);
    if (error != 0)
      return error;
  }

  cr->cr_msg = msg;
  cr->cr_sip = sip;

  return nua_client_request_try(cr);
}
Exemple #10
0
/**Create a client request.
 *
 * @retval 0 if request is pending
 * @retval > 0 if error event has been sent
 * @retval < 0 upon an error
 */
int nua_client_create(nua_handle_t *nh,
		      int event,
		      nua_client_methods_t const *methods,
		      tagi_t const * const tags)
{
  su_home_t *home = nh->nh_home;
  nua_client_request_t *cr;
  sip_method_t method;
  char const *name;

  method = methods->crm_method, name = methods->crm_method_name;
  if (!name) {
    tagi_t const *t = tl_find_last(tags, nutag_method);
    if (t)
      name = (char const *)t->t_value;
  }

  cr = su_zalloc(home, sizeof *cr + methods->crm_extra);
  if (!cr) {
    return nua_stack_event(nh->nh_nua, nh,
			   NULL,
			   (enum nua_event_e)event,
			   NUA_ERROR_AT(__FILE__, __LINE__),
			   NULL);
  }

  cr->cr_methods = methods;
  cr->cr_event = event;
  cr->cr_method = method;
  cr->cr_method_name = name;
  cr->cr_contactize = methods->crm_flags.target_refresh;
  cr->cr_dialog = methods->crm_flags.create_dialog;
  cr->cr_auto = 1;

  if (su_msg_is_non_null(nh->nh_nua->nua_signal)) {
    nua_event_data_t *e = su_msg_data(nh->nh_nua->nua_signal)->ee_data;

    if (tags == e->e_tags && event == e->e_event) {
      cr->cr_auto = 0;

      if (tags) {
	nua_move_signal(cr->cr_signal, nh->nh_nua->nua_signal);
	if (cr->cr_signal) {
	  /* Steal reference from signal */
	  cr->cr_owner = e->e_nh, e->e_nh = NULL;
	  cr->cr_tags = tags;
	}
      }
    }
  }

  if (cr->cr_owner == NULL)
    cr->cr_owner = nua_handle_ref(nh);

  if (tags && cr->cr_tags == NULL)
    cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));

#if HAVE_MEMLEAK_LOG
  SU_DEBUG_0(("%p %s() for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
#endif

  if (nua_client_request_queue(cr))
    return 0;

  return nua_client_init_request(cr);
}