nua_dialog.c 14.5 KB
Newer Older
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2006 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 nua_dialog.c
 * @brief Dialog and dialog usage handling
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Wed Mar  8 11:48:49 EET 2006 ppessi
 */

#include "config.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include <assert.h>

#include <sofia-sip/string0.h>
43
#include <sofia-sip/su_uniqueid.h>
44 45

#include <sofia-sip/sip_protos.h>
46 47 48

#define NUA_OWNER_T su_home_t

49 50
#include "nua_dialog.h"

51 52 53 54 55 56 57
#define SU_LOG (nua_log)
#include <sofia-sip/su_debug.h>

#ifndef NONE
#define NONE ((void *)-1)
#endif

58 59 60
/* ======================================================================== */
/* Dialog handling */

61
static void nua_dialog_usage_remove_at(nua_owner_t*, nua_dialog_state_t*, 
62
				       nua_dialog_usage_t**);
63
static void nua_dialog_log_usage(nua_owner_t *, nua_dialog_state_t *);
64

65 66
/**@internal
 * UAS tag and route.
67 68 69 70
 *
 * Update dialog tags and route on the UAS side.
 *
 * @param own  dialog owner
71
 * @param ds   dialog state
72 73 74 75 76 77 78
 * @param sip  SIP message containing response used to update dialog
 * @param rtag if true, set remote tag within the leg
 */
void nua_dialog_uas_route(nua_owner_t *own, 
			  nua_dialog_state_t *ds,
			  sip_t const *sip, 
			  int rtag)
79 80 81 82
{
  int established = nua_dialog_is_established(ds);

  if (!established && sip->sip_from->a_tag)
83
    ds->ds_remote_tag = su_strdup(own, sip->sip_from->a_tag);
84 85 86 87 88 89 90 91 92 93 94

  if (ds->ds_leg == NULL)
    return;

  nta_leg_server_route(ds->ds_leg, sip->sip_record_route, sip->sip_contact);
  ds->ds_route = ds->ds_route || sip->sip_record_route || sip->sip_contact;

  if (rtag && !established && sip->sip_from->a_tag)
    nta_leg_rtag(ds->ds_leg, sip->sip_from->a_tag);
}

95 96
/**@internal
 * UAC tag and route.
97 98 99
 *
 * Update dialog tags and route on the UAC side.
 *
100
 * @param own  dialog owner
101
 * @param ds   dialog state
102 103 104
 * @param sip  SIP message containing response used to update dialog
 * @param rtag if true, set remote tag within the leg
 */
105 106 107 108
void nua_dialog_uac_route(nua_owner_t *own, 
			  nua_dialog_state_t *ds,
			  sip_t const *sip,
			  int rtag)
109 110 111 112
{
  int established = nua_dialog_is_established(ds);

  if (!established && sip->sip_to->a_tag)
113
    ds->ds_remote_tag = su_strdup(own, sip->sip_to->a_tag);
114 115 116 117 118 119 120 121 122 123 124

  if (ds->ds_leg == NULL)
    return;

  nta_leg_client_route(ds->ds_leg, sip->sip_record_route, sip->sip_contact);
  ds->ds_route = ds->ds_route || sip->sip_record_route || sip->sip_contact;

  if (rtag && !established && sip->sip_to->a_tag)
    nta_leg_rtag(ds->ds_leg, sip->sip_to->a_tag);
}

125
/**@internal Store information from remote endpoint. */
126 127 128
void nua_dialog_store_peer_info(nua_owner_t *own, 
				nua_dialog_state_t *ds,
				sip_t const *sip)
129
{
130
  nua_remote_t *nr = ds->ds_remote_ua;
131
  nua_dialog_usage_t *du;
132
  nua_remote_t old[1];
133 134 135

  *old = *nr;

136 137 138 139 140 141 142 143 144 145 146 147 148 149
  if (sip && sip->sip_status &&
      sip->sip_status->st_status >= 300 &&
      sip->sip_status->st_status <= 399)
    sip = NULL;			/* Redirected */

  if (sip == NULL) {
    nr->nr_allow = NULL, su_free(own, old->nr_allow);
    nr->nr_accept = NULL, su_free(own, old->nr_accept);
    nr->nr_require = NULL, su_free(own, old->nr_require);
    nr->nr_supported = NULL, su_free(own, old->nr_supported);
    nr->nr_user_agent = NULL, su_free(own, old->nr_user_agent);
    return;
  }

150
  if (sip->sip_allow) {
151 152
    nr->nr_allow = sip_allow_dup(own, sip->sip_allow);
    su_free(own, old->nr_allow);
153 154 155
  }

  if (sip->sip_accept) {
156 157
    nr->nr_accept = sip_accept_dup(own, sip->sip_accept);
    su_free(own, old->nr_accept);
158 159 160
  }

  if (sip->sip_require) {
161 162
    nr->nr_require = sip_require_dup(own, sip->sip_require);
    su_free(own, old->nr_require);
163 164 165
  }

  if (sip->sip_supported) {
166 167
    nr->nr_supported = sip_supported_dup(own, sip->sip_supported);
    su_free(own, old->nr_supported);
168 169 170
  }

  if (sip->sip_user_agent) {
171 172 173 174 175 176
    nr->nr_user_agent = sip_user_agent_dup(own, sip->sip_user_agent);
    su_free(own, old->nr_user_agent);
  }
  else if (sip->sip_server) {
    nr->nr_user_agent = sip_user_agent_dup(own, sip->sip_server);
    su_free(own, old->nr_user_agent);
177
  }
178 179 180 181 182

  for (du = ds->ds_usage; du; du = du->du_next) {
    if (du->du_class->usage_peer_info)
      du->du_class->usage_peer_info(du, ds, sip);
  }
183 184
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
/** Remove dialog (if there is no other usages). */
int nua_dialog_remove(nua_owner_t *own,
		      nua_dialog_state_t *ds,
		      nua_dialog_usage_t *usage)
{
  if (ds->ds_usage == usage && (usage == NULL || usage->du_next == NULL)) {
    nua_dialog_store_peer_info(own, ds, NULL); /* zap peer info */
    nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL;
    su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL;
    ds->ds_route = 0;
  }
  return 0;
}


200
/** @internal Get dialog usage slot. */
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
nua_dialog_usage_t **
nua_dialog_usage_at(nua_dialog_state_t const *ds, 
		    nua_usage_class const *kind,
		    sip_event_t const *event)
{
  static nua_dialog_usage_t *none = NULL;

  if (ds) {
    nua_dialog_usage_t *du, * const * prev;
    sip_event_t const *o;

    for (prev = &ds->ds_usage; (du = *prev); prev = &du->du_next) {
      if (du->du_class != kind)
	continue;

      if (event == NONE)
	return (nua_dialog_usage_t **)prev;

      o = du->du_event;

      if (!event && !o)
	return (nua_dialog_usage_t **)prev;

      if (event != o) {
	if (event == NULL || o == NULL)
	  continue;
	if (strcmp(event->o_type, o->o_type))
	  continue;
229 230 231 232
	if (str0casecmp(event->o_id, o->o_id)) {
	  if (event->o_id || strcmp(event->o_type, "refer"))
	    continue;
	}
233 234 235 236 237 238 239 240 241
      }

      return (nua_dialog_usage_t **)prev;
    }
  }

  return &none;
}

242
/** @internal Get a dialog usage */
243 244 245 246 247 248 249
nua_dialog_usage_t *nua_dialog_usage_get(nua_dialog_state_t const *ds, 
					 nua_usage_class const *kind,
					 sip_event_t const *event)
{
  return *nua_dialog_usage_at(ds, kind, event);
}

250
/** @internal Get dialog usage name */
251 252 253 254 255 256 257
char const *nua_dialog_usage_name(nua_dialog_usage_t const *du)
{
  if (du == NULL)
    return "<NULL>";
  return du->du_class->usage_name(du);
} 

258
/** @internal Add dialog usage */
259
nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own, 
260 261 262 263 264 265 266 267 268 269 270 271
					 struct nua_dialog_state *ds, 
					 nua_usage_class const *uclass,
					 sip_event_t const *event)
{
  if (ds) {
    sip_event_t *o;
    nua_dialog_usage_t *du, **prev_du;

    prev_du = nua_dialog_usage_at(ds, uclass, event);
    du = *prev_du;
    if (du) {		/* Already exists */
      SU_DEBUG_5(("nua(%p): adding already existing %s usage%s%s\n",
272
		  (void *)own, nua_dialog_usage_name(du), 
273 274 275 276 277 278 279 280 281 282 283
		  event ? "  with event " : "", event ? event->o_type : ""));
      
      if (prev_du != &ds->ds_usage) {
	/* Move as a first usage in the list */
	*prev_du = du->du_next;
	du->du_next = ds->ds_usage;
	ds->ds_usage = du;
      }
      return du;
    }

284
    o = event ? sip_event_dup(own, event) : NULL;
285 286

    if (o != NULL || event == NULL)
287
      du = su_zalloc(own, sizeof *du + uclass->usage_size);
288 289 290 291 292

    if (du) {
      du->du_class = uclass;
      du->du_event = o;

293 294 295
      if (uclass->usage_add(own, ds, du) < 0) {
	su_free(own, o);
	su_free(own, du);
296 297 298 299
	return NULL;
      }
	
      SU_DEBUG_5(("nua(%p): adding %s usage%s%s\n",
300
		  (void *)own, nua_dialog_usage_name(du), 
301 302
		  o ? " with event " : "", o ? o->o_type :""));

303
      su_home_ref(own);
304 305 306 307 308
      du->du_next = ds->ds_usage, ds->ds_usage = du;

      return du;
    }

309
    su_free(own, o);
310 311 312 313 314
  }

  return NULL;
}

315
/** @internal Remove dialog usage. */
316
void nua_dialog_usage_remove(nua_owner_t *own, 
317 318 319 320 321
			     nua_dialog_state_t *ds,
			     nua_dialog_usage_t *du)
{
  nua_dialog_usage_t **at;

322
  assert(own); assert(ds); assert(du);
323 324 325 326 327 328 329

  for (at = &ds->ds_usage; *at; at = &(*at)->du_next)
    if (du == *at)
      break;

  assert(*at);

330
  nua_dialog_usage_remove_at(own, ds, at);
331 332
}

333
/** @internal Remove dialog usage. 
334 335 336 337
 *
 * Zap dialog state (leg, tag and route) if no usages remain. 
*/
static 
338
void nua_dialog_usage_remove_at(nua_owner_t *own, 
339 340 341 342 343 344
				nua_dialog_state_t *ds,
				nua_dialog_usage_t **at)
{
  if (*at) {
    nua_dialog_usage_t *du = *at;
    sip_event_t const *o = NULL;
345 346 347
    nua_client_request_t *cr, *cr_next;
    nua_server_request_t *sr, *sr_next;

348 349 350 351 352 353 354 355 356
    /* Destroy saved client request */
    if (nua_client_is_bound(du->du_cr)) {
      nua_client_bind(cr = du->du_cr, NULL);
      if (!nua_client_is_queued(cr) &&
	  !nua_client_is_reporting(cr))
	nua_client_request_destroy(cr);
    }

    /* Clean references from queued client requests */
357 358 359 360 361 362 363 364 365 366 367
    for (cr = ds->ds_cr; cr; cr = cr_next) {
      cr_next = cr->cr_next;
      if (cr->cr_usage == du)
	cr->cr_usage = NULL;
    }

    for (sr = ds->ds_sr; sr; sr = sr_next) {
      sr_next = sr->sr_next;
      if (sr->sr_usage == du)
	nua_server_request_destroy(sr);
    }
368 369 370 371 372 373

    *at = du->du_next;

    o = du->du_event;

    SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
374
		(void *)own, nua_dialog_usage_name(du), 
375
		o ? " with event " : "", o ? o->o_type :""));
376 377 378
    du->du_class->usage_remove(own, ds, du);
    su_home_unref(own);
    su_free(own, du);
379 380 381 382 383
  }

  /* Zap dialog if there is no more usages */
  if (ds->ds_usage == NULL) {
    nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL;
384
    su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL;
385 386 387 388 389 390
    ds->ds_route = 0;
    ds->ds_has_events = 0;
    ds->ds_terminated = 0;
    return;
  }
  else if (!ds->ds_terminated) {
391
    nua_dialog_log_usage(own, ds);
392 393 394 395
  }
}

static
396
void nua_dialog_log_usage(nua_owner_t *own, nua_dialog_state_t *ds)
397 398 399
{
  nua_dialog_usage_t *du;

400
  if (SU_LOG->log_level >= 3) {
401
    char buffer[160];
402 403
    size_t l = 0, N = sizeof buffer;
    ssize_t n;
404 405 406 407 408 409 410 411 412 413 414 415
    
    buffer[0] = '\0';

    for (du = ds->ds_usage; du; du = du->du_next) {
      msg_header_t const *h = (void *)du->du_event;

      if (!h)
	continue;

      n = sip_event_e(buffer + l, N - l, h, 0);
      if (n == -1)
	break;
416
      l += (size_t)n;
417 418 419 420 421 422
      if (du->du_next && l + 2 < sizeof(buffer)) {
	strcpy(buffer + l, ", ");
	l += 2;
      }
    }
    
423
    SU_DEBUG_3(("nua(%p): handle with %s%s%s\n", (void *)own,
424 425 426 427 428 429
		ds->ds_has_session ? "session and " : "", 
		ds->ds_has_events ? "events " : "",
		buffer));
  }
}

430 431 432 433 434 435 436 437 438 439 440
/** Deinitialize dialog and its usage. @internal */
void nua_dialog_deinit(nua_owner_t *own,
		       nua_dialog_state_t *ds)
{
  while (ds->ds_usage) {
    nua_dialog_usage_remove_at(own, ds, &ds->ds_usage);
  }
}


/** @internal Dialog has been terminated. Remove all usages. */
441 442 443 444
void nua_dialog_remove_usages(nua_owner_t *own,
			      struct nua_dialog_state *ds,
			      int status,
			      char const *phrase)
445 446 447 448 449 450 451 452 453 454 455 456
{
  ds->ds_terminated = 1;

  while (ds->ds_usage) {
#if 0
    int call = 0;

    if (ds->ds_usage->du_kind == nua_session_usage)
      call = 1;			/* Delay sending the event */
    else
      /* XXX */;
#endif
457
    nua_dialog_usage_remove_at(own, ds, &ds->ds_usage);
458
  }
459 460

  nua_dialog_remove(own, ds, NULL);
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
/**@internal
 * Set expiration time. 
 */
void nua_dialog_usage_set_expires(nua_dialog_usage_t *du,
				  unsigned delta)
{
  if (delta) {
    sip_time_t now = sip_now(), expires = now + delta;
    if (expires < now)
      expires = SIP_TIME_MAX;
    du->du_expires = expires;
    nua_dialog_usage_set_refresh(du, delta);
  }
  else
    du->du_expires = 0, du->du_refresh = 0;
}
479

480 481
/**@internal
 * Set refresh value suitably. 
482
 *
483 484 485
 * The refresh time is set either around half of the @a delta interval or,
 * if @a delta is less than 5 minutes but longer than 90 seconds, 30..60
 * seconds before end of interval.
486 487 488 489 490 491
 *
 * If @a delta is 0, the refresh time is set at the end of the world
 * (maximum time, for 32-bit systems sometimes during 2036).
 */
void nua_dialog_usage_set_refresh(nua_dialog_usage_t *du, unsigned delta)
{
492
  if (delta == 0)
493
    du->du_refresh = 0;
494
  else if (delta > 90 && delta < 5 * 60)
495
    /* refresh 30..60 seconds before deadline */
496
    nua_dialog_usage_refresh_range(du, delta - 60, delta - 30);
497 498 499 500 501 502 503 504
  else {
    /* By default, refresh around half time before deadline */
    unsigned min = (delta + 2) / 4;
    unsigned max = (delta + 2) / 4 + (delta + 1) / 2;
    if (min == 0)
      min = 1;
    nua_dialog_usage_refresh_range(du, min, max);
  }
505
}
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
/**@internal Set refresh in range min..max seconds in the future. */
void nua_dialog_usage_refresh_range(nua_dialog_usage_t *du, 
				    unsigned min, unsigned max)
{
  sip_time_t now = sip_now(), target;
  unsigned delta;

  if (max < min)
    max = min;

  if (min != max)
    delta = su_randint(min, max);
  else
    delta = min;

  if (now + delta >= now)
    target = now + delta;
524 525 526
  else
    target = SIP_TIME_MAX;

527 528 529
  SU_DEBUG_7(("nua(): refresh %s after %lu seconds (in [%u..%u])\n",
	      nua_dialog_usage_name(du), target - now, min, max));

530 531
  du->du_refresh = target;
}
532

533
/**@internal Do not refresh. */
534
void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
535
{
536 537
  if (du)
    du->du_refresh = 0;
538 539 540
}

/** @internal Refresh usage or shutdown usage if @a now is 0. */
541
void nua_dialog_usage_refresh(nua_owner_t *owner,
542
			      nua_dialog_state_t *ds,
543 544 545 546
			      nua_dialog_usage_t *du, 
			      sip_time_t now)
{
  if (du) {
547 548 549
    du->du_refresh = 0;

    if (now > 0) {
550 551
      assert(du->du_class->usage_refresh);
      du->du_class->usage_refresh(owner, ds, du, now);
552 553 554
    }
    else {
      du->du_shutdown = 1;
555 556
      assert(du->du_class->usage_shutdown);
      du->du_class->usage_shutdown(owner, ds, du);
557
    }
558 559 560
  }
}

561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
/** Terminate all dialog usages gracefully. */
int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
{
  nua_dialog_usage_t *du;

  do {
    for (du = ds->ds_usage; du; du = du->du_next) {
      if (!du->du_shutdown) {
	nua_dialog_usage_shutdown(owner, ds, du);
	break;
      }
    }
  } while (du);

  return 1;
}

578
/** (Gracefully) terminate usage */
579
void nua_dialog_usage_shutdown(nua_owner_t *owner,
580 581 582 583 584 585 586 587 588 589
				nua_dialog_state_t *ds,
				nua_dialog_usage_t *du)
{
  if (du) {
    du->du_refresh = 0;
    du->du_shutdown = 1;
    assert(du->du_class->usage_shutdown);
    du->du_class->usage_shutdown(owner, ds, du);
  }
}