Example #1
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 #2
0
/**@internal Do not refresh. */
void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
{
  if (du) {
    du->du_refquested = sip_now();
    du->du_refresh = 0;
  }
}
Example #3
0
/**@internal Set refresh in range min..max seconds in the future. */
void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du,
					unsigned min, unsigned max)
{
  sip_time_t now = sip_now(), target;
  unsigned delta;

  if (max < min)
    max = min;

  if (min != max)
    delta = su_randint(min, max);
  else
    delta = min;

  if (now + delta >= now)
    target = now + delta;
  else
    target = SIP_TIME_MAX;

  SU_DEBUG_7(("nua(): refresh %s after %lu seconds (in [%u..%u])\n",
	      nua_dialog_usage_name(du), target - now, min, max));

  du->du_refquested = now;

  du->du_refresh = target;
}
Example #4
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 #5
0
/** Set absolute refresh time */
void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
				     sip_time_t target)
{
  SU_DEBUG_7(("nua(): refresh %s after %lu seconds\n",
	      nua_dialog_usage_name(du), target - sip_now()));
  du->du_refresh = target;
}
Example #6
0
/** Set absolute refresh time */
void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
				     sip_time_t target)
{
  sip_time_t now = sip_now();
  if (target < now)
    target = now;
  nua_dialog_usage_set_refresh_in(du, target - now);
}
Example #7
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 #8
0
/**@internal Do not refresh. */
void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
{
  if (du) {
    if (du->du_refresh_timer) {
      su_timer_reset(du->du_refresh_timer);
    }
    du->du_refquested = sip_now();
  }
}
Example #9
0
/** @internal Shut down stack. */
void nua_stack_shutdown(nua_t *nua)
{
  nua_handle_t *nh, *nh_next;
  int busy = 0;
  sip_time_t now = sip_now();
  int status;
  char const *phrase;

  enter;

  if (!nua->nua_shutdown)
    nua->nua_shutdown = now;

  for (nh = nua->nua_handles; nh; nh = nh_next) {
    nua_dialog_state_t *ds = nh->nh_ds;

    nh_next = nh->nh_next;

    busy += nua_dialog_repeat_shutdown(nh, ds);

    if (nh->nh_soa) {
      soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
    }

    if (nua_client_request_pending(ds->ds_cr))
      busy++;

    if (nh_notifier_shutdown(nh, NULL, NEATAG_REASON("noresource"), TAG_END()))
      busy++;
  }

  if (!busy)
    SET_STATUS(200, "Shutdown successful");
  else if (now == nua->nua_shutdown)
    SET_STATUS(100, "Shutdown started");
  else if (now - nua->nua_shutdown < 30)
    SET_STATUS(101, "Shutdown in progress");
  else
    SET_STATUS(500, "Shutdown timeout");

  if (status >= 200) {
    for (nh = nua->nua_handles; nh; nh = nh_next) {
      nh_next = nh->nh_next;
      while (nh->nh_ds && nh->nh_ds->ds_usage) {
	nua_dialog_usage_remove(nh, nh->nh_ds, nh->nh_ds->ds_usage, NULL, NULL);
      }
    }
    su_timer_destroy(nua->nua_timer), nua->nua_timer = NULL;
    nta_agent_destroy(nua->nua_nta), nua->nua_nta = NULL;
  }

  nua_stack_event(nua, NULL, NULL, nua_r_shutdown, status, phrase, NULL);
}
Example #10
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);
}
Example #11
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, sip_now());
  }

  return retval;
}
Example #12
0
static void nua_dialog_usage_set_refresh_timer(nua_dialog_usage_t *du,
                                               su_duration_t timeout,
                                               int deferrable)
{
  du->du_refquested = sip_now();

  if (!du->du_refresh_timer) {
    nua_handle_t const *nh = (nua_handle_t *)du->du_dialog->ds_owner;
    nua_t const *nua = nh->nh_nua;
    du->du_refresh_timer = su_timer_create(su_root_task(nua->nua_root), 0);
  }

  su_timer_deferrable(du->du_refresh_timer, deferrable);

  su_timer_set_interval(du->du_refresh_timer, nua_dialog_refresh_timer, du,
                        timeout);
}
Example #13
0
/**@internal
 * Timer routine.
 *
 * Go through all active handles and execute pending tasks
 */
void nua_stack_timer(nua_t *nua, su_timer_t *t, su_timer_arg_t *a)
{
  nua_handle_t *nh, *nh_next;
  sip_time_t now = sip_now();
  su_root_t *root = su_timer_root(t);

  su_timer_set(t, nua_stack_timer, a);

  if (nua->nua_shutdown) {
    nua_stack_shutdown(nua);
    return;
  }

  for (nh = nua->nua_handles; nh; nh = nh_next) {
    nh_next = nh->nh_next;
    nh_call_pending(nh, now);
    su_root_yield(root);	/* Handle received packets */
  }
}
Example #14
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);
}
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());
}
Example #16
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 #17
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);
}
Example #18
0
int main(int ac, char *av[])
{
  int numeric = 0;
  sip_time_t t = 0, t2 = 0;
  char const *s;
  char buf[1024];

  if (av[1] && strcmp(av[1], "-n") == 0)
    av++, numeric = 1;

  if (av[1] && (strcmp(av[1], "-?") == 0 || strcmp(av[1], "-h") == 0))
    usage();

  if ((s = av[1])) {
    size_t n, m;
    /* Concatenate all arguments with a space in between them */
    for (n = 0; (s = av[1]); av++) {
      m = strlen(s);
      if (n + m + 2 > sizeof(buf))
	exit(1);
      memcpy(buf + n, s, m);
      buf[n + m] = ' ';
      n += m + 1;
    }
    buf[n] = '\0';

    s = buf;

    if (s[0] < '0' || s[0] > '9') {
      if (msg_date_d(&s, &t) < 0) {
	fprintf(stderr, "sip-date: %s is not valid time\n", s);
	exit(1);
      }
    }
    else {
      for (; *s; ) {
	if (msg_delta_d(&s, &t2) < 0)
	  usage();

	switch (*s) {
	case 'y': t2 = YEAR_DAYS(t2) - YEAR_DAYS(EPOCH);
	  /*FALLTHROUGH*/
	case 'd': t += t2 * 24 * 60 * 60; s++; break;
	case 'h': t += t2 * 60 * 60; s++; break;
	case 'm': t += t2 * 60; s++; break;
	case 's':
	  s++;
	  /*FALLTHROUGH*/
	default:
	  t += t2;
	  break;
	}

	while (*s && *s == ' ')
	  s++;
      }
    }
  }
  else {
    t = sip_now();
  }

  if (numeric) {
    msg_delta_e(buf, sizeof(buf), t);
  }
  else {
    msg_date_e(buf, sizeof(buf), t);
  }

  puts(buf);

  return 0;
}
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;
}