static void mrcp_sofia_on_resource_discover(
						int                   status,
						mrcp_sofia_agent_t   *sofia_agent,
						nua_handle_t         *nh,
						mrcp_sofia_session_t *sofia_session,
						sip_t const          *sip,
						tagi_t                tags[])
{
	mrcp_session_t *session = sofia_session->session;
	if(session) {
		const char *remote_sdp_str = NULL;
		mrcp_session_descriptor_t *descriptor = NULL;

		tl_gets(tags, 
				SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
				TAG_END());

		if(remote_sdp_str) {
			sdp_parser_t *parser = NULL;
			sdp_session_t *sdp = NULL;
			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP %s\n%s", 
				session->name,
				remote_sdp_str);

			parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
			sdp = sdp_session(parser);
			descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,session->pool);
			sdp_parser_free(parser);
		}

		mrcp_session_discover_response(session,descriptor);
	}
}
static void mrcp_sofia_on_state_change(
						int                   status,
						mrcp_sofia_agent_t   *sofia_agent,
						nua_handle_t         *nh,
						mrcp_sofia_session_t *sofia_session,
						sip_t const          *sip,
						tagi_t                tags[])
{
	int ss_state = nua_callstate_init;
	tl_gets(tags,
			NUTAG_CALLSTATE_REF(ss_state),
			TAG_END());
	
	apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"SIP Call State %s [%s]",
		sofia_session ? sofia_session->session->name : "",
		nua_callstate_name(ss_state));

	switch(ss_state) {
		case nua_callstate_ready:
			mrcp_sofia_on_session_ready(status,sofia_agent,nh,sofia_session,sip,tags);
			break;
		case nua_callstate_terminated:
			mrcp_sofia_on_session_terminate(status,sofia_agent,nh,sofia_session,sip,tags);
			break;
	}
}
int nth_engine_set_params(nth_engine_t * he,
			  tag_type_t tag, tag_value_t value, ...)
{
  int n;
  ta_list ta;
  unsigned expires;
  int error_msg;
  msg_mclass_t const *mclass;
  int mflags;
  int streaming;
  url_string_t const *proxy;

  if (he == NULL)
    return (errno = EINVAL), -1;

  ta_start(ta, tag, value);

  expires = he->he_expires;
  error_msg = he->he_error_msg;
  mclass = he->he_mclass;
  mflags = he->he_mflags;
  streaming = he->he_streaming;
  proxy = (void *) he->he_default_proxy;

  n = tl_gets(ta_args(ta),
	      NTHTAG_EXPIRES_REF(expires),
	      NTHTAG_ERROR_MSG_REF(error_msg),
	      NTHTAG_MCLASS_REF(mclass),
	      NTHTAG_MFLAGS_REF(mflags),
	      NTHTAG_STREAMING_REF(streaming),
	      NTHTAG_PROXY_REF(proxy), TAG_END());

  if (n > 0) {
    if (proxy->us_url != he->he_default_proxy) {
      url_t *copy = url_hdup(he->he_home, proxy->us_url);

      if (proxy && !copy) {
	n = -1;
      } else {
	su_free(he->he_home, (void *) he->he_default_proxy);
	he->he_default_proxy = copy;
      }
    }
  }

  if (n > 0) {
    he->he_expires = expires;
    he->he_error_msg = error_msg != 0;
    if (mclass)
      he->he_mclass = mclass;
    else
      he->he_mclass = http_default_mclass();
    he->he_mflags = mflags;
    he->he_streaming = streaming != 0;
  }

  ta_end(ta);

  return n;
}
Exemple #4
0
static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags)
{
  soa_static_session_t *sss = (soa_static_session_t *)ss;
  char const *audio_aux = sss->sss_audio_aux;
  int ordered_user = sss->sss_ordered_user;
  int reuse_rejected = sss->sss_reuse_rejected;
  int n, m;

  n = tl_gets(tags,
	      SOATAG_AUDIO_AUX_REF(audio_aux),
	      SOATAG_ORDERED_USER_REF(ordered_user),
	      SOATAG_REUSE_REJECTED_REF(reuse_rejected),
	      TAG_END());

  if (n > 0 && !su_casematch(audio_aux, sss->sss_audio_aux)) {
    char *s = su_strdup(ss->ss_home, audio_aux), *tbf = sss->sss_audio_aux;
    if (s == NULL && audio_aux != NULL)
      return -1;
    sss->sss_audio_aux = s;
    if (tbf)
      su_free(ss->ss_home, tbf);
  }

  sss->sss_ordered_user = ordered_user != 0;
  sss->sss_reuse_rejected = reuse_rejected != 0;

  m = soa_base_set_params(ss, tags);
  if (m < 0)
    return m;

  return n + m;
}
static void mrcp_sofia_on_session_ready(
						int                   status,
						mrcp_sofia_agent_t   *sofia_agent,
						nua_handle_t         *nh,
						mrcp_sofia_session_t *sofia_session,
						sip_t const          *sip,
						tagi_t                tags[])
{
	const char *local_sdp_str = NULL, *remote_sdp_str = NULL;
	mrcp_session_descriptor_t *descriptor = NULL;

	tl_gets(tags, 
			SOATAG_LOCAL_SDP_STR_REF(local_sdp_str),
			SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
			TAG_END());

	if(remote_sdp_str) {
		sdp_parser_t *parser = NULL;
		sdp_session_t *sdp = NULL;
		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str);

		parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
		sdp = sdp_session(parser);
		descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,sofia_session->session->pool);
		sdp_parser_free(parser);
	}

	mrcp_session_answer(sofia_session->session,descriptor);
}
static void mrcp_sofia_on_state_change(
						int                   status,
						mrcp_sofia_agent_t   *sofia_agent,
						nua_handle_t         *nh,
						mrcp_sofia_session_t *sofia_session,
						sip_t const          *sip,
						tagi_t                tags[])
{
	int nua_state = nua_callstate_init;
	tl_gets(tags,
			NUTAG_CALLSTATE_REF(nua_state),
			TAG_END());
	
	if(!sofia_session || !sofia_session->session) {
		apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"SIP Call State [%s]", nua_callstate_name(nua_state));
		return;
	}
	apt_obj_log(APT_LOG_MARK,APT_PRIO_NOTICE,sofia_session->session->log_obj,"SIP Call State %s [%s]",
		sofia_session->session->name,
		nua_callstate_name(nua_state));

	switch(nua_state) {
		case nua_callstate_ready:
			mrcp_sofia_on_session_ready(status,sofia_agent,nh,sofia_session,sip,tags);
			break;
		case nua_callstate_terminated:
			mrcp_sofia_on_session_terminate(status,sofia_agent,nh,sofia_session,sip,tags);
			break;
	}
	sofia_session->nua_state = nua_state;
}
/**Create an authentication plugin module.
 *
 * The function auth_mod_create() creates a module used to authenticate the
 * requests.
 *
 * @param root pointer to a su_root_t object
 * @param tag,value,... tagged argument list
 *
 * @TAGS
 * AUTHTAG_METHOD(), AUTHTAG_REALM(), AUTHTAG_DB(), AUTHTAG_ALLOW(),
 * AUTHTAG_QOP(), AUTHTAG_ALGORITHM(), AUTHTAG_EXPIRES(),
 * AUTHTAG_BLACKLIST(), AUTHTAG_FORBIDDEN(), AUTHTAG_ANONYMOUS(),
 * AUTHTAG_REMOTE().
 */
auth_mod_t *auth_mod_create(su_root_t *root,
			    tag_type_t tag, tag_value_t value, ...)
{
  auth_mod_t *am = NULL;

  ta_list ta;

  char const *method = NULL;

  ta_start(ta, tag, value);

  tl_gets(ta_args(ta),
	  AUTHTAG_METHOD_REF(method),
	  TAG_NULL());

  if (method) {
    auth_scheme_t *bscheme = NULL;
    char const *base;
    size_t len;

    base = strrchr(method, '+');
    if (base)
      len = base++ - method;
    else
      len = strlen(method);

    if (base == NULL)
      ;
    else if (su_casematch(base, "Basic"))
      bscheme = auth_scheme_basic;
    else if (su_casematch(base, "Digest"))
      bscheme = auth_scheme_digest;

    if (base == NULL || bscheme) {
      int i;

      for (i = 0; schemes[i] && i < N; i++) {
	if (su_casenmatch(schemes[i]->asch_method, method, len) &&
	    schemes[i]->asch_method[len] == 0) {
	  am = auth_mod_alloc(schemes[i], ta_tags(ta));
	  if (schemes[i]->asch_init(am, bscheme, root, ta_tags(ta)) == -1) {
	    auth_mod_destroy(am), am = NULL;
	  }
	  break;
	}
      }
    }
  }

  ta_end(ta);

  return am;
}
/* Create a event view for notifier */
static
nea_event_t *nh_notifier_event(nua_handle_t *nh,
			       su_home_t *home,
			       sip_event_t const *event,
			       tagi_t const *tags)
{
  nea_event_t *ev = nea_event_get(nh->nh_notifier, event->o_type);
  sip_accept_t const *accept = NULL;
  char const  *accept_s = NULL;
  sip_content_type_t const *ct = NULL;
  char const *ct_s = NULL;

  if (ev == NULL) {
    char *o_type, *o_subtype;
    char *temp = NULL;

    o_type = su_strdup(home, event->o_type);
    if (o_type == NULL)
      return NULL;
    o_subtype = strchr(o_type, '.');
    if (o_subtype)
      *o_subtype++ = '\0';

    tl_gets(tags,
	    SIPTAG_ACCEPT_REF(accept),
	    SIPTAG_ACCEPT_STR_REF(accept_s),
	    SIPTAG_CONTENT_TYPE_REF(ct),
	    SIPTAG_CONTENT_TYPE_STR_REF(ct_s),
	    TAG_END());

    /*
     * XXX - We really should build accept header when we add new content
     * types
     */
    if (accept_s == NULL && accept)
      accept_s = temp = sip_header_as_string(home, (sip_header_t *)accept);
    if (accept_s == NULL && ct)
      accept_s = ct->c_type;
    if (accept_s == NULL && ct_s)
      accept_s = ct_s;

    ev = nea_event_create(nh->nh_notifier,
			  authorize_watcher, nh,
			  o_type, o_subtype,
			  ct ? ct->c_type : ct_s,
			  accept_s);

    su_free(home, temp);
    su_free(home, o_type);
  }

  return ev;
}
Exemple #9
0
/**Create a resolver.
 *
 * The function sres_resolver_create() is used to allocate and initialize
 * the resolver object using the Sofia asynchronous reactor #su_root_t.
 */
sres_resolver_t *
sres_resolver_create(su_root_t *root,
		     char const *conf_file_path,
		     tag_type_t tag, tag_value_t value, ...)
{
  sres_resolver_t *res;
  sres_sofia_t *srs;
  sres_cache_t *cache = NULL;
  ta_list ta;

  if (root == NULL)
    return su_seterrno(EFAULT), (void *)NULL;

  ta_start(ta, tag, value);
  tl_gets(ta_args(ta),
	  SRESTAG_RESOLV_CONF_REF(conf_file_path),
	  SRESTAG_CACHE_REF(cache),
	  TAG_END());
  ta_end(ta);

  res = sres_resolver_new_with_cache(conf_file_path, cache, NULL);
  srs = res ? su_zalloc(0, sizeof *srs) : NULL;

  if (res && srs) {
    su_timer_t *t;

    srs->srs_resolver = res;
    srs->srs_root = root;
    srs->srs_socket = INVALID_SOCKET;

    sres_resolver_set_async(res, sres_sofia_update, srs, 0);

    t = su_timer_create(su_root_task(root), SRES_RETRANSMIT_INTERVAL);
    srs->srs_timer = t;

    if (!srs->srs_timer)
      SU_DEBUG_3(("sres: cannot create timer\n"));
#if nomore
    else if (su_timer_set_for_ever(t, sres_sofia_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer\n"));
#else
    else if (sres_resolver_set_timer_cb(res, sres_sofia_set_timer, srs) < 0)
      SU_DEBUG_3(("sres: cannot set timer cb\n"));
#endif
    else
      return res;		/* Success! */

    sres_resolver_destroy(res), res = NULL;
  }

  return res;
}
static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t   *sofia_agent,
									   nua_handle_t         *nh,
									   mrcp_sofia_session_t *sofia_session,
									   sip_t const          *sip,
									   tagi_t                tags[])
{
	apt_bool_t status = FALSE;
	const char *remote_sdp_str = NULL;
	mrcp_session_descriptor_t *descriptor;

	if(!sofia_session) {
		sofia_session = mrcp_sofia_session_create(sofia_agent,nh);
		if(!sofia_session) {
			nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
			return;
		}
	}

	descriptor = mrcp_session_descriptor_create(sofia_session->session->pool);

	tl_gets(tags, 
			SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
			TAG_END());

	if(remote_sdp_str) {
		sdp_parser_t *parser = NULL;
		sdp_session_t *sdp = NULL;
		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_NAMESID_FMT"\n%s",
			sofia_session->session->name,
			MRCP_SESSION_SID(sofia_session->session),
			remote_sdp_str);

		parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
		sdp = sdp_session(parser);
		status = mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,NULL,sofia_session->session->pool);
		sdp_parser_free(parser);
	}

	if(status == FALSE) {
		nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
		return;
	}

	mrcp_session_offer(sofia_session->session,descriptor);
}
static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t   *sofia_agent,
									   nua_handle_t         *nh,
									   mrcp_sofia_session_t *sofia_session,
									   sip_t const          *sip,
									   tagi_t                tags[])
{
	int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
	const char *local_sdp_str = NULL, *remote_sdp_str = NULL;
	mrcp_session_descriptor_t *descriptor = NULL;

	tl_gets(tags, 
			NUTAG_OFFER_RECV_REF(offer_recv),
			NUTAG_ANSWER_RECV_REF(answer_recv),
			NUTAG_OFFER_SENT_REF(offer_sent),
			NUTAG_ANSWER_SENT_REF(answer_sent),
			SOATAG_LOCAL_SDP_STR_REF(local_sdp_str),
			SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
			TAG_END());
	
	if(!sofia_session) {
		sofia_session = mrcp_sofia_session_create(sofia_agent,nh);
		if(!sofia_session) {
			nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
			return;
		}
	}

	if(remote_sdp_str) {
		sdp_parser_t *parser = NULL;
		sdp_session_t *sdp = NULL;
		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str);

		parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
		sdp = sdp_session(parser);		
		descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,sofia_session->session->pool);
		sdp_parser_free(parser);
	}

	if(!descriptor) {
		nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
		return;
	}

	mrcp_session_offer(sofia_session->session,descriptor);
}
int nth_site_set_params(nth_site_t *site,
			tag_type_t tag, tag_value_t value, ...)
{
  int n;
  ta_list ta;

  server_t *server;
  int master;
  msg_mclass_t const *mclass;
  int mflags;
  auth_mod_t *am;

  if (site == NULL)
    return (errno = EINVAL), -1;

  server = site->site_server;
  master = site == server->srv_sites;
  am = site->site_auth;

  mclass = server->srv_mclass;
  mflags = server->srv_mflags;

  ta_start(ta, tag, value);

  n = tl_gets(ta_args(ta),
	      TAG_IF(master, NTHTAG_MCLASS_REF(mclass)),
	      TAG_IF(master, NTHTAG_MFLAGS_REF(mflags)),
	      NTHTAG_AUTH_MODULE_REF(am),
	      TAG_END());

  if (n > 0) {
    if (mclass)
      server->srv_mclass = mclass;
    else
      server->srv_mclass = http_default_mclass();
    server->srv_mflags = mflags;
    auth_mod_ref(am), auth_mod_unref(site->site_auth), site->site_auth = am;
  }

  ta_end(ta);

  return n;
}
static int tport_http_connect_init_primary(tport_primary_t *pri,
					   tp_name_t tpn[1],
					   su_addrinfo_t *ai,
					   tagi_t const *tags,
					   char const **return_culprit)
{
  tport_http_connect_t *thc = (tport_http_connect_t *)pri;
  char const *http_connect = NULL;
  url_t *http_proxy;
  int error;
  char const *host, *port;
  su_addrinfo_t hints[1];

  tl_gets(tags,
	  TPTAG_HTTP_CONNECT_REF(http_connect),
	  TAG_END());
  if (!http_connect)
    return *return_culprit = "missing proxy url", -1;

  http_proxy = url_hdup(pri->pri_home, URL_STRING_MAKE(http_connect)->us_url);
  if (!http_proxy || !http_proxy->url_host)
    return *return_culprit = "invalid proxy url", -1;

  host = http_proxy->url_host;
  port = http_proxy->url_port;
  if (!port || !port[0])
    port = "8080";

  memcpy(hints, ai, sizeof hints);

  hints->ai_flags = 0;
  hints->ai_addr = NULL;
  hints->ai_addrlen = 0;
  hints->ai_next = NULL;
  hints->ai_canonname = NULL;

  error = su_getaddrinfo(host, port, hints, &thc->thc_proxy);
  if (error)
    return *return_culprit = "su_getaddrinfo", -1;

  return tport_tcp_init_client(pri, tpn, ai, tags, return_culprit);
}
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 mrcp_sofia_on_session_ready(
						int                   status,
						mrcp_sofia_agent_t   *sofia_agent,
						nua_handle_t         *nh,
						mrcp_sofia_session_t *sofia_session,
						sip_t const          *sip,
						tagi_t                tags[])
{
	mrcp_session_t *session = sofia_session->session;
	if(session) {
		const char *local_sdp_str = NULL, *remote_sdp_str = NULL;
		mrcp_session_descriptor_t *descriptor = NULL;

		tl_gets(tags, 
				SOATAG_LOCAL_SDP_STR_REF(local_sdp_str),
				SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
				TAG_END());

		if(remote_sdp_str) {
			sdp_parser_t *parser = NULL;
			sdp_session_t *sdp = NULL;
			const char *force_destination_ip = NULL;
			apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_NAMESID_FMT"\n%s",
				session->name,
				MRCP_SESSION_SID(session),
				remote_sdp_str);

			parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
			sdp = sdp_session(parser);
			if(sofia_session->sip_settings->force_destination == TRUE) {
				force_destination_ip = sofia_session->sip_settings->server_ip;
			}
			descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,force_destination_ip,session->pool);
			sdp_parser_free(parser);
		}

		mrcp_session_answer(session,descriptor);
	}
}
/** @internal Terminate notifier. */
void nua_stack_terminate(nua_t *nua,
			 nua_handle_t *nh,
			 nua_event_t e,
			 tagi_t const *tags)
{
  sip_event_t const *event = NULL;
  sip_content_type_t const *ct = NULL;
  sip_payload_t const *pl = NULL;
  char const *event_s = NULL, *ct_s = NULL, *pl_s = NULL;
  nea_event_t *nev = NULL;

  if (nh->nh_notifier == NULL) {
    UA_EVENT2(e, 900, "No event server to terminate");
    return;
  }

  tl_gets(tags,
	  SIPTAG_EVENT_REF(event),
	  SIPTAG_EVENT_STR_REF(event_s),
	  SIPTAG_CONTENT_TYPE_REF(ct),
	  SIPTAG_CONTENT_TYPE_STR_REF(ct_s),
	  SIPTAG_PAYLOAD_REF(pl),
	  SIPTAG_PAYLOAD_STR_REF(pl_s),
	  TAG_END());

  nev = nea_event_get(nh->nh_notifier,
		      event ? event->o_type : event_s);

  if (nev && (pl || pl_s) && (ct || ct_s)) {
    nea_server_update(nh->nh_notifier, nev, TAG_NEXT(tags));
  }

  nh_notifier_shutdown(nh, NULL,
		       NEATAG_REASON("noresource"),
		       TAG_NEXT(tags));

  nua_stack_event(nua, nh, NULL, e, SIP_200_OK, NULL);
}
void
nua_stack_notifier(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
{
  su_home_t home[1] = { SU_HOME_INIT(home) };
  sip_event_t const *event = NULL;
  sip_content_type_t const *ct = NULL;
  sip_payload_t const *pl = NULL;
  url_string_t const *url = NULL;
  char const *event_s = NULL, *ct_s = NULL, *pl_s = NULL;
  nea_event_t *ev;
  int status = 900;
  char const *phrase = nua_internal_error;

  nua_stack_init_handle(nua, nh, tags);

  tl_gets(tags,
	  NUTAG_URL_REF(url),
	  SIPTAG_EVENT_REF(event),
	  SIPTAG_EVENT_STR_REF(event_s),
	  SIPTAG_CONTENT_TYPE_STR_REF(ct_s),
	  SIPTAG_PAYLOAD_REF(pl),
	  SIPTAG_PAYLOAD_STR_REF(pl_s),
	  TAG_END());

  if (!event && !event_s)
    status = 400, phrase = "Missing Event";

  else if (!ct && !ct_s)
    status = 400, phrase = "Missing Content-Type";

  else if (!nh->nh_notifier &&
	   !(nh->nh_notifier =
	     nea_server_create(nua->nua_nta, nua->nua_root,
			       url->us_url,
			       NH_PGET(nh, max_subscriptions),
			       NULL, nh,
			       TAG_NEXT(tags))))
    status = 900, phrase = nua_internal_error;

  else if (!event && !(event = sip_event_make(home, event_s)))
    status = 900, phrase = "Could not create an event header";

  else if (!(ev = nh_notifier_event(nh, home, event, tags)))
    status = 900, phrase = "Could not create an event view";

  else if (nea_server_update(nh->nh_notifier, ev,  TAG_NEXT(tags)) < 0)
    status = 900, phrase = "No content for event";

  else if (nea_server_notify(nh->nh_notifier, ev) < 0)
    status = 900, phrase = "Error when notifying watchers";

  else
    nua_stack_tevent(nua, nh, NULL, e, status = SIP_200_OK,
		     SIPTAG_EVENT(event),
		     SIPTAG_CONTENT_TYPE(ct),
		     TAG_END());

  if (status != 200)
    nua_stack_event(nua, nh, NULL, e, status, phrase, NULL);

  su_home_deinit(home);
}
server_t *server_create(url_t const *url,
			tag_type_t tag, tag_value_t value, ...)
{
  server_t *srv;
  msg_mclass_t const *mclass = NULL;
  tp_name_t tpn[1] = {{ NULL }};
  su_root_t *root = NULL;
  http_server_t const *server = NULL;
  int persistent = 0;
  char const *server_str = SERVER_VERSION;
  ta_list ta;

  ta_start(ta, tag, value);
  tl_gets(ta_args(ta),
	  NTHTAG_ROOT_REF(root),
	  NTHTAG_MCLASS_REF(mclass),
	  TPTAG_REUSE_REF(persistent),
	  HTTPTAG_SERVER_REF(server),
	  HTTPTAG_SERVER_STR_REF(server_str),
	  TAG_END());

  if (!root || !url ||
      (url->url_type != url_http && url->url_type != url_https)
      || !(srv = su_home_new(sizeof(*srv)))) {
    ta_end(ta);
    return NULL;
  }

  tpn->tpn_proto = url_tport_default((enum url_type_e)url->url_type);
  tpn->tpn_canon = url->url_host;
  tpn->tpn_host =  url->url_host;
  tpn->tpn_port = url_port(url);

  srv->srv_tports = tport_tcreate(srv, nth_server_class, root,
				  TPTAG_IDLE(600000),
				  TPTAG_TIMEOUT(300000),
				  ta_tags(ta));

  srv->srv_persistent = persistent;
  srv->srv_max_bodylen = 1 << 30; /* 1 GB */

  if (tport_tbind(srv->srv_tports, tpn, http_tports,
		  TAG_END()) >= 0 ||
      tport_tbind(srv->srv_tports, tpn, http_no_tls_tports,
		  TAG_END()) >= 0) {
    srv->srv_root = root;
    srv->srv_mclass = mclass ? mclass : http_default_mclass();
    srv->srv_mflags = MSG_DO_CANONIC;

    if (server)
      srv->srv_server = http_server_dup(srv->srv_home, server);
    else
      srv->srv_server = http_server_make(srv->srv_home, server_str);

    tport_get_params(srv->srv_tports,
		     TPTAG_QUEUESIZE_REF(srv->srv_queuesize),
		     TAG_END());
  }
  else {
    SU_DEBUG_1(("nth_server_create: cannot bind transports "
		URL_FORMAT_STRING "\n",
		URL_PRINT_ARGS(url)));
    server_destroy(srv), srv = NULL;
  }

  ta_end(ta);

  return srv;
}
int tport_udp_init_primary(tport_primary_t *pri,
			   tp_name_t tpn[1],
			   su_addrinfo_t *ai,
			   tagi_t const *tags,
			   char const **return_culprit)
{
  unsigned rmem = 0, wmem = 0;
  int events = SU_WAIT_IN;
  int s;
#if HAVE_IP_ADD_MEMBERSHIP
  su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
#endif
  int const one = 1; (void)one;

  s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
  if (s == INVALID_SOCKET)
    return *return_culprit = "socket", -1;

  pri->pri_primary->tp_socket = s;

  if (tport_bind_socket(s, ai, return_culprit) < 0)
    return -1;

  tport_set_tos(s, ai, pri->pri_params->tpp_tos);

#if HAVE_IP_ADD_MEMBERSHIP
  if (ai->ai_family == AF_INET &&
      IN_MULTICAST(ntohl(su->su_sin.sin_addr.s_addr))) {
    /* Try to join to the multicast group */
    /* Bind to the SIP address like
       <sip:88.77.66.55:5060;maddr=224.0.1.75;transport=udp> */
    struct ip_mreq imr[1];
    struct in_addr iface;

    memset(imr, 0, sizeof imr);

    imr->imr_multiaddr = su->su_sin.sin_addr;

    if (host_is_ip4_address(tpn->tpn_canon) &&
	su_inet_pton(AF_INET, tpn->tpn_canon, &iface) > 0) {
      imr->imr_interface = iface;
    }

    if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, imr, (sizeof imr)) < 0) {
      SU_DEBUG_3(("setsockopt(%s): %s\n",
		  "IP_ADD_MEMBERSHIP", su_strerror(su_errno())));
    }
#if HAVE_IP_MULTICAST_LOOP
    else
      if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof one) < 0) {
	SU_DEBUG_3(("setsockopt(%s): %s\n",
		    "IP_MULTICAST_LOOP", su_strerror(su_errno())));
    }
#endif
  }
#endif

#if HAVE_IP_MTU_DISCOVER
  {
    /* Turn off DF flag on Linux */
    int dont = IP_PMTUDISC_DONT;
    if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &dont, sizeof(dont)) < 0) {
	SU_DEBUG_3(("setsockopt(%s): %s\n",
		    "IP_MTU_DISCOVER", su_strerror(su_errno())));
    }
  }
#endif

#if HAVE_IP_RECVERR
  if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
    if (setsockopt(s, IPPROTO_IP, IP_RECVERR, &one, sizeof(one)) < 0) {
      if (ai->ai_family == AF_INET)
	SU_DEBUG_3(("setsockopt(%s): %s\n",
		    "IPVRECVERR", su_strerror(su_errno())));
    }
    events |= SU_WAIT_ERR;
  }
#endif
#if HAVE_IPV6_RECVERR
  if (ai->ai_family == AF_INET6) {
    if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVERR, &one, sizeof(one)) < 0)
      SU_DEBUG_3(("setsockopt(IPV6_RECVERR): %s\n", su_strerror(su_errno())));
    events |= SU_WAIT_ERR;
  }
#endif

  tl_gets(tags,
	  TPTAG_UDP_RMEM_REF(rmem),
	  TPTAG_UDP_WMEM_REF(wmem),
	  TAG_END());

  if (rmem != 0 &&
#if HAVE_SO_RCVBUFFORCE
      setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rmem, sizeof rmem) < 0 &&
#endif
      setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&rmem, sizeof rmem) < 0) {
    SU_DEBUG_3(("setsockopt(SO_RCVBUF): %s\n",
		su_strerror(su_errno())));
  }

  if (wmem != 0 &&
#if HAVE_SO_SNDBUFFORCE
      setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&wmem, sizeof wmem) < 0 &&
#endif
      setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&wmem, sizeof wmem) < 0) {
    SU_DEBUG_3(("setsockopt(SO_SNDBUF): %s\n",
		su_strerror(su_errno())));
  }

  pri->pri_primary->tp_events = events;

  tport_init_compressor(pri->pri_primary, tpn->tpn_comp, tags);

  tport_check_trunc(pri->pri_primary, ai);

#if HAVE_SOFIA_STUN
  tport_stun_server_add_socket(pri->pri_primary);
#endif

  return 0;
}
/**Initialize an authentication module instance.
 *
 * The function auth_mod_init_default() initializes an authentication module
 * object used to authenticate the requests.
 *
 * @param am
 * @param base
 * @param root
 * @param tag,value,... tagged argument list
 *
 * @TAGS
 * AUTHTAG_REALM(), AUTHTAG_OPAQUE(), AUTHTAG_DB(), AUTHTAG_QOP(),
 * AUTHTAG_ALGORITHM(), AUTHTAG_EXPIRES(), AUTHTAG_NEXT_EXPIRES(),
 * AUTHTAG_BLACKLIST(), AUTHTAG_FORBIDDEN(), AUTHTAG_ANONYMOUS(),
 * AUTHTAG_FAKE(), AUTHTAG_ALLOW(), AUTHTAG_REMOTE(), and
 * AUTHTAG_MASTER_KEY().
 *
 * @return 0 if successful
 * @return -1 upon an error
 */
int auth_init_default(auth_mod_t *am,
                      auth_scheme_t *base,
                      su_root_t *root,
                      tag_type_t tag, tag_value_t value, ...)
{
    int retval = 0;

    ta_list ta;

    char const *realm = NULL, *opaque = NULL, *db = NULL, *allows = NULL;
    char const *qop = NULL, *algorithm = NULL;
    unsigned expires = 60 * 60, next_expires = 5 * 60;
    unsigned max_ncount = 0;
    unsigned blacklist = 5;
    int forbidden = 0;
    int anonymous = 0;
    int fake = 0;
    url_string_t const *remote = NULL;
    char const *master_key = "fish";
    char *s;

    ta_start(ta, tag, value);

    /* Authentication stuff */
    tl_gets(ta_args(ta),
            AUTHTAG_REALM_REF(realm),
            AUTHTAG_OPAQUE_REF(opaque),
            AUTHTAG_DB_REF(db),
            AUTHTAG_QOP_REF(qop),
            AUTHTAG_ALGORITHM_REF(algorithm),
            AUTHTAG_EXPIRES_REF(expires),
            AUTHTAG_NEXT_EXPIRES_REF(next_expires),
            AUTHTAG_MAX_NCOUNT_REF(max_ncount),
            AUTHTAG_BLACKLIST_REF(blacklist),
            AUTHTAG_FORBIDDEN_REF(forbidden),
            AUTHTAG_ANONYMOUS_REF(anonymous),
            AUTHTAG_FAKE_REF(fake),
            AUTHTAG_ALLOW_REF(allows),
            AUTHTAG_REMOTE_REF(remote),
            AUTHTAG_MASTER_KEY_REF(master_key),
            TAG_NULL());

    if (!realm) realm = "*";
    if (!allows) allows = "ACK, BYE, CANCEL";

    am->am_realm = su_strdup(am->am_home, realm);
    am->am_opaque = su_strdup(am->am_home, opaque);
    am->am_db = su_strdup(am->am_home, db);
    s = su_strdup(am->am_home, allows);
    if (s)
        msg_commalist_d(am->am_home, &s, &am->am_allow, NULL);
    am->am_expires = expires;
    am->am_next_exp = next_expires;
    am->am_max_ncount = max_ncount;
    am->am_blacklist = blacklist;
    am->am_forbidden = forbidden;
    am->am_anonymous = anonymous;
    am->am_fake = fake;
    am->am_remote = url_hdup(am->am_home, (url_t *)remote);
    am->am_algorithm = algorithm ? su_strdup(am->am_home, algorithm) : "MD5";
    am->am_nextnonce = !algorithm || su_casematch(algorithm, "MD5");
    if (next_expires == 0)
        am->am_nextnonce = 0;
    am->am_qop = su_strdup(am->am_home, qop);

    if (master_key) {
        su_md5_t md5[1];

        su_md5_init(md5);
        su_md5_strupdate(md5, master_key);
        su_md5_strupdate(md5, "70P 53KR37");
        su_md5_digest(md5, am->am_master_key);
    }

    auth_md5_hmac_key(am);

    /* Make sure that we have something
       that can be used to identify credentials */
    if (am->am_opaque && strcmp(am->am_opaque, "*") == 0) {
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
        char hostname[HOST_NAME_MAX + 1];
        su_md5_t md5[1];
        uint8_t hmac[6];

        gethostname(hostname, sizeof hostname);

        auth_md5_hmac_init(am, md5);

        su_md5_strupdate(md5, hostname);
        su_md5_update(md5, ":", 1);
        if (am->am_remote)
            url_update(md5, am->am_remote);

        auth_md5_hmac_digest(am, md5, hmac, sizeof hmac);

        base64_e(hostname, sizeof hostname, hmac, sizeof hmac);

        am->am_opaque = su_strdup(am->am_home, hostname);

        if (!am->am_opaque) {
            retval = -1;
            SU_DEBUG_1(("%s: cannot create unique identifier\n", __func__));
        }
    }

    if (retval < 0)
        ;
    else if (db) {
        retval = auth_readdb(am);
        if (retval == -1) {
            int err = errno;
            SU_DEBUG_1(("auth-module: %s: %s\n", am->am_db, strerror(err)));
            errno = err;
        }
    }
    else {
        retval = auth_htable_resize(am->am_home, am->am_users, 0);
    }

    ta_end(ta);

    return retval;
}
Exemple #21
0
/** Initialize logging. */
int tport_open_log(tport_master_t *mr, tagi_t *tags)
{
  int n;
  int log_msg = mr->mr_log != 0;
  char const *dump = NULL;
  char const *capt = NULL;;
  
  if(mr->mr_capt_name) capt = mr->mr_capt_name;
  
  n = tl_gets(tags,
	      TPTAG_LOG_REF(log_msg),
	      TPTAG_DUMP_REF(dump),
	      TPTAG_CAPT_REF(capt),
	      TAG_END());

  if (getenv("MSG_STREAM_LOG") != NULL || getenv("TPORT_LOG") != NULL)
    log_msg = 1;
  mr->mr_log = log_msg ? MSG_DO_EXTRACT_COPY : 0;

  if (getenv("TPORT_CAPT"))
    capt = getenv("TPORT_CAPT");
  if (getenv("MSG_DUMP"))
    dump = getenv("MSG_DUMP");
  if (getenv("TPORT_DUMP"))
    dump = getenv("TPORT_DUMP");
 
  if(capt) {

        char *captname, *p, *host_s;
        char port[10];
        su_addrinfo_t *ai = NULL, hints[1] = {{ 0 }};
        unsigned len =0;

        if (mr->mr_capt_name && mr->mr_capt_sock && strcmp(capt, mr->mr_capt_name) == 0)                
              return n;

        captname = su_strdup(mr->mr_home, capt);
        if (captname == NULL)
              return n;
                           
        if(strncmp(captname, "udp:",4) != 0) {
              su_log("tport_open_log: capturing. Only udp protocol supported [%s]\n", captname);          
              return n;
        } 
        
        /* separate proto and host */
        p = captname+4;
        if( (*(p)) == '\0') {
                su_log("malformed ip address\n");
                return n;
        }
        host_s = p;

        if( (p = strrchr(p+1, ':')) == 0 ) {
                su_log("no host or port specified\n");
                return n;
        }
 
        /*the address contains a port number*/
        *p = '\0';
        p++;

        if (atoi(p) <1024  || atoi(p)>65536)
        {
                su_log("invalid port number; must be in [1024,65536]\n");
                return n;
        }

        strncpy(port, p, sizeof(port));
                        
        *p = '\0'; 
        
        /* check if we have [] */
        if (host_s[0] == '[') {
              len = strlen(host_s + 1) - 1;              
              if(host_s[len+1] != ']') {
                su_log("bracket not closed\n");
                return n;            
            }            
            memmove(host_s, host_s + 1, len);
            host_s[len] = '\0';
        }                              

        /* and again */
        captname = su_strdup(mr->mr_home, capt);
        if (captname == NULL) return n;
        
        su_free(mr->mr_home, mr->mr_capt_name);
        mr->mr_capt_name = captname;

        if (mr->mr_capt_sock)
          su_close(mr->mr_capt_sock), mr->mr_capt_sock = 0;        

        /* HINTS && getaddrinfo */
        hints->ai_flags = AI_NUMERICSERV;
        hints->ai_family = AF_UNSPEC; 
        hints->ai_socktype = SOCK_DGRAM;
        hints->ai_protocol = IPPROTO_UDP;
        

        if (su_getaddrinfo(host_s, port, hints, &ai)) {
            su_perror("capture: su_getaddrinfo()");
            return n;
        }
        
	mr->mr_capt_sock = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if (mr->mr_capt_sock == INVALID_SOCKET) {
	    su_perror("capture: invalid socket");
	    return n;
	}

	su_setblocking(mr->mr_capt_sock, 0);         /* Don't block */

	if (connect(mr->mr_capt_sock, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == -1) {
	    if (errno != EINPROGRESS) {
		    su_perror("capture: socket connect");
		    return n;
	    }	                
	}
		
	su_freeaddrinfo(ai);        
  }
  else if(mr->mr_capt_sock) {
      /* close capture server*/
      su_close(mr->mr_capt_sock);
      mr->mr_capt_sock = 0;
  }

  if (dump) {
    time_t now;
    char *dumpname;
    
    if (mr->mr_dump && strcmp(dump, mr->mr_dump) == 0)
      return n;
    dumpname = su_strdup(mr->mr_home, dump);
    if (dumpname == NULL)
      return n;
    su_free(mr->mr_home, mr->mr_dump);
    mr->mr_dump = dumpname;

    if (mr->mr_dump_file && mr->mr_dump_file != stdout)
      fclose(mr->mr_dump_file), mr->mr_dump_file = NULL;

    if (strcmp(dumpname, "-"))
      mr->mr_dump_file = fopen(dumpname, "ab"); /* XXX */
    else
      mr->mr_dump_file = stdout;

    if (mr->mr_dump_file) {
      time(&now);
      fprintf(mr->mr_dump_file, "dump started at %s\n\n", ctime(&now));
    }
  }

  return n;
}
static int tport_ws_init_primary_secure(tport_primary_t *pri,
				 tp_name_t tpn[1],
				 su_addrinfo_t *ai,
				 tagi_t const *tags,
				 char const **return_culprit)
{
  tport_ws_primary_t *wspri = (tport_ws_primary_t *)pri;
  const char *cert = "/ssl.pem";
  const char *key = "/ssl.pem";
  char *homedir;
  char *tbf = NULL;
  su_home_t autohome[SU_HOME_AUTO_SIZE(1024)];
  char const *path = NULL;
  int ret = -1;

  su_home_auto(autohome, sizeof autohome);

  tl_gets(tags,
	  TPTAG_CERTIFICATE_REF(path),
	  TAG_END());

  if (!path) {
    homedir = getenv("HOME");
    if (!homedir)
      homedir = "";
    path = tbf = su_sprintf(autohome, "%s/.sip/auth", homedir);
  }

  if (path) {
    key  = su_sprintf(autohome, "%s/%s", path, "wss.key");
	if (access(key, R_OK) != 0) key = NULL;
	cert = su_sprintf(autohome, "%s/%s", path, "wss.crt");
	if (access(cert, R_OK) != 0) cert = NULL;
	if ( !key )  key  = su_sprintf(autohome, "%s/%s", path, "wss.pem");
	if ( !cert ) cert = su_sprintf(autohome, "%s/%s", path, "wss.pem");
	if (access(key, R_OK) != 0) key = NULL;
	if (access(cert, R_OK) != 0) cert = NULL;
  }

  init_ssl();

  //  OpenSSL_add_all_algorithms();   /* load & register cryptos */                                                                                       
  //  SSL_load_error_strings();     /* load all error messages */                                                                                         
  wspri->ssl_method = SSLv23_server_method();   /* create server instance */
  wspri->ssl_ctx = SSL_CTX_new((SSL_METHOD *)wspri->ssl_method);         /* create context */
  SSL_CTX_sess_set_remove_cb(wspri->ssl_ctx, NULL);
  wspri->ws_secure = 1;

  if ( !wspri->ssl_ctx ) goto done;

  /* set the local certificate from CertFile */
  SSL_CTX_use_certificate_file(wspri->ssl_ctx, cert, SSL_FILETYPE_PEM);
  /* set the private key from KeyFile */
  SSL_CTX_use_PrivateKey_file(wspri->ssl_ctx, key, SSL_FILETYPE_PEM);
  /* verify private key */
  if ( !SSL_CTX_check_private_key(wspri->ssl_ctx) ) {
	  goto done;
  }

  SSL_CTX_set_cipher_list(wspri->ssl_ctx, "HIGH:!DSS:!aNULL@STRENGTH");

  ret = tport_ws_init_primary(pri, tpn, ai, tags, return_culprit);

 done:
  su_home_zap(autohome);
  return ret;
}
Exemple #23
0
int test_events(struct context *ctx)
{
  BEGIN();

  struct endpoint *a = &ctx->a,  *b = &ctx->b;
  struct call *a_call = a->call, *b_call = b->call;
  struct event *e, *en, *es;
  sip_t const *sip;
  tagi_t const *t, *n_tags, *r_tags;
  url_t b_url[1];
  enum nua_substate substate;
  nea_sub_t *sub = NULL;

  char const open[] =
    "<?xml version='1.0' encoding='UTF-8'?>\n"
    "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
    "   entity='pres:[email protected]'>\n"
    "  <tuple id='ksac9udshce'>\n"
    "    <status><basic>open</basic></status>\n"
    "    <contact priority='1.0'>sip:[email protected]</contact>\n"
    "  </tuple>\n"
    "</presence>\n";

  char const closed[] =
    "<?xml version='1.0' encoding='UTF-8'?>\n"
    "<presence xmlns='urn:ietf:params:xml:ns:cpim-pidf' \n"
    "   entity='pres:[email protected]'>\n"
    "  <tuple id='ksac9udshce'>\n"
    "    <status><basic>closed</basic></status>\n"
    "  </tuple>\n"
    "</presence>\n";


/* SUBSCRIBE test

   A			B
   |------SUBSCRIBE---->|
   |<--------405--------|
   |			|

*/
  if (print_headings)
    printf("TEST NUA-12.1: SUBSCRIBE without notifier\n");

  nua_set_params(b->nua, SIPTAG_ALLOW_EVENTS(NULL), TAG_END());
  run_b_until(ctx, nua_r_set_params, until_final_response);

  TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));

  SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
	    SIPTAG_EVENT_STR("presence"),
	    TAG_END());

  run_ab_until(ctx, -1, save_until_final_response,
	       -1, NULL /* XXX save_until_received */);

  /* Client events:
     nua_subscribe(), nua_r_subscribe
  */
  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_subscribe);
  TEST(e->data->e_status, 489);
  TEST_1(!e->next);

#if 0				/* XXX */
  /*
   Server events:
   nua_i_subscribe
  */
  TEST_1(e = b->events->head);
  TEST_E(e->data->e_event, nua_i_subscribe);
  TEST(e->data->e_status, 405);
  TEST_1(sip = sip_object(e->data->e_msg));
  TEST_1(sip->sip_event);
  TEST_S(sip->sip_event->o_type, "presence");
  TEST_1(!e->next);
#endif

  free_events_in_list(ctx, a->events);
  free_events_in_list(ctx, b->events);
  nua_handle_destroy(b_call->nh), b_call->nh = NULL;

  if (print_headings)
    printf("TEST NUA-12.1: PASSED\n");

  /* ---------------------------------------------------------------------- */

/* SUBSCRIBE test using notifier and establishing subscription

   A			B
   |                    |
   |------SUBSCRIBE---->|
   |<--------202--------|
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/

  if (print_headings)
    printf("TEST NUA-12.2: using notifier and establishing subscription\n");

  TEST_1(b_call->nh = nua_handle(b->nua, b_call, TAG_END()));

  *b_url = *b->contact->m_url;

  NOTIFIER(b, b_call, b_call->nh,
	   NUTAG_URL(b_url),
	   SIPTAG_EVENT_STR("presence"),
	   SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
	   SIPTAG_PAYLOAD_STR(closed),
	   NEATAG_THROTTLE(1),
	   TAG_END());
  run_b_until(ctx, nua_r_notifier, until_final_response);

  SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
	    SIPTAG_EVENT_STR("presence"),
	    SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"),
	    TAG_END());

  run_ab_until(ctx, -1, save_until_notified_and_responded,
	       -1, save_until_received);

  /* Client events:
     nua_subscribe(), nua_i_notify/nua_r_subscribe
  */
  TEST_1(en = event_by_type(a->events->head, nua_i_notify));
  TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));

  TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe);
  r_tags = e->data->e_tags;
  TEST_1(tl_find(r_tags, nutag_substate));
  if (es->next == en) {
    TEST_1(200 <= e->data->e_status && e->data->e_status < 300);
    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic);
  }
  else {
    TEST_1(200 <= e->data->e_status && e->data->e_status < 300);
    TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active);
  }

  TEST_1(e = en); TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  n_tags = e->data->e_tags;

  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_1(sip->sip_content_type);
  TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "active");
  TEST_1(sip->sip_subscription_state->ss_expires);
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
  TEST_1(!en->next || !es->next);
  free_events_in_list(ctx, a->events);

  /* XXX --- Do not check server side events */
  free_events_in_list(ctx, b->events);

  if (print_headings)
    printf("TEST NUA-12.2: PASSED\n");

  /* ---------------------------------------------------------------------- */

/* NOTIFY with updated content

   A			B
   |                    |
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/
  if (print_headings)
    printf("TEST NUA-12.3: update notifier\n");

  /* Update presence data */

  NOTIFIER(b, b_call, b_call->nh,
	   SIPTAG_EVENT_STR("presence"),
	   SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"),
	   SIPTAG_PAYLOAD_STR(open),
	   TAG_END());

  run_ab_until(ctx, -1, save_until_notified, -1, save_until_received);

  /* subscriber events:
     nua_i_notify
  */
  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_1(sip->sip_content_type);
  TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "active");
  TEST_1(sip->sip_subscription_state->ss_expires);
  n_tags = e->data->e_tags;
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value,
       nua_substate_active);
  TEST_1(!e->next);
  free_events_in_list(ctx, a->events);

  /* XXX --- Do not check server side events */
  free_events_in_list(ctx, b->events);

  if (print_headings)
    printf("TEST NUA-12.3: PASSED\n");

  /* ---------------------------------------------------------------------- */

/* un-SUBSCRIBE

   A			B
   |                    |
   |------SUBSCRIBE---->|
   |<--------202--------|
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/
  if (print_headings)
    printf("TEST NUA-12.5: un-SUBSCRIBE\n");

  memset(&a->flags, 0, sizeof a->flags);

  UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END());

  run_ab_until(ctx, -1, save_until_notified_and_responded,
	       -1, save_until_subscription);

  /* Client events:
     nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe
  */
  TEST_1(e = a->events->head);
  if (e->data->e_event == nua_i_notify) {
    TEST_E(e->data->e_event, nua_i_notify);
    TEST_1(sip = sip_object(e->data->e_msg));
    n_tags = e->data->e_tags;
    TEST_1(sip->sip_event);
    TEST_1(sip->sip_subscription_state);
    TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
    TEST_1(!sip->sip_subscription_state->ss_expires);
    TEST_1(tl_find(n_tags, nutag_substate));
    TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);

    TEST_1(e = e->next);
    TEST_E(e->data->e_event, nua_r_unsubscribe);
    TEST_1(tl_find(e->data->e_tags, nutag_substate));
    TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
       nua_substate_terminated);
  }
  else {
    TEST_E(e->data->e_event, nua_r_unsubscribe);
    TEST_1(tl_find(e->data->e_tags, nutag_substate));
    /* NOTIFY is no more dropped after successful response to unsubscribe */
    TEST(tl_find(e->data->e_tags, nutag_substate)->t_value,
       nua_substate_active);

    TEST_1(e = e->next);
    TEST_E(e->data->e_event, nua_i_notify);
    TEST_1(sip = sip_object(e->data->e_msg));
    n_tags = e->data->e_tags;
    TEST_1(sip->sip_event);
    TEST_1(sip->sip_subscription_state);
    TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
    TEST_1(!sip->sip_subscription_state->ss_expires);
    TEST_1(tl_find(n_tags, nutag_substate));
    TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
  }
  TEST_1(!e->next);
  free_events_in_list(ctx, a->events);

  /* Server events: nua_i_subscription with terminated status */
  TEST_1(e = b->events->head);
  TEST_E(e->data->e_event, nua_i_subscription);
  TEST(tl_gets(e->data->e_tags,
               NEATAG_SUB_REF(sub),
               NUTAG_SUBSTATE_REF(substate),
               TAG_END()), 2);
  TEST_1(sub);
  TEST(substate, nua_substate_terminated);
  TEST_1(!e->next);

  free_events_in_list(ctx, b->events);

  if (print_headings)
    printf("TEST NUA-12.5: PASSED\n");

  /* ---------------------------------------------------------------------- */
/* Fetch event, SUBSCRIBE with expires: 0

   A			B
   |                    |
   |------SUBSCRIBE---->|
   |<--------202--------|
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/
  if (print_headings)
    printf("TEST NUA-12.5.1: event fetch\n");

  SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
	    SIPTAG_EVENT_STR("presence"),
	    SIPTAG_ACCEPT_STR("application/pidf+xml"),
	    SIPTAG_EXPIRES_STR("0"),
	    TAG_END());

  run_ab_until(ctx, -1, save_until_notified_and_responded,
	       -1, save_until_subscription);

  /* Client events:
     nua_subscribe(), nua_i_notify/nua_r_subscribe
  */
  TEST_1(en = event_by_type(a->events->head, nua_i_notify));
  TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));

  e = es; TEST_E(e->data->e_event, nua_r_subscribe);
  TEST_1(t = tl_find(e->data->e_tags, nutag_substate));
  TEST_1(t->t_value == nua_substate_pending ||
	 t->t_value == nua_substate_terminated ||
	 t->t_value == nua_substate_embryonic);

  e = en; TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  n_tags = e->data->e_tags;

  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_1(sip->sip_content_type);
  TEST_S(sip->sip_content_type->c_type, "application/pidf+xml");
  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value,
       nua_substate_terminated);
  TEST_1(!en->next || !es->next);
  free_events_in_list(ctx, a->events);

  /*
   Server events:
   nua_i_subscription
  */
  TEST_1(e = b->events->head);
  TEST_E(e->data->e_event, nua_i_subscription);
  TEST(tl_gets(e->data->e_tags, NEATAG_SUB_REF(sub), TAG_END()), 1);
  TEST_1(sub);
  TEST_1(!e->next);

  free_events_in_list(ctx, b->events);

  if (print_headings)
    printf("TEST NUA-12.4.1: PASSED\n");


  /* ---------------------------------------------------------------------- */
/* 2nd SUBSCRIBE with event id

   A			B
   |                    |
   |------SUBSCRIBE---->|
   |<--------202--------|
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/
  /* XXX - we should do this before unsubscribing first one */
  if (print_headings)
    printf("TEST NUA-12.4.2: establishing 2nd subscription\n");

   NOTIFIER(b, b_call, b_call->nh,
	    SIPTAG_EVENT_STR("presence"),
	    SIPTAG_CONTENT_TYPE_STR("application/xpidf+xml"),
	    SIPTAG_PAYLOAD_STR(open),
	    NEATAG_THROTTLE(1),
	    NUTAG_SUBSTATE(nua_substate_pending),
	    TAG_END());
  run_b_until(ctx, nua_r_notifier, until_final_response);

  NOTIFIER(b, b_call, b_call->nh,
	   SIPTAG_EVENT_STR("presence"),
	   SIPTAG_CONTENT_TYPE_STR("application/xpidf+xml"),
	   SIPTAG_PAYLOAD_STR(closed),
	   NEATAG_THROTTLE(1),
	   NEATAG_FAKE(1),
	   NUTAG_SUBSTATE(nua_substate_pending),
	   TAG_END());
  run_b_until(ctx, nua_r_notifier, until_final_response);

  SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url),
	    SIPTAG_EVENT_STR("presence;id=1"),
	    SIPTAG_ACCEPT_STR("application/xpidf+xml"),
	    TAG_END());

  run_ab_until(ctx, -1, save_until_notified_and_responded,
	       -1, save_until_subscription);

  /* Client events:
     nua_subscribe(), nua_i_notify/nua_r_subscribe
  */
  TEST_1(en = event_by_type(a->events->head, nua_i_notify));
  TEST_1(es = event_by_type(a->events->head, nua_r_subscribe));

  e = es; TEST_E(e->data->e_event, nua_r_subscribe);
  TEST_1(t = tl_find(e->data->e_tags, nutag_substate));
  TEST_1(t->t_value == nua_substate_pending ||
	 t->t_value == nua_substate_embryonic);

  e = en; TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  n_tags = e->data->e_tags;

  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_S(sip->sip_event->o_id, "1");
  TEST_1(sip->sip_content_type);
  TEST_S(sip->sip_content_type->c_type, "application/xpidf+xml");
  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
  /* Check that we really got "fake" content */
  TEST_1(memmem(sip->sip_payload->pl_data, sip->sip_payload->pl_len,
		"<basic>closed</basic>", strlen("<basic>closed</basic>")));
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "pending");
  TEST_1(sip->sip_subscription_state->ss_expires);
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value,
       nua_substate_pending);
  TEST_1(!en->next || !es->next);
  free_events_in_list(ctx, a->events);

  /*
   Server events:
   nua_i_subscription
  */
  TEST_1(e = b->events->head);
  TEST_E(e->data->e_event, nua_i_subscription);
  TEST(tl_gets(e->data->e_tags, NEATAG_SUB_REF(sub), TAG_END()), 1);
  TEST_1(sub);
  TEST_1(!e->next);

  free_events_in_list(ctx, b->events);

  /* Authorize user A */
  AUTHORIZE(b, b_call, b_call->nh,
	    NUTAG_SUBSTATE(nua_substate_active),
	    NEATAG_SUB(sub),
	    NEATAG_FAKE(0),
	    TAG_END());

  run_ab_until(ctx, -1, save_until_notified,
	       -1, save_until_final_response);

  /* subscriber events:
     nua_i_notify with NUTAG_SUBSTATE(nua_substate_active)
  */
  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_1(sip->sip_content_type);
  TEST_S(sip->sip_content_type->c_type, "application/xpidf+xml");
  TEST_1(sip->sip_payload && sip->sip_payload->pl_data);
  /* Check that we really got real content */
  TEST_1(memmem(sip->sip_payload->pl_data, sip->sip_payload->pl_len,
		"<basic>open</basic>", strlen("<basic>open</basic>")));
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "active");
  TEST_1(sip->sip_subscription_state->ss_expires);
  n_tags = e->data->e_tags;
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active);
  TEST_1(!e->next);
  free_events_in_list(ctx, a->events);

  /*
   Server events:
   nua_r_authorize
  */
  TEST_1(e = b->events->head);
  TEST_E(e->data->e_event, nua_r_authorize);
  TEST_1(!e->next);

  free_events_in_list(ctx, b->events);

  if (print_headings)
    printf("TEST NUA-12.4: PASSED\n");

  /* ---------------------------------------------------------------------- */

/* NOTIFY terminating subscription

   A			B
   |                    |
   |<------NOTIFY-------|
   |-------200 OK------>|
   |                    |
*/

  if (print_headings)
    printf("TEST NUA-12.6: terminate notifier\n");

  TERMINATE(b, b_call, b_call->nh, TAG_END());

  run_ab_until(ctx, -1, save_until_notified, -1, until_final_response);

  /* Client events:
     nua_i_notify
  */
  TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_notify);
  TEST_1(sip = sip_object(e->data->e_msg));
  TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence");
  TEST_S(sip->sip_event->o_id, "1");
  TEST_1(sip->sip_subscription_state);
  TEST_S(sip->sip_subscription_state->ss_substate, "terminated");
  TEST_1(!sip->sip_subscription_state->ss_expires);
  n_tags = e->data->e_tags;
  TEST_1(tl_find(n_tags, nutag_substate));
  TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated);
  TEST_1(!e->next);
  free_events_in_list(ctx, a->events);

  if (print_headings)
    printf("TEST NUA-12.6: PASSED\n");

  /* ---------------------------------------------------------------------- */


  nua_handle_destroy(a_call->nh), a_call->nh = NULL;
  nua_handle_destroy(b_call->nh), b_call->nh = NULL;

  END();			/* test_events */
}