Example #1
0
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);
}
Example #3
0
static
int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
  sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip);

  if (sr->sr_status < 200 || nu == NULL) {
  }
  else if (sr->sr_status < 300 &&
	   /* No subscription if Refer-Sub: false in response */
	   (rs == NULL || !su_casematch(rs->rs_value, "false"))) {
    sr->sr_usage->du_ready = 1;

    nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires);

    if (sr->sr_application)	/* Application responded to REFER */
      nu->nu_substate = nua_substate_active;
  }
  else {
    /* Destroy the implicit subscription usage */
    sr->sr_terminating = 1;
  }

  return nua_base_server_respond(sr, tags);
}
Example #4
0
/** @interal Shut down NOTIFY usage.
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
static int nua_notify_usage_shutdown(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;

  nu->nu_substate = nua_substate_terminated;

  if (cr) {
    SU_DEBUG_5(("%s(%p, %p, %p): using existing cr=%p\n",
		"nua_notify_usage_shutdown",
		(void *)nh, (void *)ds, (void *)du, (void *)cr));

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

    if (nua_client_tcreate(nh, nua_r_notify,
			   &nua_notify_client_methods,
			   SIPTAG_EVENT(du->du_event),
			   NUTAG_SUBSTATE(nua_substate_terminated),
			   TAG_END()) >= 0)
      return 0;
  }

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
  return 200;
}
Example #5
0
static int nua_refer_server_preprocess(nua_server_request_t *sr)
{
  nua_handle_t *nh = sr->sr_owner;
  sip_t const *sip = sr->sr_request.sip;
  struct notifier_usage *nu;
  sip_event_t *o;

  if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id))
    o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
  else
    o = sip_event_make(nh->nh_home, "refer");

  if (o) {
    sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
    msg_header_free(nh->nh_home, (msg_header_t *)o);
  }

  if (!sr->sr_usage)
    return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);

  nu = nua_dialog_usage_private(sr->sr_usage);
  nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires);

  return 0;
}
Example #6
0
static int nua_notify_client_report(nua_client_request_t *cr,
				    int status, char const *phrase,
				    sip_t const *sip,
				    nta_outgoing_t *orq,
				    tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  enum nua_substate substate = nua_substate_terminated;

  if (nu && !cr->cr_terminated)
    substate = nu->nu_substate;

  nua_stack_tevent(nh->nh_nua, nh,
		   nta_outgoing_getresponse(orq),
		   (enum nua_event_e)cr->cr_event,
		   status, phrase,
		   NUTAG_SUBSTATE(substate),
		   SIPTAG_EVENT(du ? du->du_event : NULL),
		   TAG_NEXT(tags));

  if (du && du->du_cr == cr && !cr->cr_terminated) {
    if (nu->nu_requested) {
      /* Re-SUBSCRIBEd while NOTIFY was in progress, resend NOTIFY */
      nua_client_resend_request(cr, 0);
    }
    else if (nu->nu_expires) {
      nua_dialog_usage_set_refresh_at(du, nu->nu_expires);
    }
  }

  return 0;
}
static int nua_refer_client_response(nua_client_request_t *cr,
				     int status, char const *phrase,
				     sip_t const *sip)
{
  nua_dialog_usage_t *du = cr->cr_usage;
  enum nua_substate substate = nua_substate_terminated;

  if (du) {
    struct event_usage *eu = nua_dialog_usage_private(du);

    if (status < 200) {
      substate = eu->eu_substate;
    }
    else if (status < 300) {
      sip_refer_sub_t const *rs = sip_refer_sub(sip);

      if (rs && su_casematch("false", rs->rs_value))
	cr->cr_terminated = 1;

      if (!cr->cr_terminated)
	substate = eu->eu_substate;
    }
  }

  return nua_base_client_tresponse(cr, status, phrase, sip,
				   NUTAG_SUBSTATE(substate),
				   SIPTAG_EVENT(du ? du->du_event : NULL),
				   TAG_END());
}
Example #8
0
static int nua_publish_client_init(nua_client_request_t *cr,
				   msg_t *msg, sip_t *sip,
				   tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du;
  struct publish_usage *pu;

  if (cr->cr_event == nua_r_publish) {
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_publish_usage, NULL);
    if (!du)
      return -1;
    pu = nua_dialog_usage_private(du);
    pu->pu_published = 0;
    if (sip->sip_if_match) {
      pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_if_match);
      if (!pu->pu_etag)
	return -1;
      sip_header_remove(msg, sip, (sip_header_t *)sip->sip_if_match);
    }
  }
  else
    du = nua_dialog_usage_get(nh->nh_ds, nua_publish_usage, NULL);

  cr->cr_usage = du;

  return 0;
}
Example #9
0
static
int nua_publish_client_request(nua_client_request_t *cr,
			       msg_t *msg, sip_t *sip,
			       tagi_t const *tags)
{
  nua_dialog_usage_t *du = cr->cr_usage;
  int un, done;
  sip_etag_t const *etag = NULL;

  un = cr->cr_terminating ||
    cr->cr_event != nua_r_publish ||
    (du && du->du_shutdown) ||
    (sip->sip_expires && sip->sip_expires->ex_delta == 0);
  nua_client_set_terminating(cr, un);
  done = un;

  if (du) {
    struct publish_usage *pu = nua_dialog_usage_private(du);

    if (nua_client_bind(cr, du) < 0)
      return -1;
    if (pu->pu_published)
      done = 1;
    etag = pu->pu_etag;
  }

  return nua_base_client_trequest(cr, msg, sip,
				  SIPTAG_IF_MATCH(etag),
				  TAG_IF(done, SIPTAG_PAYLOAD(NONE)),
				  TAG_IF(done, SIPTAG_CONTENT_TYPE(NONE)),
				  TAG_IF(un, SIPTAG_EXPIRES_STR("0")),
				  TAG_NEXT(tags));
}
Example #10
0
static int nua_publish_client_check_restart(nua_client_request_t *cr,
					    int status, char const *phrase,
					    sip_t const *sip)
{
  char const *restarting = NULL;

  if (cr->cr_terminating || !cr->cr_usage)
    ;
  else if (status == 412)
    restarting = phrase;
  else if (200 <= status && status < 300 &&
	   sip->sip_expires && sip->sip_expires->ex_delta == 0)
    restarting = "Immediate re-PUBLISH";

  if (restarting) {
    struct publish_usage *pu = nua_dialog_usage_private(cr->cr_usage);

    if (pu) {
      pu->pu_published = 0;
      su_free(cr->cr_owner->nh_home, pu->pu_etag), pu->pu_etag = NULL;
      if (nua_client_restart(cr, 100, restarting))
	return 0;
    }
  }

  return nua_base_client_check_restart(cr, status, phrase, sip);
}
static int nua_subscribe_client_request(nua_client_request_t *cr,
					msg_t *msg, sip_t *sip,
					tagi_t const *tags)
{
  nua_dialog_usage_t *du = cr->cr_usage;
  sip_time_t expires = 0;

  if (cr->cr_event == nua_r_destroy || !du || du->du_shutdown)
    nua_client_set_terminating(cr, 1);

  if (du) {
    struct event_usage *eu = nua_dialog_usage_private(du);
    sip_event_t *o = sip->sip_event;

    if (nua_client_bind(cr, du) < 0)
      return -1;

    if (eu->eu_no_id && o && o->o_id) {
      /* Notifier does not handle id properly, remove it */
      msg_header_remove_param(o->o_common, "id");
    }

#if 0
    if (cr->cr_terminating) {
      /* Already terminated subscription? */
      if (eu->eu_substate == nua_substate_terminated ||
	  eu->eu_substate == nua_substate_embryonic) {
	return nua_client_return(cr, SIP_200_OK, msg);
      }
    }
#endif

    nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */

    if (cr->cr_terminating || cr->cr_event != nua_r_subscribe)
      expires = eu->eu_delta = 0;
    else if (sip->sip_expires)
      /* Use value specified by application or negotiated with Min-Expires */
      expires = eu->eu_delta = sip->sip_expires->ex_delta;
    else
    /* We just use common default value, but the default is actually
       package-specific according to the RFC 3265 section 4.4.4:
       [Event] packages MUST also define a
       default "Expires" value to be used if none is specified. */
      expires = eu->eu_delta = 3600;

    eu->eu_final_wait = 0;

    if (eu->eu_substate == nua_substate_terminated)
      eu->eu_substate = nua_substate_embryonic;
  }

  if (!sip->sip_expires || sip->sip_expires->ex_delta != expires) {
    sip_expires_t ex[1];
    sip_expires_init(ex)->ex_delta = expires;
    sip_add_dup(msg, sip, (sip_header_t *)ex);
  }

  return nua_base_client_request(cr, msg, sip, tags);
}
Example #12
0
int nua_subscribe_server_preprocess(nua_server_request_t *sr)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_state_t *ds = nh->nh_ds;
  nua_dialog_usage_t *du;
  struct notifier_usage *nu;
  sip_t const *sip = sr->sr_request.sip;
  sip_event_t *o = sip->sip_event;
  char const *event = o ? o->o_type : NULL;
  /* Maximum expiration time */
  unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600;
  sip_time_t now = sip_now();
  sip_allow_events_t *appl_event = NH_PGET(nh, appl_event);

  assert(nh && nh->nh_nua->nua_dhandle != nh);

  du = nua_dialog_usage_get(ds, nua_notify_usage, o);

  if (du == NULL) {
    /* Create a new subscription */
    du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o);
    if (du == NULL)
      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
  }
  else if (!msg_header_find_param((void *)appl_event, event)) {
    unsigned max_expires;

    /* Refresh existing subscription */
    if (su_strmatch(event, "refer"))
      max_expires = NH_PGET(nh, refer_expires);
    else
      max_expires = NH_PGET(nh, sub_expires);

    if (expires >= max_expires)
      expires = max_expires;

    SR_STATUS1(sr, SIP_200_OK);
  }

  nu = nua_dialog_usage_private(du);

  if (now + expires >= now)
    nu->nu_requested = now + expires;
  else
    nu->nu_requested = SIP_TIME_MAX - 1;

#if SU_HAVE_EXPERIMENTAL
  nu->nu_etags =
    sip_suppress_body_if_match(sip) ||
    sip_suppress_notify_if_match(sip) ||
    sip_has_feature(sr->sr_request.sip->sip_supported, "etags");
#endif

  sr->sr_usage = du;

  return sr->sr_status <= 100 ? 0 : sr->sr_status;
}
Example #13
0
/** @internal Respond to a SUBSCRIBE request.
 *
 */
static
int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags)
{
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);

  msg_t *msg = sr->sr_response.msg;
  sip_t *sip = sr->sr_response.sip;

  if (200 <= sr->sr_status && sr->sr_status < 300) {
    sip_expires_t ex[1];

    sip_expires_init(ex);

    if (nu) {
      sip_time_t now = sip_now();

      if (nu->nu_requested) {
	if (sip->sip_expires) {
	  /* Expires in response can only shorten the expiration time */
	  if (nu->nu_requested > now + sip->sip_expires->ex_delta)
	    nu->nu_requested = now + sip->sip_expires->ex_delta;
	}
	else {
	  unsigned sub_expires = NH_PGET(sr->sr_owner, sub_expires);
	  if (nu->nu_requested > now + sub_expires)
	    nu->nu_requested = now + sub_expires;
	}

	if (nu->nu_requested >= now)
	  nu->nu_expires = nu->nu_requested;
	else
	  nu->nu_expires = now;

	if (nu->nu_expires <= now)
	  nu->nu_substate = nua_substate_terminated;
      }

      if (nu->nu_expires > now)
	ex->ex_delta = nu->nu_expires - now;
    }
    else {
      /* Always add header Expires: 0 */
    }

    if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta)
      sip_add_dup(msg, sip, (sip_header_t *)ex);
  }

  return nua_base_server_respond(sr, tags);
}
/** 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 int nua_refer_client_request(nua_client_request_t *cr,
				    msg_t *msg, sip_t *sip,
				    tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du, *du0 = cr->cr_usage;
  struct event_usage *eu;
  sip_event_t *event;
  int error;

  cr->cr_usage = NULL;

  event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
  if (!event)
    return -1;

  if (du0 == NULL ||
      du0->du_event == NULL ||
      du0->du_event->o_id == NULL ||
      strcmp(du0->du_event->o_id, event->o_id)) {
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event);
    if (!du)
      return -1;
  }
  else {
    du = du0, du0 = NULL;
  }

  if (du0)
    nua_dialog_usage_remove(nh, nh->nh_ds, du0, NULL, NULL);

  eu = nua_dialog_usage_private(cr->cr_usage = du);
  eu ->eu_refer = 1;

  error = nua_base_client_request(cr, msg, sip, tags);

  if (!error) {
    /* Give application an Event header for matching NOTIFYs with REFER */
    nua_stack_tevent(nh->nh_nua, nh, NULL,
		     (enum nua_event_e)cr->cr_event, SIP_100_TRYING,
		     NUTAG_REFER_EVENT(event),
		     SIPTAG_EVENT(event),
		     TAG_END());
    su_free(nh->nh_home, event);
  }

  return error;
}
Example #16
0
static
int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_state_t *ds = nh->nh_ds;
  nua_dialog_usage_t *du = sr->sr_usage;
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  enum nua_substate substate = nua_substate_terminated;
  int notify = 0;
  int retval;

  if (nu && !sr->sr_terminating) {
    substate = nu->nu_substate;
  }

  /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */
  if (nu && nu->nu_requested && substate != nua_substate_embryonic) {
#if SU_HAVE_EXPERIMENTAL
    sip_t const *sip = sr->sr_request.sip;
    sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip);
    sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip);

    if (!nu->nu_tag)
      notify = 1;
    else if (snim && su_casematch(snim->snim_tag, nu->nu_tag))
      notify = 0;
    else if (sbim && su_casematch(snim->snim_tag, nu->nu_tag))
      notify = 1, nu->nu_no_body = 1;
    else
#endif
      notify = 1;

    notify = notify && du->du_cr != NULL;
  }

  retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), TAG_END());

  if (retval >= 2 || du == NULL)
    return retval;

  if (notify) {
    /* Send NOTIFY (and terminate subscription, when needed) */
    nua_dialog_usage_refresh(nh, ds, du);
  }

  return retval;
}
/** Terminate subscription.
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
					nua_dialog_state_t *ds,
					nua_dialog_usage_t *du)
{
  struct event_usage *eu = nua_dialog_usage_private(du);
  nua_client_request_t *cr = du->du_cr;

  assert(eu); (void)eu;

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

  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
  return 200;
}
Example #18
0
static
int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
  sip_t const *sip = sr->sr_request.sip;
  sip_referred_by_t *by = sip->sip_referred_by, default_by[1];
  sip_event_t const *o = sr->sr_usage->du_event;
  enum nua_substate substate = nua_substate_terminated;
  int initial = sr->sr_initial, retval;

  if (nu) {
    if (!sr->sr_terminating)
      substate = nu->nu_substate;
  }

  if (by == NULL) {
     by = sip_referred_by_init(default_by);

    by->b_display = sip->sip_from->a_display;
    *by->b_url = *sip->sip_from->a_url;
  }

  retval = nua_base_server_treport(sr,
				   NUTAG_SUBSTATE(substate),
				   NUTAG_REFER_EVENT(o),
				   TAG_IF(by, SIPTAG_REFERRED_BY(by)),
				   TAG_END());

  if (retval >= 2 || nu == NULL)
    return retval;

  if (initial)
    nua_stack_post_signal(nh,
			  nua_r_notify,
			  SIPTAG_EVENT(o),
			  SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
			  SIPTAG_PAYLOAD_STR("SIP/2.0 100 Trying\r\n"),
			  TAG_END());

  return retval;
}
Example #19
0
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);
}
Example #20
0
static int nua_notify_client_init(nua_client_request_t *cr,
				  msg_t *msg, sip_t *sip,
				  tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du;
  struct notifier_usage *nu;
  sip_event_t const *o = sip->sip_event;
  sip_subscription_state_t *ss = sip->sip_subscription_state;
  sip_time_t now = sip_now();

  if (o == NULL && nh->nh_ds->ds_has_notifys == 1)
    o = NONE;

  du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o);

  if (!du) {
    tagi_t const *newsub = tl_find_last(tags, nutag_newsub);

    if (!newsub || !newsub->t_value)
      return 0; /* Rejected eventually by nua_notify_client_request() */

    /* Create new notifier */
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
    if (du == NULL)
      return -1;

    nu = nua_dialog_usage_private(du);
    nu->nu_expires = now;
  }
  else
    nu = nua_dialog_usage_private(du);


  if (nu->nu_substate == nua_substate_terminated) {
    /*Xyzzy*/;
  }
  else if (ss != NULL) {
    /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
    nu->nu_substate = nua_substate_make(ss->ss_substate);

    if (ss->ss_expires) {
      unsigned long expires = strtoul(ss->ss_expires, NULL, 10);
      if (now + expires < now)
	expires = SIP_TIME_MAX - now - 1;

      /* We can change the lifetime of unsolicited subscription at will */
      if (nu->nu_requested == 0)
	nu->nu_expires = nu->nu_requested = now + expires;
      /* Notifier can only shorten the subscribed time */
      else if (nu->nu_requested >= now + expires)
	nu->nu_expires = nu->nu_requested = now + expires;
    }
    else {
      if (nu->nu_requested >= nu->nu_expires)
	nu->nu_expires = nu->nu_requested;
    }

  }
  else {
    enum nua_substate substate = nu->nu_substate;

    if (nu->nu_requested >= nu->nu_expires)
      nu->nu_expires = nu->nu_requested;

    if (nu->nu_expires > now) {
      tagi_t const *t = tl_find_last(tags, nutag_substate);
      if (t)
        substate = (enum nua_substate)t->t_value;
    }
    else
      substate = nua_substate_terminated;

    switch (substate) {
    case nua_substate_embryonic:
      /*FALLTHROUGH*/
    case nua_substate_pending:
      nu->nu_substate = nua_substate_pending;
      break;
    case nua_substate_active:
    default:
      nu->nu_substate = nua_substate_active;
      break;
    case nua_substate_terminated:
      nu->nu_substate = nua_substate_terminated;
      break;
    }
  }

  cr->cr_usage = du;

  return nua_notify_client_init_etag(cr, msg, sip, tags);
}
Example #21
0
static int nua_notify_client_init_etag(nua_client_request_t *cr,
				       msg_t *msg, sip_t *sip,
				       tagi_t const *tags)
{
#if SU_HAVE_EXPERIMENTAL
  nua_handle_t *nh = cr->cr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(cr->cr_usage);
  nua_server_request_t *sr;

  if (nu->nu_tag)
    su_free(nh->nh_home, nu->nu_tag), nu->nu_tag = NULL;
    nu->nu_no_body = 0;

  if (sip->sip_etag) {
    nu->nu_appl_etags = 1;
    nu->nu_tag = su_strdup(nh->nh_home, sip->sip_etag->g_string);
  }
  else if (!nu->nu_appl_etags && nu->nu_etags) {
    su_md5_t md5[1];
    unsigned char digest[SU_MD5_DIGEST_SIZE];
    sip_payload_t pl[1] = { SIP_PAYLOAD_INIT() };
    char token[2 * 16];

    su_md5_init(md5);

    if (sip->sip_payload) *pl = *sip->sip_payload;

    if (pl->pl_len)
      su_md5_update(md5, pl->pl_data, pl->pl_len);
    su_md5_update(md5, &pl->pl_len, sizeof(pl->pl_len));

    if (sip->sip_content_type)
      su_md5_striupdate(md5, sip->sip_content_type->c_type);

    su_md5_digest(md5, digest);
    token64_e(token, sizeof token, digest, sizeof digest);
    token[(sizeof token) - 1] = '\0';
    nu->nu_tag = su_strdup(nh->nh_home, token);
  }

  if (!nu->nu_requested || !nu->nu_tag)
    return 0;

  /* Check if SUBSCRIBE had matching suppression */
  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
    if (sr->sr_usage == cr->cr_usage && sr->sr_method == sip_method_subscribe)
      break;

  if (sr) {
    sip_t const *sip = sr->sr_request.sip;

    sip_suppress_body_if_match_t *sbim;
    sip_suppress_notify_if_match_t *snim;

    if (cr->cr_usage->du_ready) {
      snim = sip_suppress_notify_if_match(sip);

      if (snim && su_casematch(snim->snim_tag, nu->nu_tag)) {
	if (nu->nu_requested > nu->nu_expires)
	  nu->nu_expires = nu->nu_requested;
	nu->nu_requested = 0;
	return nua_client_return(cr, 202, "NOTIFY Suppressed", msg);
      }
    }

    sbim = sip_suppress_body_if_match(sip);
    if (sbim && su_casematch(sbim->sbim_tag, nu->nu_tag))
      nu->nu_no_body = 1;
  }
#endif

  return 0;
}
Example #22
0
static
int nua_notify_client_request(nua_client_request_t *cr,
			      msg_t *msg, sip_t *sip,
			      tagi_t const *tags)
{
  nua_dialog_usage_t *du = cr->cr_usage;
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  su_home_t *home = msg_home(msg);
  sip_time_t now = sip_now();
  sip_subscription_state_t *ss = sip->sip_subscription_state;
  char const *expires;

  if (du == NULL)		/* Subscription has been terminated */
    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);

  assert(du && nu);

  if (du && nua_client_bind(cr, du) < 0)
    return -1;

  if (nu->nu_requested)
    nu->nu_expires = nu->nu_requested;
  nu->nu_requested = 0;

  if (nu->nu_expires <= now || du->du_shutdown) {
    nu->nu_substate = nua_substate_terminated;
    expires = "expires=0";
  }
  else {
    expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now);
  }

  if (ss == NULL || nua_substate_make(ss->ss_substate) != nu->nu_substate) {
    if (nu->nu_substate == nua_substate_terminated)
      expires = nu->nu_expires > now ? "reason=noresource" : "reason=timeout";

    ss = sip_subscription_state_format(home, "%s;%s",
				       nua_substate_name(nu->nu_substate),
				       expires);

    msg_header_insert(msg, (void *)sip, (void *)ss);
  }
  else if (nu->nu_substate != nua_substate_terminated) {
    msg_header_replace_param(home, ss->ss_common, expires);
  }

#if SU_HAVE_EXPERIMENTAL
  if (nu->nu_tag && !sip->sip_etag)
    msg_header_add_make(msg, (void *)sip, sip_etag_class, nu->nu_tag);

  if (nu->nu_no_body) {
    nu->nu_no_body = 0;
    msg_header_remove(msg, (void *)sip, (void *)sip->sip_payload);
    msg_header_remove(msg, (void *)sip, (void *)sip->sip_content_length);
  }
#endif

  if (nu->nu_substate == nua_substate_terminated)
    nua_client_set_terminating(cr, 1);

  if (cr->cr_terminating) {
    nua_server_request_t *sr;
    for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) {
      if (sr->sr_usage == du) {
	/* If subscribe has not been responded, don't terminate usage by NOTIFY */
	sr->sr_terminating = 1;
	nua_client_set_terminating(cr, 0);
	break;
      }
    }
  }

  if (du->du_event && !sip->sip_event)
    sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event);

  return nua_base_client_request(cr, msg, sip, tags);
}
int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_usage_t *du = sr->sr_usage;
  struct event_usage *eu = nua_dialog_usage_private(du);
  sip_t const *sip = sr->sr_request.sip;
  enum nua_substate substate = nua_substate_terminated;
  sip_time_t delta = SIP_TIME_MAX;
  sip_event_t const *o = sip->sip_event;
  int retry = -1;
  int retval;

  if (eu) {
    sip_subscription_state_t *subs = sip->sip_subscription_state;

    substate = eu->eu_substate;

    if (substate == nua_substate_active || substate == nua_substate_pending) {
      if (subs && subs->ss_expires) {
	sip_time_t now = sip_now();
	sip_time_t delta0 = strtoul(subs->ss_expires, NULL, 10);
	if (now + delta0 <= eu->eu_expires)
	  delta = delta0;
      }
    }
    else if (substate == nua_substate_embryonic) {
      if (subs && subs->ss_reason) {
	if (su_casematch(subs->ss_reason, "deactivated")) {
	  retry = 0;		/* retry immediately */
	}
	else if (su_casematch(subs->ss_reason, "probation")) {
	  retry = 30;
	  if (subs->ss_retry_after)
	    retry = strtoul(subs->ss_retry_after, NULL, 10);
	  if (retry > 3600)
	    retry = 3600;
	}
      }
    }
    else if (substate == nua_substate_terminated) {
      sr->sr_terminating = 1;
    }
  }

  retval = nua_base_server_treport(sr, /* can destroy sr */
				   NUTAG_SUBSTATE(substate),
				   SIPTAG_EVENT(o),
				   TAG_NEXT(tags));

  if (retval != 1 || du == NULL)
    return retval;

  if (eu->eu_unsolicited) {
    /* Xyzzy */;
  }
  else if (retry >= 0) {		/* Try to subscribe again */
    /* XXX - this needs through testing */
    nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
    nua_dialog_usage_set_refresh_range(du, retry, retry + 5);
  }
  else {
    if (delta < SIP_TIME_MAX) {
      nua_dialog_usage_set_refresh(du, delta);
      eu->eu_expires = du->du_refquested + delta;
    }
  }

  return retval;
}
int nua_notify_server_preprocess(nua_server_request_t *sr)
{
  nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
  nua_dialog_usage_t *du;
  struct event_usage *eu;
  sip_t const *sip = sr->sr_request.sip;
  sip_event_t *o = sip->sip_event;
  enum nua_substate substate = nua_substate_terminated;
  sip_subscription_state_t *subs = sip->sip_subscription_state;
  char const *what = "", *reason = NULL;
  int solicited = 1;

  du = nua_dialog_usage_get(ds, nua_subscribe_usage, o);

  if (du == NULL) {
    if (!sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), SIP_METHOD_NOTIFY))
      return SR_STATUS(sr, 481, "Subscription Does Not Exist");

    solicited = 0;    /* Let application to handle unsolicited NOTIFY */
    du = nua_dialog_usage_add(sr->sr_owner, ds, nua_subscribe_usage, o);
    if (!du)
      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
  }

  sr->sr_usage = du;
  eu = nua_dialog_usage_private(du); assert(eu);
  eu->eu_notified++;
  if (!o || !o->o_id)
    eu->eu_no_id = 1;

  if (subs == NULL) {
    /* Compatibility */
    unsigned long delta = eu->eu_delta;
    if (sip->sip_expires)
      delta = sip->sip_expires->ex_delta;

    if (delta == 0)
      substate = nua_substate_terminated, what = "terminated";
    else
      substate = nua_substate_active, what = "active";
  }
  else if (su_casematch(subs->ss_substate, what = "terminated")) {
    substate = nua_substate_terminated;
    reason = subs->ss_reason;

    if (su_casematch(reason, "deactivated") ||
	su_casematch(reason, "probation"))
      substate = nua_substate_embryonic;
  }
  else if (su_casematch(subs->ss_substate, what = "pending")) {
    substate = nua_substate_pending;
  }
  else /* if (su_casematch(subs->ss_substate, what = "active")) */ {
    /* Any extended state is considered as active */
    what = subs->ss_substate;
    substate = nua_substate_active;
  }

  eu->eu_substate = substate;
  if (!solicited)
    eu->eu_unsolicited = 1;

  SU_DEBUG_5(("nua(%p): %s: %s (%s)\n",
	      (void *)sr->sr_owner, "nua_notify_server_preprocess",
	      what, reason ? reason : ""));

  if (solicited)
    return SR_STATUS1(sr, SIP_200_OK);

  return 0;
}
static int nua_subscribe_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;
  struct event_usage *eu = nua_dialog_usage_private(du);
  enum nua_substate substate;

  if (eu == NULL || cr->cr_terminated)
    substate = nua_substate_terminated;
  else if (status >= 300)
    substate = eu->eu_substate;
  else {
    int win_messenger_enable = NH_PGET(nh, win_messenger_enable);
    sip_time_t delta, now = sip_now();

    du->du_ready = 1;

    if (eu->eu_substate != nua_substate_terminated)
      /* If there is no @Expires header,
	 use default value stored in eu_delta */
      delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date,
				  eu->eu_delta, now);
    else
      delta = 0;

    if (delta > eu->eu_delta)
      delta = eu->eu_delta;

    if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) {
      /* Notify from messanger does not match with dialog tag */
      nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, "");
    }

    if (delta > 0) {
      nua_dialog_usage_set_refresh(du, delta);
      eu->eu_expires = du->du_refquested + delta;
    }
    else {
      if (eu->eu_substate == nua_substate_terminated) {
	if (!eu->eu_notified)
	  eu->eu_substate = nua_substate_embryonic;
      }

      if (eu->eu_substate != nua_substate_terminated) {
	/* Wait 32 seconds for NOTIFY. */
	delta = 64 * NTA_SIP_T1 / 1000;

	eu->eu_final_wait = 1;

	if (!eu->eu_notified && win_messenger_enable)
	  delta = 4 * 60; 	/* Wait 4 minutes for NOTIFY from Messenger */

	nua_dialog_usage_set_refresh_range(du, delta, delta);
      }
      else {
	nua_dialog_usage_reset_refresh(du);
      }

      eu->eu_expires = du->du_refquested;
    }

    substate = eu->eu_substate;

    if (substate == nua_substate_terminated)
      /* let nua_base_client_tresponse to remove usage */
      cr->cr_terminated = 1;
  }

  return nua_base_client_tresponse(cr, status, phrase, sip,
				   NUTAG_SUBSTATE(substate),
				   SIPTAG_EVENT(du ? du->du_event : NULL),
				   TAG_END());
}