test_nua.c 169 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * 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
 *
 */

/**@CFILE test_nua.c
 * @brief High-level tester for Sofia SIP User Agent Engine
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
29
 * @author Martti Mela <Martti Mela@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
30 31 32 33 34 35 36 37
 *
 * @date Created: Wed Aug 17 12:12:12 EEST 2005 ppessi
 */

#include "config.h"

struct context;
#define NUA_MAGIC_T struct context
Pekka Pessi's avatar
Pekka Pessi committed
38 39 40

struct call;
#define NUA_HMAGIC_T struct call
Pekka Pessi's avatar
Pekka Pessi committed
41

42 43
#include "sofia-sip/nua.h"
#include "sofia-sip/sip_status.h"
Pekka Pessi's avatar
Pekka Pessi committed
44

45 46
#include <sofia-sip/sdp.h>
#include <sofia-sip/sip_header.h>
Pekka Pessi's avatar
Pekka Pessi committed
47

48 49 50
#include <sofia-sip/su_log.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_tag_io.h>
Pekka Pessi's avatar
Pekka Pessi committed
51

Pekka Pessi's avatar
Pekka Pessi committed
52
#include <test_proxy.h>
Pekka Pessi's avatar
Pekka Pessi committed
53
#include <test_nat.h>
54
#include <sofia-sip/auth_module.h>
Pekka Pessi's avatar
Pekka Pessi committed
55

56 57 58 59 60
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include <assert.h>
61
#include <unistd.h>
62 63 64 65 66

#if HAVE_ALARM
#include <signal.h>
#endif

67 68 69 70
#if defined(_WIN32)
#include <fcntl.h>
#endif

Pekka Pessi's avatar
Pekka Pessi committed
71
extern su_log_t nua_log[];
72
extern su_log_t soa_log[];
73
extern su_log_t nea_log[];
74 75
extern su_log_t nta_log[];
extern su_log_t tport_log[];
Pekka Pessi's avatar
Pekka Pessi committed
76 77
extern su_log_t su_log_default[];

78 79 80
extern void *memmem(const void *haystack, size_t haystacklen,
		    const void *needle, size_t needlelen);

Pekka Pessi's avatar
Pekka Pessi committed
81
char const name[] = "test_nua";
82 83

int print_headings = 1;
Pekka Pessi's avatar
Pekka Pessi committed
84 85 86
int tstflags = 0;
#define TSTFLAGS tstflags

87
#include <sofia-sip/tstdef.h>
Pekka Pessi's avatar
Pekka Pessi committed
88 89 90 91 92 93 94 95 96 97

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

#define NONE ((void*)-1)

98 99
#define TEST_E(a, b) TEST_S(nua_event_name(a), nua_event_name(b))

100 101 102
struct endpoint;

typedef
Pekka Pessi's avatar
Pekka Pessi committed
103 104 105
int condition_function(nua_event_t event,
		       int status, char const *phrase,
		       nua_t *nua, struct context *ctx,
106
		       struct endpoint *ep,
Pekka Pessi's avatar
Pekka Pessi committed
107
		       nua_handle_t *nh, struct call *call,
Pekka Pessi's avatar
Pekka Pessi committed
108 109 110
		       sip_t const *sip,
		       tagi_t tags[]);

Pekka Pessi's avatar
Pekka Pessi committed
111 112 113 114 115 116 117 118 119 120
typedef
void printer_function(nua_event_t event,
		      char const *operation,
		      int status, char const *phrase,
		      nua_t *nua, struct context *ctx,
		      struct endpoint *ep,
		      nua_handle_t *nh, struct call *call,
		      sip_t const *sip,
		      tagi_t tags[]);

Pekka Pessi's avatar
Pekka Pessi committed
121 122 123
struct proxy_transaction;
struct registration_entry;

124
enum { event_is_extra, event_is_normal, event_is_special };
Pekka Pessi's avatar
Pekka Pessi committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

struct eventlist {
  nua_event_t kind;
  struct event *head, **tail;
};

struct event
{
  struct event *next, **prev;
  struct call *call;
  nua_saved_event_t saved_event[1];
  nua_event_data_t const *data;
};


140
struct context
Pekka Pessi's avatar
Pekka Pessi committed
141 142 143 144
{
  su_home_t home[1];
  su_root_t *root;

145 146
  int threading, proxy_tests;
  char const *external_proxy;
Pekka Pessi's avatar
Pekka Pessi committed
147

148
  struct endpoint {
Pekka Pessi's avatar
Pekka Pessi committed
149
    char name[4];
Pekka Pessi's avatar
Pekka Pessi committed
150
    struct context *ctx;	/* Backpointer */
151 152 153

    int running;

154 155
    condition_function *next_condition;
    nua_event_t next_event, last_event;
Pekka Pessi's avatar
Pekka Pessi committed
156
    nua_t *nua;
157
    sip_contact_t *contact;
Pekka Pessi's avatar
Pekka Pessi committed
158
    sip_from_t *to;
Pekka Pessi's avatar
Pekka Pessi committed
159

Pekka Pessi's avatar
Pekka Pessi committed
160
    printer_function *printer;
Pekka Pessi's avatar
Pekka Pessi committed
161

Pekka Pessi's avatar
Pekka Pessi committed
162
    /* Per-call stuff */
Pekka Pessi's avatar
Pekka Pessi committed
163 164 165
    struct call {
      struct call *next;
      nua_handle_t *nh;
166
      char const *sdp;
Pekka Pessi's avatar
Pekka Pessi committed
167
      struct eventlist *events;
Pekka Pessi's avatar
Pekka Pessi committed
168
    } call[1], reg[1];
Pekka Pessi's avatar
Pekka Pessi committed
169

Pekka Pessi's avatar
Pekka Pessi committed
170 171 172 173 174 175 176
    int (*is_special)(nua_event_t e);

    /* Normal events are saved here */
    struct eventlist events[1];
    /* Special events are saved here */
    struct eventlist specials[1];

Pekka Pessi's avatar
Pekka Pessi committed
177 178 179 180 181 182 183 184 185
    /* State flags for complex scenarios */
    union {
      struct {
	unsigned bit0:1, bit1:1, bit2:1, bit3:1;
	unsigned bit4:1, bit5:1, bit6:1, bit7:1;
      } b;
      unsigned n;
    } flags;

186
  } a, b, c;
Pekka Pessi's avatar
Pekka Pessi committed
187 188

  struct proxy *p;
Pekka Pessi's avatar
Pekka Pessi committed
189
  struct nat *nat;
Pekka Pessi's avatar
Pekka Pessi committed
190 191
};

Pekka Pessi's avatar
Pekka Pessi committed
192 193 194 195 196
static int save_event_in_list(struct context *,
			      nua_event_t nevent,
			      struct endpoint *,
			      struct call *);
static void free_events_in_list(struct context *,
Pekka Pessi's avatar
Pekka Pessi committed
197
				struct eventlist *);
198

199 200 201 202 203 204 205 206 207 208 209
#define CONDITION_PARAMS			\
  nua_event_t event,				\
  int status, char const *phrase,		\
  nua_t *nua, struct context *ctx,		\
  struct endpoint *ep,				\
  nua_handle_t *nh, struct call *call,		\
  sip_t const *sip,				\
  tagi_t tags[]

int until_final_response(CONDITION_PARAMS){ return status >= 200; }
int save_until_final_response(CONDITION_PARAMS)
210
{
Pekka Pessi's avatar
Pekka Pessi committed
211 212
  save_event_in_list(ctx, event, ep, ep->call);
  return event >= nua_r_set_params && status >= 200;
213 214
}

215 216
/** Save events.
 *
Pekka Pessi's avatar
Pekka Pessi committed
217 218
 * Terminate when a event is saved.
 */
219
int save_until_received(CONDITION_PARAMS)
220
{
221 222 223 224 225 226 227 228 229 230 231 232
  return save_event_in_list(ctx, event, ep, ep->call) == event_is_normal;
}

int save_events(CONDITION_PARAMS)
{
  return save_event_in_list(ctx, event, ep, ep->call) == event_is_normal;
}

/** Save events until nua_i_outbound is received.  */
int save_until_special(CONDITION_PARAMS)
{
  return save_event_in_list(ctx, event, ep, ep->call) == event_is_special;
233 234
}

Pekka Pessi's avatar
Pekka Pessi committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
/* Return call state from event tag list */
int callstate(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, nutag_callstate);
  return ti ? ti->t_value : -1;
}

/* Return true if offer is sent */
int is_offer_sent(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, nutag_offer_sent);
  return ti ? ti->t_value : 0;
}

/* Return true if answer is sent */
int is_answer_sent(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, nutag_answer_sent);
  return ti ? ti->t_value : 0;
}

/* Return true if offer is recv */
int is_offer_recv(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, nutag_offer_recv);
  return ti ? ti->t_value : 0;
}

/* Return true if answer is recv */
int is_answer_recv(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, nutag_answer_recv);
  return ti ? ti->t_value : 0;
}

/* Return audio state from event tag list */
int audio_activity(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, soatag_active_audio);
  return ti ? ti->t_value : -1;
}

/* Return video state from event tag list */
int video_activity(tagi_t const *tags)
{
  tagi_t const *ti = tl_find(tags, soatag_active_video);
  return ti ? ti->t_value : -1;
}

Pekka Pessi's avatar
Pekka Pessi committed
284 285 286 287 288 289 290 291 292
static
void print_event(nua_event_t event,
		 char const *operation,
		 int status, char const *phrase,
		 nua_t *nua, struct context *ctx,
		 struct endpoint *ep,
		 nua_handle_t *nh, struct call *call,
		 sip_t const *sip,
		 tagi_t tags[])
Pekka Pessi's avatar
Pekka Pessi committed
293 294 295
{
  if (event == nua_i_state) {
    fprintf(stderr, "%s.nua(%p): event %s %s\n",
296
	    ep->name, nh, nua_event_name(event),
Pekka Pessi's avatar
Pekka Pessi committed
297 298
	    nua_callstate_name(callstate(tags)));
  }
Pekka Pessi's avatar
Pekka Pessi committed
299
  else if ((int)event >= nua_r_set_params) {
Pekka Pessi's avatar
Pekka Pessi committed
300 301 302
    fprintf(stderr, "%s.nua(%p): event %s status %u %s\n",
	    ep->name, nh, nua_event_name(event), status, phrase);
  }
Pekka Pessi's avatar
Pekka Pessi committed
303 304 305 306
  else if ((int)event >= 0) {
    fprintf(stderr, "%s.nua(%p): event %s %s\n",
	    ep->name, nh, nua_event_name(event), phrase);
  }
Pekka Pessi's avatar
Pekka Pessi committed
307 308
  else if (status > 0) {
    fprintf(stderr, "%s.nua(%p): call %s() with status %u %s\n",
309
	    ep->name, nh, operation, status, phrase);
Pekka Pessi's avatar
Pekka Pessi committed
310 311
  }
  else {
Pekka Pessi's avatar
Pekka Pessi committed
312 313 314 315 316
    tagi_t const *t;
    t = tl_find(tags, siptag_subject_str);
    if (t && t->t_value) {
      char const *subject = (char const *)t->t_value;
      fprintf(stderr, "%s.nua(%p): call %s() \"%s\"\n",
Pekka Pessi's avatar
Pekka Pessi committed
317
	      ep->name, nh, operation, subject);
Pekka Pessi's avatar
Pekka Pessi committed
318 319 320
    }
    else
      fprintf(stderr, "%s.nua(%p): call %s()\n",
Pekka Pessi's avatar
Pekka Pessi committed
321
	      ep->name, nh, operation);
Pekka Pessi's avatar
Pekka Pessi committed
322 323 324 325 326
  }

  if ((tstflags & tst_verbatim) && tags)
    tl_print(stderr, "", tags);
}
327

328 329 330 331
void ep_callback(nua_event_t event,
		 int status, char const *phrase,
		 nua_t *nua, struct context *ctx,
		 struct endpoint *ep,
Pekka Pessi's avatar
Pekka Pessi committed
332
		 nua_handle_t *nh, struct call *call,
333 334
		 sip_t const *sip,
		 tagi_t tags[])
335
{
Pekka Pessi's avatar
Pekka Pessi committed
336
  if (ep->printer)
Pekka Pessi's avatar
Pekka Pessi committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
    ep->printer(event, "", status, phrase, nua, ctx, ep, nh, call, sip, tags);

  if (call == NULL) {
    for (call = ep->call; call; call = call->next) {
      if (!call->nh)
	break;
      if (nh == call->nh)
	break;
    }

    if (call && call->nh == NULL) {
      call->nh = nh;
      nua_handle_bind(nh, call);
    }
  }
Pekka Pessi's avatar
Pekka Pessi committed
352 353 354 355 356

  if ((ep->next_event == -1 || ep->next_event == event) &&
      (ep->next_condition == NULL ||
       ep->next_condition(event, status, phrase,
			  nua, ctx, ep, nh, call, sip, tags)))
357
    ep->running = 0;
358

Pekka Pessi's avatar
Pekka Pessi committed
359
  ep->last_event = event;
360 361
}

362
void a_callback(nua_event_t event,
363 364
		int status, char const *phrase,
		nua_t *nua, struct context *ctx,
Pekka Pessi's avatar
Pekka Pessi committed
365
		nua_handle_t *nh, struct call *call,
366 367
		sip_t const *sip,
		tagi_t tags[])
Pekka Pessi's avatar
Pekka Pessi committed
368
{
369 370
  ep_callback(event, status, phrase, nua, ctx, &ctx->a, nh, call, sip, tags);
}
371

372 373 374
void b_callback(nua_event_t event,
		int status, char const *phrase,
		nua_t *nua, struct context *ctx,
Pekka Pessi's avatar
Pekka Pessi committed
375
		nua_handle_t *nh, struct call *call,
376 377 378 379 380
		sip_t const *sip,
		tagi_t tags[])
{
  ep_callback(event, status, phrase, nua, ctx, &ctx->b, nh, call, sip, tags);
}
Pekka Pessi's avatar
Pekka Pessi committed
381

382 383 384
void c_callback(nua_event_t event,
		int status, char const *phrase,
		nua_t *nua, struct context *ctx,
Pekka Pessi's avatar
Pekka Pessi committed
385
		nua_handle_t *nh, struct call *call,
386 387 388 389
		sip_t const *sip,
		tagi_t tags[])
{
  ep_callback(event, status, phrase, nua, ctx, &ctx->c, nh, call, sip, tags);
Pekka Pessi's avatar
Pekka Pessi committed
390 391
}

392 393 394 395
void run_abc_until(struct context *ctx,
		   nua_event_t a_event, condition_function *a_condition,
		   nua_event_t b_event, condition_function *b_condition,
		   nua_event_t c_event, condition_function *c_condition)
Pekka Pessi's avatar
Pekka Pessi committed
396
{
397
  struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
398 399 400 401 402

  a->next_event = a_event;
  a->next_condition = a_condition;
  a->last_event = -1;
  a->running = a_condition != NULL || a_event != -1;
Pekka Pessi's avatar
Pekka Pessi committed
403
  a->flags.n = 0;
404 405 406 407 408

  b->next_event = b_event;
  b->next_condition = b_condition;
  b->last_event = -1;
  b->running = b_condition != NULL || b_event != -1;
Pekka Pessi's avatar
Pekka Pessi committed
409
  b->flags.n = 0;
Pekka Pessi's avatar
Pekka Pessi committed
410

411 412 413 414
  c->next_event = c_event;
  c->next_condition = c_condition;
  c->last_event = -1;
  c->running = c_condition != NULL || c_event != -1;
Pekka Pessi's avatar
Pekka Pessi committed
415
  c->flags.n = 0;
416 417

  for (; a->running || b->running || c->running;) {
Pekka Pessi's avatar
Pekka Pessi committed
418 419
    su_root_step(ctx->root, 1000);
  }
420
}
Pekka Pessi's avatar
Pekka Pessi committed
421

422 423 424 425 426 427 428
void run_ab_until(struct context *ctx,
		  nua_event_t a_event, condition_function *a_condition,
		  nua_event_t b_event, condition_function *b_condition)
{
  run_abc_until(ctx, a_event, a_condition, b_event, b_condition, -1, NULL);
}

429 430 431 432
int run_a_until(struct context *ctx,
		nua_event_t a_event,
		condition_function *a_condition)
{
433
  run_abc_until(ctx, a_event, a_condition, -1, NULL, -1, NULL);
434 435 436 437 438 439 440
  return ctx->a.last_event;
}

int run_b_until(struct context *ctx,
		nua_event_t b_event,
		condition_function *b_condition)
{
441
  run_abc_until(ctx, -1, NULL, b_event, b_condition, -1, NULL);
442
  return ctx->b.last_event;
Pekka Pessi's avatar
Pekka Pessi committed
443 444
}

Pekka Pessi's avatar
Pekka Pessi committed
445 446 447 448 449 450 451 452
int run_c_until(struct context *ctx,
		nua_event_t event,
		condition_function *condition)
{
  run_abc_until(ctx, -1, NULL, -1, NULL, event, condition);
  return ctx->c.last_event;
}

Pekka Pessi's avatar
Pekka Pessi committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
#define OPERATION(x) \
int x(struct endpoint *ep, \
      struct call *call, nua_handle_t *nh, \
      tag_type_t tag, tag_value_t value, \
      ...) \
{ \
  ta_list ta; \
  ta_start(ta, tag, value); \
\
  if (ep->printer) \
    ep->printer(-1, "nua_" #x, 0, "", ep->nua, ep->ctx, ep, \
		nh, call, NULL, ta_args(ta)); \
\
  nua_##x(nh, ta_tags(ta)); \
\
  ta_end(ta); \
  return 0; \
} extern int dummy

OPERATION(invite);
473
OPERATION(ack);
Pekka Pessi's avatar
Pekka Pessi committed
474 475 476
OPERATION(bye);
OPERATION(cancel);
OPERATION(authenticate);
477
OPERATION(update);
478
OPERATION(info);
Pekka Pessi's avatar
Pekka Pessi committed
479 480 481 482
OPERATION(refer);
OPERATION(message);
OPERATION(options);
OPERATION(publish);
483 484
OPERATION(unpublish);
OPERATION(unregister);
Pekka Pessi's avatar
Pekka Pessi committed
485
OPERATION(subscribe);
Pekka Pessi's avatar
Pekka Pessi committed
486
OPERATION(unsubscribe);
487 488 489
OPERATION(notify);
OPERATION(notifier);
OPERATION(terminate);
490
OPERATION(authorize);
491

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
int do_register(struct endpoint *ep,
		struct call *call, nua_handle_t *nh,
		tag_type_t tag, tag_value_t value,
		...)
{
  ta_list ta;
  ta_start(ta, tag, value);

  if (ep->printer)
    ep->printer(-1, "nua_" "register", 0, "", ep->nua, ep->ctx, ep,
		nh, call, NULL, ta_args(ta));

  nua_register(nh, ta_tags(ta));

  ta_end(ta);
  return 0;
}

Pekka Pessi's avatar
Pekka Pessi committed
510
/* Respond via endpoint and handle */
Pekka Pessi's avatar
Pekka Pessi committed
511 512 513
int respond(struct endpoint *ep,
	    struct call *call,
	    nua_handle_t *nh,
Pekka Pessi's avatar
Pekka Pessi committed
514 515 516 517 518 519 520
	    int status, char const *phrase,
	    tag_type_t tag, tag_value_t value,
	    ...)
{
  ta_list ta;

  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
521 522

  if (ep->printer)
Pekka Pessi's avatar
Pekka Pessi committed
523 524
    ep->printer(-1, "nua_respond", status, phrase, ep->nua, ep->ctx, ep,
		nh, call, NULL, ta_args(ta));
Pekka Pessi's avatar
Pekka Pessi committed
525

Pekka Pessi's avatar
Pekka Pessi committed
526 527 528 529 530 531
  nua_respond(nh, status, phrase, ta_tags(ta));
  ta_end(ta);

  return 0;
}

532

Pekka Pessi's avatar
Pekka Pessi committed
533 534 535 536 537
/* Reject all but currently used handles */
struct call *check_handle(struct endpoint *ep,
			  struct call *call,
			  nua_handle_t *nh,
			  int status, char const *phrase)
538
{
Pekka Pessi's avatar
Pekka Pessi committed
539 540
  if (call)
    return call;
541

Pekka Pessi's avatar
Pekka Pessi committed
542 543
  if (status)
    respond(ep, call, nh, status, phrase, TAG_END());
544

Pekka Pessi's avatar
Pekka Pessi committed
545 546
  nua_handle_destroy(nh);
  return NULL;
547 548
}

Pekka Pessi's avatar
Pekka Pessi committed
549

Pekka Pessi's avatar
Pekka Pessi committed
550
/* Save nua event in call-specific list */
551
static
552
int save_event_in_list(struct context *ctx,
Pekka Pessi's avatar
Pekka Pessi committed
553 554 555 556
		       nua_event_t nevent,
		       struct endpoint *ep,
		       struct call *call)

557
{
Pekka Pessi's avatar
Pekka Pessi committed
558
  struct eventlist *list;
Pekka Pessi's avatar
Pekka Pessi committed
559
  struct event *e;
Pekka Pessi's avatar
Pekka Pessi committed
560
  int action = ep->is_special(nevent);
Pekka Pessi's avatar
Pekka Pessi committed
561

Pekka Pessi's avatar
Pekka Pessi committed
562
  if (action == event_is_extra)
Pekka Pessi's avatar
Pekka Pessi committed
563
    return 0;
Pekka Pessi's avatar
Pekka Pessi committed
564 565 566 567 568 569
  else if (action == event_is_special)
    list = ep->specials;
  else if (call->events)
    list = call->events;
  else
    list = ep->events;
Pekka Pessi's avatar
Pekka Pessi committed
570 571

  e = su_zalloc(ctx->home, sizeof *e);
572 573

  if (!e) { perror("su_zalloc"), abort(); }
574

575 576
  if (!nua_save_event(ep->nua, e->saved_event)) {
    su_free(ctx->home, e);
Pekka Pessi's avatar
Pekka Pessi committed
577
    return -1;
578
  }
Pekka Pessi's avatar
Pekka Pessi committed
579

Pekka Pessi's avatar
Pekka Pessi committed
580 581 582
  *(e->prev = list->tail) = e; list->tail = &e->next;

  e->call = call;
Pekka Pessi's avatar
Pekka Pessi committed
583
  e->data = nua_event_data(e->saved_event);
584

585
  return action;
586
}
Pekka Pessi's avatar
Pekka Pessi committed
587

Pekka Pessi's avatar
Pekka Pessi committed
588
/* Save nua event in endpoint list */
589
static
Pekka Pessi's avatar
Pekka Pessi committed
590
void free_events_in_list(struct context *ctx,
Pekka Pessi's avatar
Pekka Pessi committed
591
			 struct eventlist *list)
Pekka Pessi's avatar
Pekka Pessi committed
592 593 594
{
  struct event *e;

Pekka Pessi's avatar
Pekka Pessi committed
595
  while ((e = list->head)) {
Pekka Pessi's avatar
Pekka Pessi committed
596 597 598 599 600
    if ((*e->prev = e->next))
      e->next->prev = e->prev;
    nua_destroy_event(e->saved_event);
    su_free(ctx->home, e);
  }
Pekka Pessi's avatar
Pekka Pessi committed
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638

  list->tail = &list->head;
}

static
int is_special(nua_event_t e)
{
  if (e == nua_i_active || e == nua_i_terminated)
    return event_is_extra;
  if (e == nua_i_outbound)
    return event_is_special;

  return event_is_normal;
}

static void
eventlist_init(struct eventlist *list)
{
  list->tail = &list->head;
}

static void
call_init(struct call *call)
{
}

static void
endpoint_init(struct context *ctx, struct endpoint *e, char id)
{
  e->name[0] = id;
  e->ctx = ctx;

  e->is_special = is_special;

  call_init(e->call);
  call_init(e->reg);
  eventlist_init(e->events);
  eventlist_init(e->specials);
Pekka Pessi's avatar
Pekka Pessi committed
639 640
}

Pekka Pessi's avatar
Pekka Pessi committed
641 642
void nolog(void *stream, char const *fmt, va_list ap) {}

Pekka Pessi's avatar
Pekka Pessi committed
643 644 645 646 647
int check_set_status(int status, char const *phrase)
{
  return status == 200 && strcmp(phrase, sip_200_OK) == 0;
}

Pekka Pessi's avatar
Pekka Pessi committed
648 649 650 651
int test_api_errors(struct context *ctx)
{
  BEGIN();

Pekka Pessi's avatar
Pekka Pessi committed
652
  /* Invoke every API function with invalid arguments */
Pekka Pessi's avatar
Pekka Pessi committed
653

Pekka Pessi's avatar
Pekka Pessi committed
654 655
  int level;

Pekka Pessi's avatar
Pekka Pessi committed
656 657
  int status; char const *phrase;

658 659 660
  if (print_headings)
    printf("TEST NUA-1.0: test API\n");

Pekka Pessi's avatar
Pekka Pessi committed
661 662 663 664 665
  /* This is a nasty macro. Test it. */
#define SET_STATUS1(x) ((status = x), status), (phrase = ((void)x))
  TEST_1(check_set_status(SET_STATUS1(SIP_200_OK)));
  TEST(status, 200); TEST_S(phrase, sip_200_OK);

Pekka Pessi's avatar
Pekka Pessi committed
666 667 668 669 670
  su_log_init(nua_log);

  level = nua_log->log_level;
  if (!(tstflags & tst_verbatim))
    su_log_set_level(nua_log, 0);
Pekka Pessi's avatar
Pekka Pessi committed
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711

  TEST_1(!nua_create(NULL, NULL, NULL, TAG_END()));
  TEST_VOID(nua_shutdown(NULL));
  TEST_VOID(nua_destroy(NULL));
  TEST_VOID(nua_set_params(NULL, TAG_END()));
  TEST_VOID(nua_get_params(NULL, TAG_END()));
  TEST_1(!nua_default(NULL));
  TEST_1(!nua_handle(NULL, NULL, TAG_END()));
  TEST_VOID(nua_handle_destroy(NULL));
  TEST_VOID(nua_handle_bind(NULL, NULL));
  TEST_1(!nua_handle_has_invite(NULL));
  TEST_1(!nua_handle_has_subscribe(NULL));
  TEST_1(!nua_handle_has_register(NULL));
  TEST_1(!nua_handle_has_active_call(NULL));
  TEST_1(!nua_handle_has_call_on_hold(NULL));
  TEST_1(!nua_handle_has_events(NULL));
  TEST_1(!nua_handle_has_registrations(NULL));
  TEST_1(!nua_handle_remote(NULL));
  TEST_1(!nua_handle_local(NULL));
  TEST_S(nua_event_name(-1), "NUA_UNKNOWN");
  TEST_VOID(nua_register(NULL, TAG_END()));
  TEST_VOID(nua_unregister(NULL, TAG_END()));
  TEST_VOID(nua_invite(NULL, TAG_END()));
  TEST_VOID(nua_ack(NULL, TAG_END()));
  TEST_VOID(nua_options(NULL, TAG_END()));
  TEST_VOID(nua_publish(NULL, TAG_END()));
  TEST_VOID(nua_message(NULL, TAG_END()));
  TEST_VOID(nua_chat(NULL, TAG_END()));
  TEST_VOID(nua_info(NULL, TAG_END()));
  TEST_VOID(nua_subscribe(NULL, TAG_END()));
  TEST_VOID(nua_unsubscribe(NULL, TAG_END()));
  TEST_VOID(nua_notify(NULL, TAG_END()));
  TEST_VOID(nua_notifier(NULL, TAG_END()));
  TEST_VOID(nua_terminate(NULL, TAG_END()));
  TEST_VOID(nua_refer(NULL, TAG_END()));
  TEST_VOID(nua_update(NULL, TAG_END()));
  TEST_VOID(nua_bye(NULL, TAG_END()));
  TEST_VOID(nua_cancel(NULL, TAG_END()));
  TEST_VOID(nua_authenticate(NULL, TAG_END()));
  TEST_VOID(nua_redirect(NULL, TAG_END()));
  TEST_VOID(nua_respond(NULL, 0, "", TAG_END()));
Pekka Pessi's avatar
Pekka Pessi committed
712

Pekka Pessi's avatar
Pekka Pessi committed
713 714
  TEST_1(!nua_handle_home(NULL));
  TEST_1(!nua_save_event(NULL, NULL));
715
  TEST_1(!nua_event_data(NULL));
Pekka Pessi's avatar
Pekka Pessi committed
716
  TEST_VOID(nua_destroy_event(NULL));
717

Pekka Pessi's avatar
Pekka Pessi committed
718 719 720 721
  {
    nua_saved_event_t event[1];

    memset(event, 0, sizeof event);
722

Pekka Pessi's avatar
Pekka Pessi committed
723
    TEST_1(!nua_save_event(NULL, event));
724
    TEST_1(!nua_event_data(event));
Pekka Pessi's avatar
Pekka Pessi committed
725 726 727
    TEST_VOID(nua_destroy_event(event));
  }

Pekka Pessi's avatar
Pekka Pessi committed
728
  su_log_set_level(nua_log, level);
Pekka Pessi's avatar
Pekka Pessi committed
729

730 731 732
  if (print_headings)
    printf("TEST NUA-1.0: PASSED\n");

Pekka Pessi's avatar
Pekka Pessi committed
733 734 735
  END();
}

736
#include <sofia-sip/su_tag_class.h>
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753

int test_tag_filter(void)
{
  BEGIN();

#undef TAG_NAMESPACE
#define TAG_NAMESPACE "test"
  tag_typedef_t tag_a = STRTAG_TYPEDEF(a);
#define TAG_A(s)      tag_a, tag_str_v((s))
  tag_typedef_t tag_b = STRTAG_TYPEDEF(b);
#define TAG_B(s)      tag_b, tag_str_v((s))

  tagi_t filter[2] = {{ NUTAG_ANY() }, { TAG_END() }};

  tagi_t *lst, *result;

  lst = tl_list(TAG_A("X"),
Pekka Pessi's avatar
Pekka Pessi committed
754
		TAG_SKIP(2),
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
		NUTAG_URL((void *)"urn:foo"),
		TAG_B("Y"),
		NUTAG_URL((void *)"urn:bar"),
		TAG_NULL());

  TEST_1(lst);

  result = tl_afilter(NULL, filter, lst);

  TEST_1(result);
  TEST(result[0].t_tag, nutag_url);
  TEST(result[1].t_tag, nutag_url);

  tl_vfree(lst);
  free(result);

  END();
}

Pekka Pessi's avatar
Pekka Pessi committed
774 775 776
int test_params(struct context *ctx)
{
  BEGIN();
777 778 779 780 781 782 783 784

  char const Alice[] = "Alice <sip:a@wonderland.org>";
  sip_from_t const *from;
  su_home_t tmphome[SU_HOME_AUTO_SIZE(16384)];
  nua_handle_t *nh;

  su_home_auto(tmphome, sizeof(tmphome));

785
  if (print_headings)
786
    printf("TEST NUA-1.1: PARAMETERS\n");
787

Pekka Pessi's avatar
Pekka Pessi committed
788
  ctx->root = su_root_create(NULL); TEST_1(ctx->root);
789

Pekka Pessi's avatar
Pekka Pessi committed
790
  /* Disable threading by command line switch? */
Pekka Pessi's avatar
Pekka Pessi committed
791
  su_root_threading(ctx->root, ctx->threading);
Pekka Pessi's avatar
Pekka Pessi committed
792

793 794
  ctx->a.nua = nua_create(ctx->root, a_callback, ctx,
			  SIPTAG_FROM_STR("sip:alice@example.com"),
795
			  NUTAG_URL("sip:0.0.0.0:*;transport=udp"),
796
			  TAG_END());
797

798 799
  TEST_1(ctx->a.nua);

800 801 802 803 804 805
  nh = nua_handle(ctx->a.nua, NULL, TAG_END()); TEST_1(nh);
  nua_handle_unref(nh);

  nh = nua_handle(ctx->a.nua, NULL, TAG_END()); TEST_1(nh);
  nua_handle_destroy(nh);

806
  from = sip_from_make(tmphome, Alice);
807 808 809

  nh = nua_handle(ctx->a.nua, NULL, TAG_END());

810
  nua_set_hparams(nh, NUTAG_INVITE_TIMER(90), TAG_END());
811
  run_a_until(ctx, nua_r_set_params, until_final_response);
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826
  /* Modify all pointer values */
  nua_set_params(ctx->a.nua,
		 SIPTAG_FROM_STR(Alice),

		 SIPTAG_SUPPORTED_STR("test"),
		 SIPTAG_ALLOW_STR("DWIM, OPTIONS, INFO"),
		 SIPTAG_USER_AGENT_STR("test_nua/1.0"),

		 SIPTAG_ORGANIZATION_STR("Te-Ras y.r."),

		 NUTAG_REGISTRAR("sip:openlaboratory.net"),

		 TAG_END());

827
  run_a_until(ctx, nua_r_set_params, until_final_response);
828

829 830 831
  /* Modify everything from their default value */
  nua_set_params(ctx->a.nua,
		 SIPTAG_FROM(from),
832
		 NUTAG_RETRY_COUNT(9),
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
		 NUTAG_MAX_SUBSCRIPTIONS(6),

		 NUTAG_ENABLEINVITE(0),
		 NUTAG_AUTOALERT(1),
		 NUTAG_EARLY_MEDIA(1),
		 NUTAG_AUTOANSWER(1),
		 NUTAG_AUTOACK(0),
		 NUTAG_INVITE_TIMER(60),

		 NUTAG_SESSION_TIMER(600),
		 NUTAG_MIN_SE(35),
		 NUTAG_SESSION_REFRESHER(nua_remote_refresher),
		 NUTAG_UPDATE_REFRESH(1),

		 NUTAG_ENABLEMESSAGE(0),
		 NUTAG_ENABLEMESSENGER(1),
		 /* NUTAG_MESSAGE_AUTOANSWER(0), */

		 NUTAG_CALLEE_CAPS(1),
		 NUTAG_MEDIA_FEATURES(1),
		 NUTAG_SERVICE_ROUTE_ENABLE(0),
		 NUTAG_PATH_ENABLE(0),
855
		 NUTAG_SUBSTATE(nua_substate_pending),
856

857 858 859 860 861 862
		 NUTAG_KEEPALIVE(66),
		 NUTAG_KEEPALIVE_STREAM(33),

		 NUTAG_OUTBOUND("foo"),
		 NUTAG_INSTANCE("urn:uuid:97701ad9-39df-1229-1083-dbc0a85f029c"),

863 864 865
		 SIPTAG_SUPPORTED(sip_supported_make(tmphome, "humppaa,kuole")),
		 SIPTAG_ALLOW(sip_allow_make(tmphome, "OPTIONS, INFO")),
		 SIPTAG_USER_AGENT(sip_user_agent_make(tmphome, "test_nua")),
866

867
		 SIPTAG_ORGANIZATION(sip_organization_make(tmphome, "Pussy Galore's Flying Circus")),
868 869

		 NUTAG_MEDIA_ENABLE(0),
870
		 NUTAG_REGISTRAR(url_hdup(tmphome, (url_t *)"sip:sip.wonderland.org")),
871

872 873
		 TAG_END());

874
  run_a_until(ctx, nua_r_set_params, until_final_response);
875

876 877 878 879
  /* Modify something... */
  nua_set_params(ctx->a.nua,
		 NUTAG_RETRY_COUNT(5),
		 TAG_END());
880
  run_a_until(ctx, nua_r_set_params, until_final_response);
881 882 883 884 885

  {
    sip_from_t const *from = NONE;
    char const *from_str = "NONE";

Pekka Pessi's avatar
Pekka Pessi committed
886 887
    unsigned retry_count = -1;
    unsigned max_subscriptions = -1;
888

889 890 891 892 893
    int invite_enable = -1;
    int auto_alert = -1;
    int early_media = -1;
    int auto_answer = -1;
    int auto_ack = -1;
Pekka Pessi's avatar
Pekka Pessi committed
894
    unsigned invite_timeout = -1;
895

Pekka Pessi's avatar
Pekka Pessi committed
896 897
    unsigned session_timer = -1;
    unsigned min_se = -1;
898 899
    int refresher = -1;
    int update_refresh = -1;
900

901 902 903
    int message_enable = -1;
    int win_messenger_enable = -1;
    int message_auto_respond = -1;
904

905 906 907 908
    int callee_caps = -1;
    int media_features = -1;
    int service_route_enable = -1;
    int path_enable = -1;
909
    int substate = -1;
910 911

    sip_allow_t const *allow = NONE;
912
    char const *allow_str = "NONE";
913 914 915 916 917 918 919
    sip_supported_t const *supported = NONE;
    char const *supported_str = "NONE";
    sip_user_agent_t const *user_agent = NONE;
    char const *user_agent_str = "NONE";
    sip_organization_t const *organization = NONE;
    char const *organization_str = "NONE";

920 921 922 923 924
    char const *outbound = "NONE";
    char const *instance = "NONE";
    
    unsigned keepalive = -1, keepalive_stream = -1;

925 926 927
    url_string_t const *registrar = NONE;

    int n;
928
    struct event *e;
929

Pekka Pessi's avatar
Pekka Pessi committed
930
    nua_get_params(ctx->a.nua, TAG_ANY(), TAG_END());
931
    run_a_until(ctx, nua_r_get_params, save_until_final_response);
932

Pekka Pessi's avatar
Pekka Pessi committed
933
    TEST_1(e = ctx->a.events->head);
934
    TEST_E(e->data->e_event, nua_r_get_params);
935

936
    n = tl_gets(e->data->e_tags,
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
	       	SIPTAG_FROM_REF(from),
	       	SIPTAG_FROM_STR_REF(from_str),

	       	NUTAG_RETRY_COUNT_REF(retry_count),
	       	NUTAG_MAX_SUBSCRIPTIONS_REF(max_subscriptions),

	       	NUTAG_ENABLEINVITE_REF(invite_enable),
	       	NUTAG_AUTOALERT_REF(auto_alert),
	       	NUTAG_EARLY_MEDIA_REF(early_media),
	       	NUTAG_AUTOANSWER_REF(auto_answer),
	       	NUTAG_AUTOACK_REF(auto_ack),
	       	NUTAG_INVITE_TIMER_REF(invite_timeout),

	       	NUTAG_SESSION_TIMER_REF(session_timer),
	       	NUTAG_MIN_SE_REF(min_se),
	       	NUTAG_SESSION_REFRESHER_REF(refresher),
	       	NUTAG_UPDATE_REFRESH_REF(update_refresh),

	       	NUTAG_ENABLEMESSAGE_REF(message_enable),
	       	NUTAG_ENABLEMESSENGER_REF(win_messenger_enable),
	       	/* NUTAG_MESSAGE_AUTOANSWER(message_auto_respond), */

	       	NUTAG_CALLEE_CAPS_REF(callee_caps),
	       	NUTAG_MEDIA_FEATURES_REF(media_features),
	       	NUTAG_SERVICE_ROUTE_ENABLE_REF(service_route_enable),
	       	NUTAG_PATH_ENABLE_REF(path_enable),
963
	       	NUTAG_SUBSTATE_REF(substate),
964 965 966 967 968 969 970 971 972 973 974

	       	SIPTAG_SUPPORTED_REF(supported),
	       	SIPTAG_SUPPORTED_STR_REF(supported_str),
	       	SIPTAG_ALLOW_REF(allow),
	       	SIPTAG_ALLOW_STR_REF(allow_str),
	       	SIPTAG_USER_AGENT_REF(user_agent),
	       	SIPTAG_USER_AGENT_STR_REF(user_agent_str),

	       	SIPTAG_ORGANIZATION_REF(organization),
	       	SIPTAG_ORGANIZATION_STR_REF(organization_str),

975 976 977 978 979 980
		NUTAG_OUTBOUND_REF(outbound),
		NUTAG_INSTANCE_REF(instance),

		NUTAG_KEEPALIVE_REF(keepalive),
		NUTAG_KEEPALIVE_STREAM_REF(keepalive_stream),

981 982 983
	       	NUTAG_REGISTRAR_REF(registrar),

		TAG_END());
984
    TEST(n, 34);
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

    TEST_S(sip_header_as_string(tmphome, (void *)from), Alice);
    TEST_S(from_str, Alice);

    TEST(retry_count, 5);
    TEST(max_subscriptions, 6);

    TEST(invite_enable, 0);
    TEST(auto_alert, 1);
    TEST(early_media, 1);
    TEST(auto_answer, 1);
    TEST(auto_ack, 0);
    TEST(invite_timeout, 60);

    TEST(session_timer, 600);
    TEST(min_se, 35);
    TEST(refresher, nua_remote_refresher);
    TEST(update_refresh, 1);

    TEST(message_enable, 0);
    TEST(win_messenger_enable, 1);
    TEST(message_auto_respond, -1); /* XXX */

    TEST(callee_caps, 1);
    TEST(media_features, 1);
    TEST(service_route_enable, 0);
    TEST(path_enable, 0);
1012
    TEST(substate, nua_substate_pending);
1013 1014 1015 1016 1017 1018 1019

    TEST_S(sip_header_as_string(tmphome, (void *)allow), "OPTIONS, INFO");
    TEST_S(allow_str, "OPTIONS, INFO");
    TEST_S(sip_header_as_string(tmphome, (void *)supported), "humppaa, kuole");
    TEST_S(supported_str, "humppaa, kuole");
    TEST_S(sip_header_as_string(tmphome, (void *)user_agent), "test_nua");
    TEST_S(user_agent_str, "test_nua");
1020
    TEST_S(sip_header_as_string(tmphome, (void *)organization),
1021 1022 1023
	   "Pussy Galore's Flying Circus");
    TEST_S(organization_str, "Pussy Galore's Flying Circus");

1024 1025 1026 1027 1028 1029
    TEST(keepalive, 66);
    TEST(keepalive_stream, 33);

    TEST_S(outbound, "foo");
    TEST_S(instance, "urn:uuid:97701ad9-39df-1229-1083-dbc0a85f029c");

1030
    TEST_S(url_as_string(tmphome, registrar->us_url),
1031
	   "sip:sip.wonderland.org");
1032

Pekka Pessi's avatar
Pekka Pessi committed
1033
    free_events_in_list(ctx, ctx->a.events);
1034
  }
1035

1036 1037
  /* Test that only those tags that have been set per handle are returned by nua_get_hparams() */

1038 1039 1040 1041
  {
    sip_from_t const *from = NONE;
    char const *from_str = "NONE";

Pekka Pessi's avatar
Pekka Pessi committed
1042 1043
    unsigned retry_count = -1;
    unsigned max_subscriptions = -1;
1044

1045 1046 1047 1048 1049
    int invite_enable = -1;
    int auto_alert = -1;
    int early_media = -1;
    int auto_answer = -1;
    int auto_ack = -1;
Pekka Pessi's avatar
Pekka Pessi committed
1050
    unsigned invite_timeout = -1;