END_TEST

START_TEST(client_2_0_1)
{
    nta_outgoing_t *orq;
    struct message *request;
    struct event *response;

    S2_CASE("client-2.0.1", "Send MESSAGE",
            "Basic non-INVITE transaction with "
            "numeric per-transaction outbound proxy");

    orq = nta_outgoing_tcreate(s2->default_leg,
                               s2_nta_orq_callback, NULL,
                               (url_string_t *)s2sip->contact->m_url,
                               SIP_METHOD_MESSAGE,
                               URL_STRING_MAKE("sip:test2.0.example.org"),
                               SIPTAG_FROM_STR("<sip:[email protected]>"),
                               TAG_END());
    fail_unless(orq != NULL);
    request = s2_sip_wait_for_request(SIP_METHOD_MESSAGE);
    fail_unless(request != NULL);
    s2_sip_respond_to(request, NULL, 200, "OK 2.0.1", TAG_END());
    response = s2_nta_wait_for(wait_for_orq, orq,
                               wait_for_status, 200,
                               0);
    s2_sip_free_message(request);
    s2_nta_free_event(response);
    nta_outgoing_destroy(orq);
}
static void
client_setup(void)
{
    s2_nta_setup("NTA", NULL, TAG_END());
    s2_nta_agent_setup(URL_STRING_MAKE("sip:0.0.0.0:*"), NULL, NULL,
                       NTATAG_DEFAULT_PROXY("sip:example.org"),
                       TAG_END());
}
static void
client_setup_tcp_only_server(void)
{
    char const * const transports[] = { "tcp", NULL };

    s2_nta_setup("NTA", transports, TAG_END());
    s2_nta_agent_setup(URL_STRING_MAKE("sip:0.0.0.0:*"), NULL, NULL,
                       NTATAG_DEFAULT_PROXY(s2sip->contact->m_url),
                       TAG_END());
}
int luasofia_nta_agent_create(lua_State * L)
{
    luasofia_su_root_t *lroot = NULL;
    url_string_t * contact = NULL;
    luasofia_nta_agent_t* u_nta_agent = NULL;
    su_home_t *home = su_home_create();
    tagi_t *tags = NULL;

    /* get and check first argument (should be a root_t) */
    lroot = (luasofia_su_root_t*)luaL_checkudata(L, -4, SU_ROOT_MTABLE);

    if(lua_isuserdata (L, -3)) {
        //Since there is no metatable for url_t or url_string_t we cant perform a checkudata here.
        contact = (url_string_t *) lua_touserdata (L, -3);
    } else {
        contact = URL_STRING_MAKE(luaL_checkstring (L, -3));
    }

    /* check the callback function */
    if(!lua_isfunction(L, -2))
        luaL_error(L, "nta_agent_create failed!, expected a callback function !");

    /* check if there is tags */
    tags = luasofia_tags_table_to_taglist(L, -1, home);

    u_nta_agent           = (luasofia_nta_agent_t *) lua_newuserdata(L, sizeof(luasofia_nta_agent_t));
    u_nta_agent->L        = L;
    u_nta_agent->agent    = nta_agent_create (lroot->root,
                                              (url_string_t const *)contact,
                                              nta_agent_message_callback,
                                              (nta_agent_magic_t *)u_nta_agent,
                                              TAG_NEXT(tags));

    // lets hold the ref to the lua callback function.
    lua_pushvalue(L, -3);
    u_nta_agent->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);

    if (!u_nta_agent->agent)
        luaL_error(L, "nta_agent_create failed!");

    /* set its metatable */
    luaL_getmetatable(L, NTA_AGENT_MTABLE);
    lua_setmetatable(L, -2);

    /* store nta_agent at luasofia userdata table 
       userdata_table[nta_agent_lightudata] = nta_agent_fulludata */
    luasofia_userdata_table_set(L, u_nta_agent->agent);

    su_home_unref(home);
    return 1;
}
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);
}
static void invite_setup(void)
{
    dialog = su_home_new(sizeof *dialog);

    fail_unless(dialog != NULL);

    s2_nta_setup("NTA", NULL, TAG_END());

    fail_unless(s2sip->udp.contact != NULL);

    s2_nta_agent_setup(URL_STRING_MAKE("sip:0.0.0.0:*"), NULL, NULL,
                       NTATAG_UA(1),
                       TAG_END());

    leg = nta_leg_tcreate(s2->nta, NULL, NULL,
                          SIPTAG_FROM_STR("<sip:[email protected]>"),
                          SIPTAG_TO_STR("<sip:test2.3.1.example.org>"),
                          TAG_END());

    fail_unless(leg != NULL);
}
END_TEST

START_TEST(client_2_0_2)
{
    nta_outgoing_t *orq;
    struct message *request;
    struct event *response;

    char payload[2048];

    S2_CASE("client-2.0.2", "Send MESSAGE",
            "Basic non-INVITE transaction exceeding "
            "default path MTU (1300 bytes)");

    memset(payload, 'x', sizeof payload);
    payload[(sizeof payload) - 1] = '\0';

    orq = nta_outgoing_tcreate(s2->default_leg,
                               s2_nta_orq_callback, NULL, NULL,
                               SIP_METHOD_MESSAGE,
                               URL_STRING_MAKE("sip:test2.0.example.org"),
                               SIPTAG_FROM_STR("<sip:[email protected]>"),
                               SIPTAG_PAYLOAD_STR(payload),
                               TAG_END());
    fail_unless(orq != NULL);
    request = s2_sip_wait_for_request(SIP_METHOD_MESSAGE);
    fail_unless(request != NULL);
    fail_unless(request->sip->sip_via->v_protocol == sip_transport_tcp);

    s2_sip_respond_to(request, NULL, 200, "OK 2.0.2", TAG_END());
    response = s2_nta_wait_for(wait_for_orq, orq,
                               wait_for_status, 200,
                               0);
    s2_nta_free_event(response);
    nta_outgoing_destroy(orq);
}
END_TEST

START_TEST(client_2_1_2)
{
    nta_outgoing_t *orq;
    struct message *request;
    struct event *response;
    url_t udpurl[1];

    S2_CASE("client-2.1.2", "Send MESSAGE",
            "Non-INVITE transaction to TCP-only server");

    client_setup_tcp_only_server();

    *udpurl = *s2sip->tcp.contact->m_url;
    udpurl->url_params = "transport=udp";

    /* Create DNS records for both UDP and TCP, resolver matches UDP */
    s2_dns_domain("udptcp.org", 1,
                  "s2", 1, udpurl,
                  "s2", 2, s2sip->tcp.contact->m_url,
                  NULL);

    /* Sent to tport selected by resolver */
    orq = nta_outgoing_tcreate(s2->default_leg,
                               s2_nta_orq_callback, NULL,
                               URL_STRING_MAKE("sip:udptcp.org"),
                               SIP_METHOD_MESSAGE,
                               URL_STRING_MAKE("sip:test2.0.example.org"),
                               SIPTAG_FROM_STR("<sip:[email protected]>"),
                               TAG_END());
    fail_unless(orq != NULL);
    response = s2_nta_wait_for(wait_for_orq, orq,
                               wait_for_status, 503,
                               0);
    s2_nta_free_event(response);
    nta_outgoing_destroy(orq);

    /* Message size exceeds 1300, tries to use TCP even if NAPTR points to UDP */
    orq = nta_outgoing_tcreate(s2->default_leg,
                               s2_nta_orq_callback, NULL,
                               URL_STRING_MAKE("sip:udptcp.org"),
                               SIP_METHOD_MESSAGE,
                               URL_STRING_MAKE("sip:test2.0.example.org"),
                               SIPTAG_FROM_STR("<sip:[email protected]>"),
#define ROW "012345678901234567890123456789012345678901234\n"
                               SIPTAG_PAYLOAD_STR( /* > 1300 bytes */
                                   "0000 " ROW "0050 " ROW
                                   "0100 " ROW "0150 " ROW
                                   "0200 " ROW "0250 " ROW
                                   "0300 " ROW "0350 " ROW
                                   "0400 " ROW "0450 " ROW
                                   "0500 " ROW "0550 " ROW
                                   "0600 " ROW "0650 " ROW
                                   "0700 " ROW "0750 " ROW
                                   "0800 " ROW "0850 " ROW
                                   "0900 " ROW "0950 " ROW
                                   "1000 " ROW "1050 " ROW
                                   "1100 " ROW "1150 " ROW
                                   "1200 " ROW "1250 " ROW
                               ),
#undef ROW
                               TAG_END());
    fail_unless(orq != NULL);
    request = s2_sip_wait_for_request(SIP_METHOD_MESSAGE);
    fail_unless(request != NULL);
    fail_unless(request->sip->sip_via->v_protocol == sip_transport_tcp);
    s2_sip_respond_to(request, NULL, 200, "2.1.2", TAG_END());
    s2_sip_free_message(request);
    response = s2_nta_wait_for(wait_for_orq, orq,
                               wait_for_status, 200,
                               0);
    s2_nta_free_event(response);
    nta_outgoing_destroy(orq);
}
    void DrachtioController::run() {
        
        if( m_bDaemonize ) {
            daemonize() ;
        }

		/* now we can initialize logging */
		m_logger.reset( this->createLogger() );
		this->logConfig() ;

        DR_LOG(log_debug) << "Main thread id: " << boost::this_thread::get_id() << endl ;

       /* open stats connection */
        string adminAddress ;
        unsigned int adminPort = m_Config->getAdminPort( adminAddress ) ;
        if( 0 != adminPort ) {
            m_pClientController.reset( new ClientController( this, adminAddress, adminPort )) ;
        }

        string url ;
        m_Config->getSipUrl( url ) ;
        DR_LOG(log_notice) << "starting sip stack on " << url << endl ;
        
        int rv = su_init() ;
        if( rv < 0 ) {
            DR_LOG(log_error) << "Error calling su_init: " << rv << endl ;
            return ;
        }
        ::atexit(su_deinit);
        
        m_root = su_root_create( NULL ) ;
        if( NULL == m_root ) {
            DR_LOG(log_error) << "Error calling su_root_create: " << endl ;
            return  ;
        }
        m_home = su_home_create() ;
        if( NULL == m_home ) {
            DR_LOG(log_error) << "Error calling su_home_create" << endl ;
        }
        su_log_redirect(NULL, __sofiasip_logger_func, NULL);
        
        /* for now set logging to full debug */
        su_log_set_level(NULL, m_Config->getSofiaLogLevel() ) ;
        setenv("TPORT_LOG", "1", 1) ;
        
        /* this causes su_clone_start to start a new thread */
        su_root_threading( m_root, 0 ) ;
        rv = su_clone_start( m_root, m_clone, this, clone_init, clone_destroy ) ;
        if( rv < 0 ) {
           DR_LOG(log_error) << "Error calling su_clone_start" << endl ;
           return  ;
        }
        
        /* enable extended headers */
        if (sip_update_default_mclass(sip_extend_mclass(NULL)) < 0) {
            DR_LOG(log_error) << "Error calling sip_update_default_mclass" << endl ;
            return  ;
        }
 
         /* create our agent */
        char str[URL_MAXLEN] ;
        memset(str, 0, URL_MAXLEN) ;
        strncpy( str, url.c_str(), url.length() ) ;
        
		m_nta = nta_agent_create( m_root,
                                 URL_STRING_MAKE(str),               /* our contact address */
                                 NULL,         /* no callback function */
                                 NULL,                  /* therefore no context */
                                 TAG_NULL(),
                                 TAG_END() ) ;
        
        if( NULL == m_nta ) {
            DR_LOG(log_error) << "Error calling nta_agent_create" << endl ;
            return ;
        }
        
        m_defaultLeg = nta_leg_tcreate(m_nta, defaultLegCallback, this,
                                      NTATAG_NO_DIALOG(1),
                                      TAG_END());
        if( NULL == m_defaultLeg ) {
            DR_LOG(log_error) << "Error creating default leg" << endl ;
            return ;
        }
        
        
        /* save my contact url, via, etc */
        m_my_contact = nta_agent_contact( m_nta ) ;
        ostringstream s ;
        s << "SIP/2.0/UDP " <<  m_my_contact->m_url[0].url_host ;
        if( m_my_contact->m_url[0].url_port ) s << ":" <<  m_my_contact->m_url[0].url_port  ;
        m_my_via.assign( s.str().c_str(), s.str().length() ) ;
        DR_LOG(log_debug) << "My via header: " << m_my_via << endl ;

        m_pDialogController = boost::make_shared<SipDialogController>( this, &m_clone ) ;
              
        /* sofia event loop */
        DR_LOG(log_notice) << "Starting sofia event loop in main thread: " <<  boost::this_thread::get_id() << endl ;

        /* start a timer */
        m_timer = su_timer_create( su_root_task(m_root), 30000) ;
        su_timer_set_for_ever(m_timer, watchdogTimerHandler, this) ;
 
        su_root_run( m_root ) ;
        DR_LOG(log_notice) << "Sofia event loop ended" << endl ;
        
        su_root_destroy( m_root ) ;
        m_root = NULL ;
        su_home_unref( m_home ) ;
        su_deinit() ;

        m_Config.reset();
        this->deinitializeLogging() ;

        
    }