nua_dialog.c 14.7 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
#include <sofia-sip/sip_status.h>
47 48 49

#define NUA_OWNER_T su_home_t

50 51
#include "nua_dialog.h"

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

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

59 60 61
/* ======================================================================== */
/* Dialog handling */

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

66 67
/**@internal
 * UAS tag and route.
68 69 70 71
 *
 * Update dialog tags and route on the UAS side.
 *
 * @param own  dialog owner
72
 * @param ds   dialog state
73 74 75 76 77 78 79
 * @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)
80 81 82 83
{
  int established = nua_dialog_is_established(ds);

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

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

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

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

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

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

  *old = *nr;

137 138 139 140 141 142 143 144 145 146 147 148 149 150
  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;
  }

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

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

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

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

  if (sip->sip_user_agent) {
172 173 174 175 176 177
    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);
178
  }
179 180 181 182 183

  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);
  }
184 185
}

186 187 188 189 190 191 192
/** 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 */
193
    msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL;
194 195 196 197 198 199 200 201
    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;
}


202
/** @internal Get dialog usage slot. */
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 229 230
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;
231 232 233 234
	if (str0casecmp(event->o_id, o->o_id)) {
	  if (event->o_id || strcmp(event->o_type, "refer"))
	    continue;
	}
235 236 237 238 239 240 241 242 243
      }

      return (nua_dialog_usage_t **)prev;
    }
  }

  return &none;
}

244
/** @internal Get a dialog usage */
245 246 247 248 249 250 251
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);
}

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

260
/** @internal Add dialog usage */
261
nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own, 
262 263 264 265 266 267 268 269 270 271 272 273
					 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",
274
		  (void *)own, nua_dialog_usage_name(du), 
275 276 277 278 279 280 281 282 283 284 285
		  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;
    }

286
    o = event ? sip_event_dup(own, event) : NULL;
287 288

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

    if (du) {
292 293
      su_home_ref(own);
      du->du_dialog = ds; 
294 295 296
      du->du_class = uclass;
      du->du_event = o;

297 298 299
      if (uclass->usage_add(own, ds, du) < 0) {
	su_free(own, o);
	su_free(own, du);
300 301 302 303
	return NULL;
      }
	
      SU_DEBUG_5(("nua(%p): adding %s usage%s%s\n",
304
		  (void *)own, nua_dialog_usage_name(du), 
305 306 307 308 309 310 311
		  o ? " with event " : "", o ? o->o_type :""));

      du->du_next = ds->ds_usage, ds->ds_usage = du;

      return du;
    }

312
    su_free(own, o);
313 314 315 316 317
  }

  return NULL;
}

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

325
  assert(own); assert(ds); assert(du);
326 327 328 329 330 331 332

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

  assert(*at);

333
  nua_dialog_usage_remove_at(own, ds, at);
334 335
}

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

351 352 353 354 355 356 357 358 359
    *at = du->du_next;

    o = du->du_event;

    SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
		(void *)own, nua_dialog_usage_name(du), 
		o ? " with event " : "", o ? o->o_type :""));
    du->du_class->usage_remove(own, ds, du);

360 361 362 363 364 365 366 367 368
    /* 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 */
369 370 371 372 373 374 375 376 377 378 379
    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);
    }
380

381 382
    su_home_unref(own);
    su_free(own, du);
383 384
  }

385
  /* Zap dialog if there are no more usages */
386
  if (ds->ds_terminating)
387 388
    ;
  else if (ds->ds_usage == NULL) {
389
    nua_dialog_remove(own, ds, NULL);
390 391 392
    ds->ds_has_events = 0;
    return;
  }
393
  else {
394
    nua_dialog_log_usage(own, ds);
395 396 397 398
  }
}

static
399
void nua_dialog_log_usage(nua_owner_t *own, nua_dialog_state_t *ds)
400 401 402
{
  nua_dialog_usage_t *du;

403
  if (SU_LOG->log_level >= 3) {
404
    char buffer[160];
405 406
    size_t l = 0, N = sizeof buffer;
    ssize_t n;
407 408 409 410 411 412 413 414 415 416 417 418
    
    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;
419
      l += (size_t)n;
420 421 422 423 424 425
      if (du->du_next && l + 2 < sizeof(buffer)) {
	strcpy(buffer + l, ", ");
	l += 2;
      }
    }
    
426
    SU_DEBUG_3(("nua(%p): handle with %s%s%s\n", (void *)own,
427 428 429 430 431 432
		ds->ds_has_session ? "session and " : "", 
		ds->ds_has_events ? "events " : "",
		buffer));
  }
}

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
/** Deinitialize dialog and its usage. @internal */
void nua_dialog_deinit(nua_owner_t *own,
		       nua_dialog_state_t *ds)
{
  ds->ds_terminating = 1;

  while (ds->ds_usage) {
    nua_dialog_usage_remove_at(own, ds, &ds->ds_usage);
  }

  nua_dialog_remove(own, ds, NULL);

  ds->ds_has_events = 0;
  ds->ds_terminating = 0;
}

449 450
/**@internal
 * Set refresh value suitably. 
451
 *
452 453 454
 * 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.
455
 *
456
 * If @a delta is 0, the dialog usage is never refreshed.
457 458 459
 */
void nua_dialog_usage_set_refresh(nua_dialog_usage_t *du, unsigned delta)
{
460
  if (delta == 0)
461
    nua_dialog_usage_reset_refresh(du);
462
  else if (delta > 90 && delta < 5 * 60)
463
    /* refresh 30..60 seconds before deadline */
464
    nua_dialog_usage_set_refresh_range(du, delta - 60, delta - 30);
465 466 467 468 469 470
  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;
471
    nua_dialog_usage_set_refresh_range(du, min, max);
472
  }
473
}
474

475
/**@internal Set refresh in range min..max seconds in the future. */
476 477
void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du, 
					unsigned min, unsigned max)
478
{
479
  sip_time_t now = sip_now(), target;
480 481 482 483 484 485 486 487 488 489
  unsigned delta;

  if (max < min)
    max = min;

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

490 491
  if (now + delta >= now)
    target = now + delta;
492
  else
493
    target = SIP_TIME_MAX;
494

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

498
  nua_dialog_usage_set_refresh_at(du, target);
499 500 501
}

/** Set absolute refresh time */
502 503
void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
				     sip_time_t target)
504 505 506 507 508 509 510 511 512 513 514
{
  SU_DEBUG_7(("nua(): refresh %s after %lu seconds\n",
	      nua_dialog_usage_name(du), target - sip_now()));
  du->du_refresh = target;
} 

/**@internal Do not refresh. */
void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
{
  if (du)
    du->du_refresh = 0;
515 516
}

517
/** @internal Refresh usage. */
518
void nua_dialog_usage_refresh(nua_owner_t *owner,
519
			      nua_dialog_state_t *ds,
520 521 522
			      nua_dialog_usage_t *du, 
			      sip_time_t now)
{
523 524
  assert(du && du->du_class->usage_refresh);
  du->du_class->usage_refresh(owner, ds, du, now);
525 526
}

527 528 529 530 531
/** Terminate all dialog usages gracefully. */
int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
{
  nua_dialog_usage_t *du;

532
  ds->ds_terminating = 1;
533

534 535 536 537 538 539 540 541 542 543 544 545
  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;
}

546
/** Shutdown (gracefully terminate) usage.
547 548 549 550 551 552
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
int nua_dialog_usage_shutdown(nua_owner_t *owner,
553 554
			      nua_dialog_state_t *ds,
			      nua_dialog_usage_t *du)
555 556
{
  if (du) {
557
    nua_dialog_usage_reset_refresh(du);
558 559
    du->du_shutdown = 1;
    assert(du->du_class->usage_shutdown);
560
    return du->du_class->usage_shutdown(owner, ds, du);
561
  }
562 563
  else
    return 200;
564
}
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602

/** Repeat shutdown of all usages.
 *
 * @note Caller must have a reference to nh
 */
int nua_dialog_repeat_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
{
  nua_dialog_usage_t *du;
  nua_server_request_t *sr, *sr_next;

  for (sr = ds->ds_sr; sr; sr = sr_next) {
    sr_next = sr->sr_next;

    if (nua_server_request_is_pending(sr)) {
      SR_STATUS1(sr, SIP_410_GONE); /* 410 terminates dialog */
      nua_server_respond(sr, NULL);
      nua_server_report(sr);
    }
  }

  for (du = ds->ds_usage; du ;) {
    nua_dialog_usage_t *du_next = du->du_next;

    nua_dialog_usage_shutdown(owner, ds, du);

    if (du_next == NULL)
      break;

    for (du = ds->ds_usage; du; du = du->du_next) {
      if (du == du_next)
	break;
      else if (!du->du_shutdown)
	break;
    }
  }

  return ds->ds_usage != NULL;
}