test_nua.c 196 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

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

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
  int threading, proxy_tests, expensive;
146
  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

162 163
    char const *instance;

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

Pekka Pessi's avatar
Pekka Pessi committed
172 173 174 175 176 177 178
    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
179 180 181 182 183 184 185 186 187
    /* 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;

188
  } a, b, c;
Pekka Pessi's avatar
Pekka Pessi committed
189 190

  struct proxy *p;
Pekka Pessi's avatar
Pekka Pessi committed
191
  struct nat *nat;
Pekka Pessi's avatar
Pekka Pessi committed
192 193
};

Pekka Pessi's avatar
Pekka Pessi committed
194 195 196 197 198
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
199
				struct eventlist *);
200

201 202 203 204 205 206 207 208 209 210 211
#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)
212
{
Pekka Pessi's avatar
Pekka Pessi committed
213 214
  save_event_in_list(ctx, event, ep, ep->call);
  return event >= nua_r_set_params && status >= 200;
215 216
}

217 218
/** Save events.
 *
Pekka Pessi's avatar
Pekka Pessi committed
219 220
 * Terminate when a event is saved.
 */
221
int save_until_received(CONDITION_PARAMS)
222
{
223 224 225 226 227 228 229 230 231 232 233 234
  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;
235 236
}

Pekka Pessi's avatar
Pekka Pessi committed
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
/* 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;
}

272 273 274 275 276 277 278 279 280 281 282 283
/* Return true if offer/answer is sent/recv */
int is_offer_answer_done(tagi_t const *tags)
{
  tagi_t const *ti;

  return 
    ((ti = tl_find(tags, nutag_answer_recv)) && ti->t_value) ||
    ((ti = tl_find(tags, nutag_offer_sent)) && ti->t_value) ||
    ((ti = tl_find(tags, nutag_offer_recv)) && ti->t_value) ||
    ((ti = tl_find(tags, nutag_answer_sent)) && ti->t_value);
}

Pekka Pessi's avatar
Pekka Pessi committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297
/* 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
298 299 300 301 302 303 304 305 306
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
307 308 309
{
  if (event == nua_i_state) {
    fprintf(stderr, "%s.nua(%p): event %s %s\n",
310
	    ep->name, nh, nua_event_name(event),
Pekka Pessi's avatar
Pekka Pessi committed
311 312
	    nua_callstate_name(callstate(tags)));
  }
Pekka Pessi's avatar
Pekka Pessi committed
313
  else if ((int)event >= nua_r_set_params) {
Pekka Pessi's avatar
Pekka Pessi committed
314 315 316
    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
317 318 319 320
  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
321 322
  else if (status > 0) {
    fprintf(stderr, "%s.nua(%p): call %s() with status %u %s\n",
323
	    ep->name, nh, operation, status, phrase);
Pekka Pessi's avatar
Pekka Pessi committed
324 325
  }
  else {
Pekka Pessi's avatar
Pekka Pessi committed
326 327 328 329 330
    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
331
	      ep->name, nh, operation, subject);
Pekka Pessi's avatar
Pekka Pessi committed
332 333 334
    }
    else
      fprintf(stderr, "%s.nua(%p): call %s()\n",
Pekka Pessi's avatar
Pekka Pessi committed
335
	      ep->name, nh, operation);
Pekka Pessi's avatar
Pekka Pessi committed
336 337 338 339 340
  }

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

342 343 344 345
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
346
		 nua_handle_t *nh, struct call *call,
347 348
		 sip_t const *sip,
		 tagi_t tags[])
349
{
Pekka Pessi's avatar
Pekka Pessi committed
350
  if (ep->printer)
Pekka Pessi's avatar
Pekka Pessi committed
351 352
    ep->printer(event, "", status, phrase, nua, ctx, ep, nh, call, sip, tags);

353
  if (call == NULL && nh) {
Pekka Pessi's avatar
Pekka Pessi committed
354 355 356 357 358 359 360 361 362 363 364 365
    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
366 367 368 369 370

  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)))
371
    ep->running = 0;
372

Pekka Pessi's avatar
Pekka Pessi committed
373
  ep->last_event = event;
374 375 376

  if (call == NULL && nh)
    nua_handle_destroy(nh);
377 378
}

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

389 390 391
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
392
		nua_handle_t *nh, struct call *call,
393 394 395 396 397
		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
398

399 400 401
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
402
		nua_handle_t *nh, struct call *call,
403 404 405 406
		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
407 408
}

409 410 411 412
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
413
{
414
  struct endpoint *a = &ctx->a, *b = &ctx->b, *c = &ctx->c;
415 416 417 418 419

  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
420
  a->flags.n = 0;
421 422 423 424 425

  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
426
  b->flags.n = 0;
Pekka Pessi's avatar
Pekka Pessi committed
427

428 429 430 431
  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
432
  c->flags.n = 0;
433 434

  for (; a->running || b->running || c->running;) {
Pekka Pessi's avatar
Pekka Pessi committed
435 436
    su_root_step(ctx->root, 1000);
  }
437
}
Pekka Pessi's avatar
Pekka Pessi committed
438

439 440 441 442 443 444 445
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);
}

446 447 448 449
int run_a_until(struct context *ctx,
		nua_event_t a_event,
		condition_function *a_condition)
{
450
  run_abc_until(ctx, a_event, a_condition, -1, NULL, -1, NULL);
451 452 453 454 455 456 457
  return ctx->a.last_event;
}

int run_b_until(struct context *ctx,
		nua_event_t b_event,
		condition_function *b_condition)
{
458
  run_abc_until(ctx, -1, NULL, b_event, b_condition, -1, NULL);
459
  return ctx->b.last_event;
Pekka Pessi's avatar
Pekka Pessi committed
460 461
}

Pekka Pessi's avatar
Pekka Pessi committed
462 463 464 465 466 467 468 469
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;
}

470 471
#define OPERATION(X, x)	   \
int X(struct endpoint *ep, \
Pekka Pessi's avatar
Pekka Pessi committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
      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

489 490 491 492 493 494 495
OPERATION(INVITE, invite);
OPERATION(ACK, ack);
OPERATION(BYE, bye);
OPERATION(CANCEL, cancel);
OPERATION(AUTHENTICATE, authenticate);
OPERATION(UPDATE, update);
OPERATION(INFO, info);
496
OPERATION(PRACK, prack);
497 498 499 500 501 502 503 504 505 506 507 508 509
OPERATION(REFER, refer);
OPERATION(MESSAGE, message);
OPERATION(OPTIONS, options);
OPERATION(PUBLISH, publish);
OPERATION(UNPUBLISH, unpublish);
OPERATION(REGISTER, register);
OPERATION(UNREGISTER, unregister);
OPERATION(SUBSCRIBE, subscribe);
OPERATION(UNSUBSCRIBE, unsubscribe);
OPERATION(NOTIFY, notify);
OPERATION(NOTIFIER, notifier);
OPERATION(TERMINATE, terminate);
OPERATION(AUTHORIZE, authorize);
510

Pekka Pessi's avatar
Pekka Pessi committed
511
/* Respond via endpoint and handle */
512
int RESPOND(struct endpoint *ep,
Pekka Pessi's avatar
Pekka Pessi committed
513 514
	    struct call *call,
	    nua_handle_t *nh,
Pekka Pessi's avatar
Pekka Pessi committed
515 516 517 518 519 520 521
	    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
522 523

  if (ep->printer)
Pekka Pessi's avatar
Pekka Pessi committed
524 525
    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
526

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

  return 0;
}

533

Pekka Pessi's avatar
Pekka Pessi committed
534 535 536 537 538
/* 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)
539
{
Pekka Pessi's avatar
Pekka Pessi committed
540 541
  if (call)
    return call;
542

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

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

Pekka Pessi's avatar
Pekka Pessi committed
550

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

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

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

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

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

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

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

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

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

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

Pekka Pessi's avatar
Pekka Pessi committed
596
  while ((e = list->head)) {
Pekka Pessi's avatar
Pekka Pessi committed
597 598 599 600 601
    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
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 639

  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
640 641
}

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

Pekka Pessi's avatar
Pekka Pessi committed
644 645 646 647 648
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
649
int test_nua_api_errors(struct context *ctx)
Pekka Pessi's avatar
Pekka Pessi committed
650 651 652
{
  BEGIN();

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

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

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

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

Pekka Pessi's avatar
Pekka Pessi committed
662 663 664 665 666
  /* 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
667 668 669 670 671
  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
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695

  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()));
696
  TEST_VOID(nua_prack(NULL, TAG_END()));
Pekka Pessi's avatar
Pekka Pessi committed
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
  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
714

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

Pekka Pessi's avatar
Pekka Pessi committed
720 721 722 723
  {
    nua_saved_event_t event[1];

    memset(event, 0, sizeof event);
724

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

Pekka Pessi's avatar
Pekka Pessi committed
730
  su_log_set_level(nua_log, level);
Pekka Pessi's avatar
Pekka Pessi committed
731

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

Pekka Pessi's avatar
Pekka Pessi committed
735 736 737
  END();
}

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

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
756
		TAG_SKIP(2),
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
		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
776
int test_nua_params(struct context *ctx)
Pekka Pessi's avatar
Pekka Pessi committed
777 778
{
  BEGIN();
779 780 781 782 783 784 785 786

  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));

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

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

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

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

800 801
  TEST_1(ctx->a.nua);

802 803 804 805 806 807
  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);

808
  from = sip_from_make(tmphome, Alice);
809 810 811

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

812
  nua_set_hparams(nh, NUTAG_INVITE_TIMER(90), TAG_END());
813
  run_a_until(ctx, nua_r_set_params, until_final_response);
814

815 816 817 818 819 820 821 822 823 824 825 826 827 828
  /* 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());

829
  run_a_until(ctx, nua_r_set_params, until_final_response);
830

831 832 833
  /* Modify everything from their default value */
  nua_set_params(ctx->a.nua,
		 SIPTAG_FROM(from),
834
		 NUTAG_RETRY_COUNT(9),
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
		 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),
857
		 NUTAG_SUBSTATE(nua_substate_pending),
858

859 860 861 862 863 864
		 NUTAG_KEEPALIVE(66),
		 NUTAG_KEEPALIVE_STREAM(33),

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

865 866 867
		 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")),
868

869
		 SIPTAG_ORGANIZATION(sip_organization_make(tmphome, "Pussy Galore's Flying Circus")),
870 871

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

874 875
		 TAG_END());

876
  run_a_until(ctx, nua_r_set_params, until_final_response);
877

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

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

Pekka Pessi's avatar
Pekka Pessi committed
888 889
    unsigned retry_count = -1;
    unsigned max_subscriptions = -1;
890

891 892 893 894 895
    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
896
    unsigned invite_timeout = -1;
897

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

903 904 905
    int message_enable = -1;
    int win_messenger_enable = -1;
    int message_auto_respond = -1;
906

907 908 909 910
    int callee_caps = -1;
    int media_features = -1;
    int service_route_enable = -1;
    int path_enable = -1;
911
    int substate = -1;
912 913

    sip_allow_t const *allow = NONE;
914
    char const *allow_str = "NONE";
915 916 917 918 919 920 921
    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";

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

927 928 929
    url_string_t const *registrar = NONE;

    int n;
930
    struct event *e;
931

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

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

938
    n = tl_gets(e->data->e_tags,
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
	       	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),
965
	       	NUTAG_SUBSTATE_REF(substate),
966 967 968 969 970 971 972 973 974 975 976

	       	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),

977 978 979 980 981 982
		NUTAG_OUTBOUND_REF(outbound),
		NUTAG_INSTANCE_REF(instance),

		NUTAG_KEEPALIVE_REF(keepalive),
		NUTAG_KEEPALIVE_STREAM_REF(keepalive_stream),

983 984 985
	       	NUTAG_REGISTRAR_REF(registrar),

		TAG_END());
986
    TEST(n, 34);
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 1012 1013

    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);
1014
    TEST(substate, nua_substate_pending);
1015 1016 1017 1018 1019 1020 1021

    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");
1022
    TEST_S(sip_header_as_string(tmphome, (void *)organization),
1023 1024 1025
	   "Pussy Galore's Flying Circus");
    TEST_S(organization_str, "Pussy Galore's Flying Circus");

1026 1027 1028 1029 1030 1031
    TEST(keepalive, 66);
    TEST(keepalive_stream, 33);

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

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

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

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

1040 1041 1042 1043
  {
    sip_from_t const *from = NONE;
    char const *from_str = "NONE";

Pekka Pessi's avatar
Pekka Pessi committed
1044 1045
    unsigned retry_count = -1;
    unsigned max_subscriptions = -1;
1046

1047 1048 1049 1050 1051
    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
1052
    unsigned invite_timeout = -1;
1053

Pekka Pessi's avatar
Pekka Pessi committed
1054 1055
    unsigned session_timer = -1;
    unsigned min_se = -1;
1056 1057
    int refresher = -1;
    int update_refresh = -1;
1058

1059 1060 1061
    int message_enable = -1;
    int win_messenger_enable = -1;
    int message_auto_respond = -1;
1062

1063 1064 1065 1066
    int callee_caps = -1;
    int media_features = -1;
    int service_route_enable = -1;
    int path_enable = -1;
1067
    int substate = -1;
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080

    sip_allow_t const *allow = NONE;
    char const   *allow_str = "NONE";
    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";

    url_string_t const *registrar = NONE;

    int n;
1081
    struct event *e;
1082

1083
    nua_get_hparams(nh, TAG_ANY(), TAG_END());
1084
    run_a_until(ctx, nua_r_get_params, save_until_final_response);