<commit_before>/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@internal
 * @CFILE test_nta.c
 *
 * Test functions for NTA.
 *
 * @author <NAME> <<EMAIL>>
 *
 * @date Created: Tue Aug 21 15:18:26 2001 ppessi
 */

#include "config.h"

typedef struct agent_t agent_t;

#define SU_ROOT_MAGIC_T      agent_t

#include <sofia-sip/su_wait.h>

#include <msg_internal.h>

#define NTA_AGENT_MAGIC_T    agent_t
#define NTA_LEG_MAGIC_T      agent_t
#define NTA_OUTGOING_MAGIC_T agent_t
#define NTA_INCOMING_MAGIC_T agent_t
#define NTA_RELIABLE_MAGIC_T agent_t

#include "sofia-sip/nta.h"
#include "nta_internal.h"
#include <sofia-sip/sip_header.h>
#include <sofia-sip/sip_tag.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/tport.h>
#include <sofia-sip/htable.h>
#include <sofia-sip/sresolv.h>
#include <sofia-sip/su_log.h>
#include <sofia-sip/sofia_features.h>
#include <sofia-sip/hostdomain.h>

#include <sofia-sip/string0.h>

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>

SOFIAPUBVAR su_log_t nta_log[];
SOFIAPUBVAR su_log_t tport_log[];

int tstflags = 0;
#define TSTFLAGS tstflags

#include <sofia-sip/tstdef.h>

#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
#define __func__ name
#endif

#define NONE ((void *)-1)

struct sigcomp_compartment;

char const name[] = "test_nta";

struct agent_t {
  su_home_t       ag_home[1];
  int             ag_flags;
  su_root_t      *ag_root;
  msg_mclass_t   *ag_mclass;
  nta_agent_t    *ag_agent;

  url_string_t   *ag_obp;	/**< Outbound proxy. */

  nta_leg_t      *ag_server_leg; /**< Leg for sip:%@% */
  nta_leg_t      *ag_default_leg; /**< Leg for rest */

  unsigned        ag_drop;

  nta_outgoing_t *ag_orq;
  int             ag_status;

  char const     *ag_comp;
  struct sigcomp_compartment *ag_client_compartment;

  /* Server side */
  int             ag_response;	/**< What we answer by default */
  nta_incoming_t *ag_irq;

  struct sigcomp_compartment *ag_server_compartment;

  char const     *ag_m;

  sip_contact_t const *ag_contact;
  sip_from_t     *ag_alice;
  sip_to_t       *ag_bob;

  sip_contact_t  *ag_m_alice;
  sip_contact_t  *ag_m_bob;
  sip_contact_t  *ag_aliases;

  nta_leg_t      *ag_alice_leg;
  nta_leg_t      *ag_bob_leg;

  msg_t          *ag_request;

  nta_leg_t      *ag_expect_leg;
  nta_leg_t      *ag_latest_leg;
  nta_leg_t      *ag_call_leg;
  nta_leg_t      *ag_tag_remote; /**< If this is set, outgoing_callback()
				  *   tags it with the tag from remote.
				  */
  int             ag_tag_status; /**< Which response established dialog */
  msg_param_t     ag_call_tag;	 /**< Tag used to establish dialog */

  nta_reliable_t *ag_reliable;

  sip_via_t      *ag_out_via;	/**< Outgoing via */
  sip_via_t      *ag_in_via;	/**< Incoming via */

  sip_content_type_t *ag_content_type;
  sip_payload_t  *ag_payload;

  msg_t          *ag_probe_msg;

  /* Dummy servers */
  char const     *ag_sink_port;
  su_socket_t     ag_sink_socket, ag_down_socket;
};

static int test_init(agent_t *ag, char const *resolv_conf);
static int test_deinit(agent_t *ag);
static int test_bad_messages(agent_t *ag);
static int test_routing(agent_t *ag);
static int test_tports(agent_t *ag);
static int test_resolv(agent_t *ag, char const *resolv_conf);
static int test_dialog(agent_t *ag);
static int test_call(agent_t *ag);
static int test_prack(agent_t *ag);
static int test_fix_467(agent_t *ag);

static int test_for_ack(agent_t *ag,
			nta_incoming_t *irq, 
			sip_t const *sip);
static int test_for_ack_or_timeout(agent_t *ag,
				   nta_incoming_t *irq, 
				   sip_t const *sip);

int agent_callback(agent_t *ag,
		   nta_agent_t *nta,
		   msg_t *msg,
		   sip_t *sip)
{
  if (tstflags & tst_verbatim) {
    if (sip->sip_request) {
      printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	     name, __func__, sip->sip_request->rq_method_name, 
	     URL_PRINT_ARGS(sip->sip_request->rq_url),
	     sip->sip_request->rq_version);
    }
    else {
      printf("%s: %s: %s %03d %s\n", name, __func__, 
	     sip->sip_status->st_version, 
	     sip->sip_status->st_status, 
	     sip->sip_status->st_phrase);
    }
  }

  msg_destroy(msg);

  return 0;
}

static
void leg_match(agent_t *ag, nta_leg_t *leg, int always, char const *func)
{
  char const *match = "unknown leg";

  if (!always && (tstflags & tst_verbatim) != tst_verbatim)
    return;

  if (leg == ag->ag_default_leg)
    match = "ag_default_leg";
  else if (leg == ag->ag_server_leg) 
    match = "ag_server_leg";
  else if (leg == ag->ag_alice_leg)
    match = "ag_alice_leg";
  else if (leg == ag->ag_bob_leg)
    match = "ag_bob_leg";

  printf("%s: %s: %smatched with %s\n", name, func, 
	 always ? "mis" : "", match);
}

static
void leg_zap(agent_t *ag, nta_leg_t *leg)
{
  if (leg == ag->ag_default_leg)
    ag->ag_default_leg = NULL;
  else if (leg == ag->ag_server_leg) 
    ag->ag_server_leg = NULL;
  else if (leg == ag->ag_alice_leg)
    ag->ag_alice_leg = NULL;
  else if (leg == ag->ag_bob_leg)
    ag->ag_bob_leg = NULL;
  else 
     printf("%s:%u: %s: did not exist\n", 
	    __FILE__, __LINE__, __func__);

  nta_leg_destroy(leg);
}


int leg_callback_200(agent_t *ag,
		     nta_leg_t *leg,
		     nta_incoming_t *irq,
		     sip_t const *sip)
{
  BEGIN();

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  TEST_1(sip->sip_content_length);
  TEST_1(sip->sip_via);
  TEST_1(sip->sip_from && sip->sip_from->a_tag);

  if (ag->ag_in_via == NULL)
    ag->ag_in_via = sip_via_dup(ag->ag_home, sip->sip_via);

  if (ag->ag_request == NULL)
    ag->ag_request = nta_incoming_getrequest(irq);

  ag->ag_latest_leg = leg;

  if (ag->ag_expect_leg && leg != ag->ag_expect_leg) {
    leg_match(ag, leg, 1, __func__);
    return 500;
  }
  leg_match(ag, leg, 0, __func__);

  if (sip->sip_request->rq_method == sip_method_bye) {
    leg_zap(ag, leg);
  }

  return 200;

  END();
}

int leg_callback_500(agent_t *ag,
		     nta_leg_t *leg,
		     nta_incoming_t *irq,
		     sip_t const *sip)
{
  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  return 500;
}

int new_leg_callback_200(agent_t *ag,
			 nta_leg_t *leg,
			 nta_incoming_t *irq,
			 sip_t const *sip)
{
  BEGIN();

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  TEST_1(sip->sip_content_length);
  TEST_1(sip->sip_via);
  TEST_1(sip->sip_from && sip->sip_from->a_tag);

  ag->ag_latest_leg = leg;

  if (ag->ag_expect_leg && leg != ag->ag_expect_leg) {
    leg_match(ag, leg, 1, __func__);
    return 500;
  }
  leg_match(ag, leg, 0, __func__);

  ag->ag_bob_leg = nta_leg_tcreate(ag->ag_agent,
				   leg_callback_200,
				   ag,
				   URLTAG_URL(sip->sip_request->rq_url),
				   SIPTAG_CALL_ID(sip->sip_call_id),
				   SIPTAG_FROM(sip->sip_to),
				   SIPTAG_TO(sip->sip_from),
				   TAG_END());
  TEST_1(ag->ag_bob_leg);
  TEST_1(nta_leg_tag(ag->ag_bob_leg, NULL));
  TEST_1(nta_leg_get_tag(ag->ag_bob_leg));
  TEST_1(nta_incoming_tag(irq, nta_leg_get_tag(ag->ag_bob_leg)));

  return 200;

  END();
}


int outgoing_callback(agent_t *ag,
		      nta_outgoing_t *orq,
		      sip_t const *sip)
{
  BEGIN();

  int status = sip->sip_status->st_status;

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s %03d %s\n", name, __func__, 
	   sip->sip_status->st_version, 
	   sip->sip_status->st_status, 
	   sip->sip_status->st_phrase);
  }

  TEST(orq, ag->ag_orq);

  ag->ag_status = status;

  if (status < 200)
    return 0;

  if (ag->ag_comp) {
    nta_compartment_decref(&ag->ag_client_compartment);
    ag->ag_client_compartment = nta_outgoing_compartment(orq);
  }

  if (ag->ag_out_via == NULL)
    ag->ag_out_via = sip_via_dup(ag->ag_home, sip->sip_via);

  if (ag->ag_tag_remote) {
    TEST_S(nta_leg_rtag(ag->ag_tag_remote, sip->sip_to->a_tag), 
	   sip->sip_to->a_tag);
    ag->ag_tag_remote = NULL;
  }

  TEST_1(sip->sip_to && sip->sip_to->a_tag);

  nta_outgoing_destroy(orq);
  ag->ag_orq = NULL;
  return 0;

  END();
}

static
int test_magic_branch(agent_t *ag, sip_t const *sip) 
{
  BEGIN();
  
  if (sip) {
    TEST_1(sip->sip_via);
    TEST_S(sip->sip_via->v_branch, "MagicalBranch");
  }

  END();
}

static
int magic_callback(agent_t *ag,
		   nta_outgoing_t *orq,
		   sip_t const *sip)
{
  test_magic_branch(ag, sip);
  return outgoing_callback(ag, orq, sip);
}

void 
nta_test_run(agent_t *ag)
{
  for (ag->ag_status = 0; ag->ag_status < 200;) {
    if (tstflags & tst_verbatim) {
      fputs(".", stdout); fflush(stdout);
    }
    su_root_step(ag->ag_root, 500L);
  }
}

#include <sofia-sip/msg_mclass.h>

int test_init(agent_t *ag, char const *resolv_conf)
{
  char const *contact = "sip:*:*;comp=sigcomp";
  su_sockaddr_t su;
  socklen_t sulen, sulen0;
  su_socket_t s; 
  int af;

  BEGIN();

  TEST_1(ag->ag_root = su_root_create(ag));

  TEST_1(ag->ag_mclass = msg_mclass_clone(sip_default_mclass(), 0, 0));

#if SU_HAVE_IN6
  if (str0cmp(getenv("ipv6"), "true") == 0) {
    contact = "sip:[::]:*;comp=sigcomp";
    af = AF_INET6, sulen0 = sizeof (struct sockaddr_in6);
  }
  else {
    af = AF_INET, sulen0 = sizeof (struct sockaddr_in);
    contact = "sip:0.0.0.0:*;comp=sigcomp";
  }
#else
  af = AF_INET, sulen0 = sizeof (struct sockaddr_in);
  contact = "sip:0.0.0.0:*;comp=sigcomp";
#endif

  if (ag->ag_m)
    contact = ag->ag_m;
  else if (getenv("SIPCONTACT"))
    contact = getenv("SIPCONTACT");

  /* Sink server */
  s = socket(af, SOCK_DGRAM, 0); TEST_1(s != INVALID_SOCKET);
  memset(&su, 0, sulen = sulen0);
  su.su_family = af;
  if (getenv("sink")) {
    su.su_port = htons(atoi(getenv("sink")));
  }
  TEST_1(bind(s, &su.su_sa, sulen) < 0 ? (perror("bind"), 0) : 1);
  TEST_1(getsockname(s, &su.su_sa, &sulen) == 0);

  ag->ag_sink_port = su_sprintf(ag->ag_home, "%u", ntohs(su.su_sin.sin_port));
  ag->ag_sink_socket = s;

  /* Down server */
  s = socket(af, SOCK_STREAM, 0); TEST_1(s != INVALID_SOCKET);
  memset(&su, 0, sulen = sulen0);
  su.su_family = af;
  if (getenv("down")) {
    su.su_port = htons(atoi(getenv("down")));
  }
  TEST_1(bind(s, &su.su_sa, sulen) < 0 ? (perror("bind"), 0) : 1);
  ag->ag_down_socket = s;
  
  /* Create agent */
  TEST_1(ag->ag_agent = nta_agent_create(ag->ag_root,
					 (url_string_t *)contact,
					 NULL,
					 NULL,
					 NTATAG_MCLASS(ag->ag_mclass),
					 NTATAG_USE_TIMESTAMP(1),
					 SRESTAG_RESOLV_CONF(resolv_conf),
					 NTATAG_USE_NAPTR(0),
					 NTATAG_USE_SRV(0),
					 NTATAG_PRELOAD(2048),
					 TAG_END()));

  {
    /* Initialize our headers */
    sip_from_t from[1];
    sip_to_t to[1];
    sip_contact_t m[1];

    sip_from_init(from);
    sip_to_init(to);
    sip_contact_init(m);

    TEST_1(ag->ag_contact = nta_agent_contact(ag->ag_agent));

    *m->m_url = *ag->ag_contact->m_url;
    m->m_url->url_user = "bob";
    TEST_1(ag->ag_m_bob = sip_contact_dup(ag->ag_home, m));

    to->a_display = "Bob";
    *to->a_url = *ag->ag_contact->m_url;
    to->a_url->url_user = "bob";
    to->a_url->url_port = NULL;
    TEST_1(ag->ag_bob = sip_to_dup(ag->ag_home, to));

    *m->m_url = *ag->ag_contact->m_url;
    m->m_url->url_user = "alice";
    TEST_1(ag->ag_m_alice = sip_contact_dup(ag->ag_home, m));

    from->a_display = "Alice";
    *from->a_url = *ag->ag_contact->m_url;
    from->a_url->url_user = "alice";
    from->a_url->url_port = NULL;
    TEST_1(ag->ag_alice = sip_from_dup(ag->ag_home, from));
  }
  {
    char const data[] = 
      "v=0\r\n"
      "o=- 425432 423412 IN IP4 127.0.0.1\r\n"
      "s= \r\n"
      "c=IN IP4 127.0.0.1\r\n"
      "m=5004 audio 8 0\r\n";

    ag->ag_content_type = sip_content_type_make(ag->ag_home, "application/sdp");
    ag->ag_payload = sip_payload_make(ag->ag_home, data);
  }

  {
    sip_contact_t *m;

    ag->ag_aliases = 
      sip_contact_make(ag->ag_home, "sip:127.0.0.1, sip:localhost, sip:[::1]");
    TEST_1(ag->ag_aliases);
    TEST_1(ag->ag_aliases->m_next);
    TEST_1(ag->ag_aliases->m_next->m_next);
    TEST(ag->ag_aliases->m_next->m_next->m_next, NULL);

    for (m = ag->ag_aliases; m; m = m->m_next)
      m->m_url->url_port = ag->ag_contact->m_url->url_port;

    TEST_1(m = sip_contact_dup(ag->ag_home, ag->ag_contact));

    m->m_next = ag->ag_aliases;
    ag->ag_aliases = m;

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_REL100(1),
			      NTATAG_UA(1), 
			      NTATAG_USE_NAPTR(1),
			      NTATAG_USE_SRV(1),
			      NTATAG_MAX_FORWARDS(20),
			      TAG_END()),
	 6);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_DEFAULT_PROXY("sip:127.0.0.1"),
			      TAG_END()), 2);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_DEFAULT_PROXY(NULL),
			      TAG_END()), 2);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_DEFAULT_PROXY("tel:+35878008000"),
			      TAG_END()), -1);

  }
  
  {
    url_t url[1];

    /* Create the server leg */
    *url = *ag->ag_aliases->m_url;
    url->url_user = "%";
    TEST_1(ag->ag_server_leg = nta_leg_tcreate(ag->ag_agent, 
					       leg_callback_200,
					       ag,
					       NTATAG_NO_DIALOG(1),
					       URLTAG_URL(url),
					       TAG_END()));
  }

  END();
}  

int test_reinit(agent_t *ag)
{
  BEGIN();
  /* Create a new default leg */
  nta_leg_destroy(ag->ag_default_leg), ag->ag_default_leg = NULL;
  TEST_1(ag->ag_default_leg = nta_leg_tcreate(ag->ag_agent, 
					     leg_callback_200,
					     ag,
					     NTATAG_NO_DIALOG(1),
					     TAG_END()));
  END();
}

int test_deinit(agent_t *ag)
{
  BEGIN();

  if (ag->ag_request) msg_destroy(ag->ag_request), ag->ag_request = NULL;

  su_free(ag->ag_home, ag->ag_in_via), ag->ag_in_via = NULL;

  nta_leg_destroy(ag->ag_alice_leg);
  nta_leg_destroy(ag->ag_bob_leg);
  nta_leg_destroy(ag->ag_default_leg);
  nta_leg_destroy(ag->ag_server_leg);

  nta_agent_destroy(ag->ag_agent);
  su_root_destroy(ag->ag_root);

  su_free(ag->ag_home, (void *)ag->ag_sink_port), ag->ag_sink_port = NULL;

  free(ag->ag_mclass), ag->ag_mclass = NULL;

  END();
}  

static
int readfile(FILE *f, void **contents)
{
  /* Read in whole (binary!) file */
  char *buffer = NULL;
  long size;
  int len = -1;
  
  /* Read whole file in */
  if (fseek(f, 0, SEEK_END) < 0 ||
      (size = ftell(f)) < 0 ||
      fseek(f, 0, SEEK_SET) < 0 ||
      (long)(len = size) != size) {
    fprintf(stderr, "%s: unable to determine file size (%s)\n", 
	    __func__, strerror(errno));
    return -1;
  }

  if (!(buffer = malloc(len + 2)) ||
      fread(buffer, 1, len, f) != len) {
    fprintf(stderr, "%s: unable to read file (%s)\n", __func__, strerror(errno));
    if (buffer)
      free(buffer);
    return -1;
  }

  buffer[len] = '\0';

  *contents = buffer;

  return len;
}

#if HAVE_DIRENT_H
#include <dirent.h>
#endif 

static int test_bad_messages(agent_t *ag)
{
#if HAVE_DIRENT_H
  DIR *dir;
  struct dirent *d;
  char name[PATH_MAX + 1] = "../sip/tests/";
  size_t offset;
  char const *host, *port;
  su_addrinfo_t *ai,  hints[1];
  su_socket_t s;
  su_sockaddr_t su[1];
  socklen_t sulen;
  char via[64];
  size_t vlen;
  int i;

  dir = opendir(name);
  if (dir == NULL && getenv("srcdir")) {
    strncpy(name, getenv("srcdir"), PATH_MAX);
    strncat(name, "/../sip/tests/", PATH_MAX);
    dir = opendir(name);
  }

  if (dir == NULL) {
    fprintf(stderr, "test_nta: cannot find sip torture messages\n"); 
    fprintf(stderr, "test_nta: tried %s\n", name); 
    return 0;
  }

  offset = strlen(name);

  BEGIN();

  TEST_1(ag->ag_default_leg = nta_leg_tcreate(ag->ag_agent, 
					      leg_callback_500,
					      ag,
					      NTATAG_NO_DIALOG(1),
					      TAG_END()));

  host = ag->ag_contact->m_url->url_host;
  if (host_is_ip6_reference(host)) {
    host = strcpy(via, host + 1);
    via[strlen(via) - 1] = '\0';
  }
  port = url_port(ag->ag_contact->m_url);

  memset(hints, 0, sizeof hints);
  hints->ai_socktype = SOCK_DGRAM;
  hints->ai_protocol = IPPROTO_UDP;
  
  TEST(su_getaddrinfo(host, port, hints, &ai), 0); TEST_1(ai);
  s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); TEST_1(s != -1);
  memset(su, 0, sulen = sizeof su); 
  su->su_len = sizeof su; su->su_family = ai->ai_family;
  TEST_1(bind(s, &su->su_sa, sulen) == 0);
  TEST_1(getsockname(s, &su->su_sa, &sulen) == 0);
  sprintf(via, "v: SIP/2.0/UDP is.invalid:%u\r\n", ntohs(su->su_port));
  vlen = strlen(via);

  for (d = readdir(dir); d; d = readdir(dir)) {
    size_t len = strlen(d->d_name);
    FILE *f;
    int blen, n;
    void *buffer; char *r;

    if (len < strlen(".txt"))
      continue;
    if (strcmp(d->d_name + len - strlen(".txt"), ".txt"))
      continue;
    strncpy(name + offset, d->d_name, PATH_MAX - offset);
    TEST_1(f = fopen(name, "rb"));
    TEST_1((blen = readfile(f, &buffer)) > 0);
    r = buffer;

    if (strncmp(r, "JUNK ", 5) == 0) {
      TEST(sendto(s, r, blen, 0, ai->ai_addr, ai->ai_addrlen), blen);
    }
    else if (strncmp(r, "INVITE ", 7) != 0) {
      su_iovec_t vec[3];
      n = strcspn(r, "\r\n"); n += strspn(r + n, "\r\n");
      vec[0].siv_base = r, vec[0].siv_len = n;
      vec[1].siv_base = via, vec[1].siv_len = vlen;
      vec[2].siv_base = r + n, vec[2].siv_len = blen - n;
      TEST(su_vsend(s, vec, 3, 0, (void *)ai->ai_addr, ai->ai_addrlen),
	   blen + vlen);
    }
    free(buffer);
    su_root_step(ag->ag_root, 1);
  }

  su_close(s);

  for (i = 0; i < 20; i++)
    su_root_step(ag->ag_root, 1);

  nta_leg_destroy(ag->ag_default_leg), ag->ag_default_leg = NULL;

  END();
#else
  return 0;<commit_msg>test_nta.c: fixed a leak<commit_after>/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@internal
 * @CFILE test_nta.c
 *
 * Test functions for NTA.
 *
 * @author <NAME> <<EMAIL>>
 *
 * @date Created: Tue Aug 21 15:18:26 2001 ppessi
 */

#include "config.h"

typedef struct agent_t agent_t;

#define SU_ROOT_MAGIC_T      agent_t

#include <sofia-sip/su_wait.h>

#include <msg_internal.h>

#define NTA_AGENT_MAGIC_T    agent_t
#define NTA_LEG_MAGIC_T      agent_t
#define NTA_OUTGOING_MAGIC_T agent_t
#define NTA_INCOMING_MAGIC_T agent_t
#define NTA_RELIABLE_MAGIC_T agent_t

#include "sofia-sip/nta.h"
#include "nta_internal.h"
#include <sofia-sip/sip_header.h>
#include <sofia-sip/sip_tag.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/tport.h>
#include <sofia-sip/htable.h>
#include <sofia-sip/sresolv.h>
#include <sofia-sip/su_log.h>
#include <sofia-sip/sofia_features.h>
#include <sofia-sip/hostdomain.h>

#include <sofia-sip/string0.h>

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>

SOFIAPUBVAR su_log_t nta_log[];
SOFIAPUBVAR su_log_t tport_log[];

int tstflags = 0;
#define TSTFLAGS tstflags

#include <sofia-sip/tstdef.h>

#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
#define __func__ name
#endif

#define NONE ((void *)-1)

struct sigcomp_compartment;

char const name[] = "test_nta";

struct agent_t {
  su_home_t       ag_home[1];
  int             ag_flags;
  su_root_t      *ag_root;
  msg_mclass_t   *ag_mclass;
  nta_agent_t    *ag_agent;

  url_string_t   *ag_obp;	/**< Outbound proxy. */

  nta_leg_t      *ag_server_leg; /**< Leg for sip:%@% */
  nta_leg_t      *ag_default_leg; /**< Leg for rest */

  unsigned        ag_drop;

  nta_outgoing_t *ag_orq;
  int             ag_status;

  char const     *ag_comp;
  struct sigcomp_compartment *ag_client_compartment;

  /* Server side */
  int             ag_response;	/**< What we answer by default */
  nta_incoming_t *ag_irq;

  struct sigcomp_compartment *ag_server_compartment;

  char const     *ag_m;

  sip_contact_t const *ag_contact;
  sip_from_t     *ag_alice;
  sip_to_t       *ag_bob;

  sip_contact_t  *ag_m_alice;
  sip_contact_t  *ag_m_bob;
  sip_contact_t  *ag_aliases;

  nta_leg_t      *ag_alice_leg;
  nta_leg_t      *ag_bob_leg;

  msg_t          *ag_request;

  nta_leg_t      *ag_expect_leg;
  nta_leg_t      *ag_latest_leg;
  nta_leg_t      *ag_call_leg;
  nta_leg_t      *ag_tag_remote; /**< If this is set, outgoing_callback()
				  *   tags it with the tag from remote.
				  */
  int             ag_tag_status; /**< Which response established dialog */
  msg_param_t     ag_call_tag;	 /**< Tag used to establish dialog */

  nta_reliable_t *ag_reliable;

  sip_via_t      *ag_out_via;	/**< Outgoing via */
  sip_via_t      *ag_in_via;	/**< Incoming via */

  sip_content_type_t *ag_content_type;
  sip_payload_t  *ag_payload;

  msg_t          *ag_probe_msg;

  /* Dummy servers */
  char const     *ag_sink_port;
  su_socket_t     ag_sink_socket, ag_down_socket;
};

static int test_init(agent_t *ag, char const *resolv_conf);
static int test_deinit(agent_t *ag);
static int test_bad_messages(agent_t *ag);
static int test_routing(agent_t *ag);
static int test_tports(agent_t *ag);
static int test_resolv(agent_t *ag, char const *resolv_conf);
static int test_dialog(agent_t *ag);
static int test_call(agent_t *ag);
static int test_prack(agent_t *ag);
static int test_fix_467(agent_t *ag);

static int test_for_ack(agent_t *ag,
			nta_incoming_t *irq, 
			sip_t const *sip);
static int test_for_ack_or_timeout(agent_t *ag,
				   nta_incoming_t *irq, 
				   sip_t const *sip);

int agent_callback(agent_t *ag,
		   nta_agent_t *nta,
		   msg_t *msg,
		   sip_t *sip)
{
  if (tstflags & tst_verbatim) {
    if (sip->sip_request) {
      printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	     name, __func__, sip->sip_request->rq_method_name, 
	     URL_PRINT_ARGS(sip->sip_request->rq_url),
	     sip->sip_request->rq_version);
    }
    else {
      printf("%s: %s: %s %03d %s\n", name, __func__, 
	     sip->sip_status->st_version, 
	     sip->sip_status->st_status, 
	     sip->sip_status->st_phrase);
    }
  }

  msg_destroy(msg);

  return 0;
}

static
void leg_match(agent_t *ag, nta_leg_t *leg, int always, char const *func)
{
  char const *match = "unknown leg";

  if (!always && (tstflags & tst_verbatim) != tst_verbatim)
    return;

  if (leg == ag->ag_default_leg)
    match = "ag_default_leg";
  else if (leg == ag->ag_server_leg) 
    match = "ag_server_leg";
  else if (leg == ag->ag_alice_leg)
    match = "ag_alice_leg";
  else if (leg == ag->ag_bob_leg)
    match = "ag_bob_leg";

  printf("%s: %s: %smatched with %s\n", name, func, 
	 always ? "mis" : "", match);
}

static
void leg_zap(agent_t *ag, nta_leg_t *leg)
{
  if (leg == ag->ag_default_leg)
    ag->ag_default_leg = NULL;
  else if (leg == ag->ag_server_leg) 
    ag->ag_server_leg = NULL;
  else if (leg == ag->ag_alice_leg)
    ag->ag_alice_leg = NULL;
  else if (leg == ag->ag_bob_leg)
    ag->ag_bob_leg = NULL;
  else 
     printf("%s:%u: %s: did not exist\n", 
	    __FILE__, __LINE__, __func__);

  nta_leg_destroy(leg);
}


int leg_callback_200(agent_t *ag,
		     nta_leg_t *leg,
		     nta_incoming_t *irq,
		     sip_t const *sip)
{
  BEGIN();

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  TEST_1(sip->sip_content_length);
  TEST_1(sip->sip_via);
  TEST_1(sip->sip_from && sip->sip_from->a_tag);

  if (ag->ag_in_via == NULL)
    ag->ag_in_via = sip_via_dup(ag->ag_home, sip->sip_via);

  if (ag->ag_request == NULL)
    ag->ag_request = nta_incoming_getrequest(irq);

  ag->ag_latest_leg = leg;

  if (ag->ag_expect_leg && leg != ag->ag_expect_leg) {
    leg_match(ag, leg, 1, __func__);
    return 500;
  }
  leg_match(ag, leg, 0, __func__);

  if (sip->sip_request->rq_method == sip_method_bye) {
    leg_zap(ag, leg);
  }

  return 200;

  END();
}

int leg_callback_500(agent_t *ag,
		     nta_leg_t *leg,
		     nta_incoming_t *irq,
		     sip_t const *sip)
{
  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  return 500;
}

int new_leg_callback_200(agent_t *ag,
			 nta_leg_t *leg,
			 nta_incoming_t *irq,
			 sip_t const *sip)
{
  BEGIN();

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s " URL_PRINT_FORMAT " %s\n",
	   name, __func__, sip->sip_request->rq_method_name, 
	   URL_PRINT_ARGS(sip->sip_request->rq_url),
	   sip->sip_request->rq_version);
  }

  TEST_1(sip->sip_content_length);
  TEST_1(sip->sip_via);
  TEST_1(sip->sip_from && sip->sip_from->a_tag);

  ag->ag_latest_leg = leg;

  if (ag->ag_expect_leg && leg != ag->ag_expect_leg) {
    leg_match(ag, leg, 1, __func__);
    return 500;
  }
  leg_match(ag, leg, 0, __func__);

  ag->ag_bob_leg = nta_leg_tcreate(ag->ag_agent,
				   leg_callback_200,
				   ag,
				   URLTAG_URL(sip->sip_request->rq_url),
				   SIPTAG_CALL_ID(sip->sip_call_id),
				   SIPTAG_FROM(sip->sip_to),
				   SIPTAG_TO(sip->sip_from),
				   TAG_END());
  TEST_1(ag->ag_bob_leg);
  TEST_1(nta_leg_tag(ag->ag_bob_leg, NULL));
  TEST_1(nta_leg_get_tag(ag->ag_bob_leg));
  TEST_1(nta_incoming_tag(irq, nta_leg_get_tag(ag->ag_bob_leg)));

  return 200;

  END();
}


int outgoing_callback(agent_t *ag,
		      nta_outgoing_t *orq,
		      sip_t const *sip)
{
  BEGIN();

  int status = sip->sip_status->st_status;

  if (tstflags & tst_verbatim) {
    printf("%s: %s: %s %03d %s\n", name, __func__, 
	   sip->sip_status->st_version, 
	   sip->sip_status->st_status, 
	   sip->sip_status->st_phrase);
  }

  TEST(orq, ag->ag_orq);

  ag->ag_status = status;

  if (status < 200)
    return 0;

  if (ag->ag_comp) {
    nta_compartment_decref(&ag->ag_client_compartment);
    ag->ag_client_compartment = nta_outgoing_compartment(orq);
  }

  if (ag->ag_out_via == NULL)
    ag->ag_out_via = sip_via_dup(ag->ag_home, sip->sip_via);

  if (ag->ag_tag_remote) {
    TEST_S(nta_leg_rtag(ag->ag_tag_remote, sip->sip_to->a_tag), 
	   sip->sip_to->a_tag);
    ag->ag_tag_remote = NULL;
  }

  TEST_1(sip->sip_to && sip->sip_to->a_tag);

  nta_outgoing_destroy(orq);
  ag->ag_orq = NULL;
  return 0;

  END();
}

static
int test_magic_branch(agent_t *ag, sip_t const *sip) 
{
  BEGIN();
  
  if (sip) {
    TEST_1(sip->sip_via);
    TEST_S(sip->sip_via->v_branch, "MagicalBranch");
  }

  END();
}

static
int magic_callback(agent_t *ag,
		   nta_outgoing_t *orq,
		   sip_t const *sip)
{
  test_magic_branch(ag, sip);
  return outgoing_callback(ag, orq, sip);
}

void 
nta_test_run(agent_t *ag)
{
  for (ag->ag_status = 0; ag->ag_status < 200;) {
    if (tstflags & tst_verbatim) {
      fputs(".", stdout); fflush(stdout);
    }
    su_root_step(ag->ag_root, 500L);
  }
}

#include <sofia-sip/msg_mclass.h>

int test_init(agent_t *ag, char const *resolv_conf)
{
  char const *contact = "sip:*:*;comp=sigcomp";
  su_sockaddr_t su;
  socklen_t sulen, sulen0;
  su_socket_t s; 
  int af;

  BEGIN();

  TEST_1(ag->ag_root = su_root_create(ag));

  TEST_1(ag->ag_mclass = msg_mclass_clone(sip_default_mclass(), 0, 0));

#if SU_HAVE_IN6
  if (str0cmp(getenv("ipv6"), "true") == 0) {
    contact = "sip:[::]:*;comp=sigcomp";
    af = AF_INET6, sulen0 = sizeof (struct sockaddr_in6);
  }
  else {
    af = AF_INET, sulen0 = sizeof (struct sockaddr_in);
    contact = "sip:0.0.0.0:*;comp=sigcomp";
  }
#else
  af = AF_INET, sulen0 = sizeof (struct sockaddr_in);
  contact = "sip:0.0.0.0:*;comp=sigcomp";
#endif

  if (ag->ag_m)
    contact = ag->ag_m;
  else if (getenv("SIPCONTACT"))
    contact = getenv("SIPCONTACT");

  /* Sink server */
  s = socket(af, SOCK_DGRAM, 0); TEST_1(s != INVALID_SOCKET);
  memset(&su, 0, sulen = sulen0);
  su.su_family = af;
  if (getenv("sink")) {
    su.su_port = htons(atoi(getenv("sink")));
  }
  TEST_1(bind(s, &su.su_sa, sulen) < 0 ? (perror("bind"), 0) : 1);
  TEST_1(getsockname(s, &su.su_sa, &sulen) == 0);

  ag->ag_sink_port = su_sprintf(ag->ag_home, "%u", ntohs(su.su_sin.sin_port));
  ag->ag_sink_socket = s;

  /* Down server */
  s = socket(af, SOCK_STREAM, 0); TEST_1(s != INVALID_SOCKET);
  memset(&su, 0, sulen = sulen0);
  su.su_family = af;
  if (getenv("down")) {
    su.su_port = htons(atoi(getenv("down")));
  }
  TEST_1(bind(s, &su.su_sa, sulen) < 0 ? (perror("bind"), 0) : 1);
  ag->ag_down_socket = s;
  
  /* Create agent */
  TEST_1(ag->ag_agent = nta_agent_create(ag->ag_root,
					 (url_string_t *)contact,
					 NULL,
					 NULL,
					 NTATAG_MCLASS(ag->ag_mclass),
					 NTATAG_USE_TIMESTAMP(1),
					 SRESTAG_RESOLV_CONF(resolv_conf),
					 NTATAG_USE_NAPTR(0),
					 NTATAG_USE_SRV(0),
					 NTATAG_PRELOAD(2048),
					 TAG_END()));

  {
    /* Initialize our headers */
    sip_from_t from[1];
    sip_to_t to[1];
    sip_contact_t m[1];

    sip_from_init(from);
    sip_to_init(to);
    sip_contact_init(m);

    TEST_1(ag->ag_contact = nta_agent_contact(ag->ag_agent));

    *m->m_url = *ag->ag_contact->m_url;
    m->m_url->url_user = "bob";
    TEST_1(ag->ag_m_bob = sip_contact_dup(ag->ag_home, m));

    to->a_display = "Bob";
    *to->a_url = *ag->ag_contact->m_url;
    to->a_url->url_user = "bob";
    to->a_url->url_port = NULL;
    TEST_1(ag->ag_bob = sip_to_dup(ag->ag_home, to));

    *m->m_url = *ag->ag_contact->m_url;
    m->m_url->url_user = "alice";
    TEST_1(ag->ag_m_alice = sip_contact_dup(ag->ag_home, m));

    from->a_display = "Alice";
    *from->a_url = *ag->ag_contact->m_url;
    from->a_url->url_user = "alice";
    from->a_url->url_port = NULL;
    TEST_1(ag->ag_alice = sip_from_dup(ag->ag_home, from));
  }
  {
    char const data[] = 
      "v=0\r\n"
      "o=- 425432 423412 IN IP4 127.0.0.1\r\n"
      "s= \r\n"
      "c=IN IP4 127.0.0.1\r\n"
      "m=5004 audio 8 0\r\n";

    ag->ag_content_type = sip_content_type_make(ag->ag_home, "application/sdp");
    ag->ag_payload = sip_payload_make(ag->ag_home, data);
  }

  {
    sip_contact_t *m;

    ag->ag_aliases = 
      sip_contact_make(ag->ag_home, "sip:127.0.0.1, sip:localhost, sip:[::1]");
    TEST_1(ag->ag_aliases);
    TEST_1(ag->ag_aliases->m_next);
    TEST_1(ag->ag_aliases->m_next->m_next);
    TEST(ag->ag_aliases->m_next->m_next->m_next, NULL);

    for (m = ag->ag_aliases; m; m = m->m_next)
      m->m_url->url_port = ag->ag_contact->m_url->url_port;

    TEST_1(m = sip_contact_dup(ag->ag_home, ag->ag_contact));

    m->m_next = ag->ag_aliases;
    ag->ag_aliases = m;

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_REL100(1),
			      NTATAG_UA(1), 
			      NTATAG_USE_NAPTR(1),
			      NTATAG_USE_SRV(1),
			      NTATAG_MAX_FORWARDS(20),
			      TAG_END()),
	 6);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_DEFAULT_PROXY("sip:127.0.0.1"),
			      TAG_END()), 2);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_ALIASES(ag->ag_aliases),
			      NTATAG_DEFAULT_PROXY(NULL),
			      TAG_END()), 2);

    TEST(nta_agent_set_params(ag->ag_agent, 
			      NTATAG_DEFAULT_PROXY("tel:+35878008000"),
			      TAG_END()), -1);

  }
  
  {
    url_t url[1];

    /* Create the server leg */
    *url = *ag->ag_aliases->m_url;
    url->url_user = "%";
    TEST_1(ag->ag_server_leg = nta_leg_tcreate(ag->ag_agent, 
					       leg_callback_200,
					       ag,
					       NTATAG_NO_DIALOG(1),
					       URLTAG_URL(url),
					       TAG_END()));
  }

  END();
}  

int test_reinit(agent_t *ag)
{
  BEGIN();
  /* Create a new default leg */
  nta_leg_destroy(ag->ag_default_leg), ag->ag_default_leg = NULL;
  TEST_1(ag->ag_default_leg = nta_leg_tcreate(ag->ag_agent, 
					     leg_callback_200,
					     ag,
					     NTATAG_NO_DIALOG(1),
					     TAG_END()));
  END();
}

int test_deinit(agent_t *ag)
{
  BEGIN();

  if (ag->ag_request) msg_destroy(ag->ag_request), ag->ag_request = NULL;

  su_free(ag->ag_home, ag->ag_in_via), ag->ag_in_via = NULL;

  nta_leg_destroy(ag->ag_alice_leg);
  nta_leg_destroy(ag->ag_bob_leg);
  nta_leg_destroy(ag->ag_default_leg);
  nta_leg_destroy(ag->ag_server_leg);

  nta_agent_destroy(ag->ag_agent);
  su_root_destroy(ag->ag_root);

  su_free(ag->ag_home, (void *)ag->ag_sink_port), ag->ag_sink_port = NULL;

  free(ag->ag_mclass), ag->ag_mclass = NULL;

  END();
}  

static
int readfile(FILE *f, void **contents)
{
  /* Read in whole (binary!) file */
  char *buffer = NULL;
  long size;
  int len = -1;
  
  /* Read whole file in */
  if (fseek(f, 0, SEEK_END) < 0 ||
      (size = ftell(f)) < 0 ||
      fseek(f, 0, SEEK_SET) < 0 ||
      (long)(len = size) != size) {
    fprintf(stderr, "%s: unable to determine file size (%s)\n", 
	    __func__, strerror(errno));
    return -1;
  }

  if (!(buffer = malloc(len + 2)) ||
      fread(buffer, 1, len, f) != len) {
    fprintf(stderr, "%s: unable to read file (%s)\n", __func__, strerror(errno));
    if (buffer)
      free(buffer);
    return -1;
  }

  buffer[len] = '\0';

  *contents = buffer;

  return len;
}

#if HAVE_DIRENT_H
#include <dirent.h>
#endif 

static int test_bad_messages(agent_t *ag)
{
#if HAVE_DIRENT_H
  DIR *dir;
  struct dirent *d;
  char name[PATH_MAX + 1] = "../sip/tests/";
  size_t offset;
  char const *host, *port;
  su_addrinfo_t *ai,  hints[1];
  su_socket_t s;
  su_sockaddr_t su[1];
  socklen_t sulen;
  char via[64];
  size_t vlen;
  int i;

  dir = opendir(name);
  if (dir == NULL && getenv("srcdir")) {
    strncpy(name, getenv("srcdir"), PATH_MAX);
    strncat(name, "/../sip/tests/", PATH_MAX);
    dir = opendir(name);
  }

  if (dir == NULL) {
    fprintf(stderr, "test_nta: cannot find sip torture messages\n"); 
    fprintf(stderr, "test_nta: tried %s\n", name); 
    return 0;
  }

  offset = strlen(name);

  BEGIN();

  TEST_1(ag->ag_default_leg = nta_leg_tcreate(ag->ag_agent, 
					      leg_callback_500,
					      ag,
					      NTATAG_NO_DIALOG(1),
					      TAG_END()));

  host = ag->ag_contact->m_url->url_host;
  if (host_is_ip6_reference(host)) {
    host = strcpy(via, host + 1);
    via[strlen(via) - 1] = '\0';
  }
  port = url_port(ag->ag_contact->m_url);

  memset(hints, 0, sizeof hints);
  hints->ai_socktype = SOCK_DGRAM;
  hints->ai_protocol = IPPROTO_UDP;
  
  TEST(su_getaddrinfo(host, port, hints, &ai), 0); TEST_1(ai);
  s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); TEST_1(s != -1);
  memset(su, 0, sulen = sizeof su); 
  su->su_len = sizeof su; su->su_family = ai->ai_family;
  TEST_1(bind(s, &su->su_sa, sulen) == 0);
  TEST_1(getsockname(s, &su->su_sa, &sulen) == 0);
  sprintf(via, "v: SIP/2.0/UDP is.invalid:%u\r\n", ntohs(su->su_port));
  vlen = strlen(via);

  for (d = readdir(dir); d; d = readdir(dir)) {
    size_t len = strlen(d->d_name);
    FILE *f;
    int blen, n;
    void *buffer; char *r;

    if (len < strlen(".txt"))
      continue;
    if (strcmp(d->d_name + len - strlen(".txt"), ".txt"))
      continue;
    strncpy(name + offset, d->d_name, PATH_MAX - offset);
    TEST_1(f = fopen(name, "rb"));
    TEST_1((blen = readfile(f, &buffer)) > 0);
    r = buffer;

    if (strncmp(r, "JUNK ", 5) == 0) {
      TEST(sendto(s, r, blen, 0, ai->ai_addr, ai->ai_addrlen), blen);
    }
    else if (strncmp(r, "INVITE ", 7) != 0) {
      su_iovec_t vec[3];
      n = strcspn(r, "\r\n"); n += strspn(r + n, "\r\n");
      vec[0].siv_base = r, vec[0].siv_len = n;
      vec[1].siv_base = via, vec[1].siv_len = vlen;
      vec[2].siv_base = r + n, vec[2].siv_len = blen - n;
      TEST(su_vsend(s, vec, 3, 0, (void *)ai->ai_addr, ai->ai_addrlen),
	   blen + vlen);
    }
    free(buffer);
    su_root_step(ag->ag_root, 1);
  }

  su_close(s);

  for (i = 0; i < 20; i++)
    su_root_step(ag->ag_root, 1);

  nta_leg_destroy(ag->ag_default_leg), ag->ag_default_leg = NULL;

  closedir(dir);

  END();
#else
  return 0;
{'fraction_non_alphanumeric': 0.1543029055498377, 'fraction_numerical': 0.015728498667300028, 'mean_word_length': 3.2420239561177655, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 559, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 12, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3842685', 'n_tokens_mistral': 17194, 'n_tokens_neox': 16053, 'n_words': 3005}