nua_stack.c 88.8 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
/*
 * 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 nua_stack.c
26
 * @brief Sofia-SIP User Agent Engine implementation
Pekka Pessi's avatar
Pekka Pessi committed
27 28 29
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
30
 * @author Martti Mela <Martti Mela@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
31 32 33 34 35 36 37 38
 * @author Remeres Jacobs <Remeres.Jacobs@nokia.com>
 * @author Tat Chan <Tat.Chan@nokia.com>
 *
 * @date Created: Wed Feb 14 18:32:58 2001 ppessi
 */

#include "config.h"

39
#include <sofia-sip/su_tag_class.h>
Pekka Pessi's avatar
Pekka Pessi committed
40
#include <sofia-sip/su_tag_inline.h>
41
#include <sofia-sip/su_tagarg.h>
42
#include <sofia-sip/su_strlst.h>
43
#include <sofia-sip/su_uniqueid.h>
Pekka Pessi's avatar
Pekka Pessi committed
44

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

#define SU_ROOT_MAGIC_T   struct nua_s
48
#define SU_MSG_ARG_T      struct nua_ee_data
49
#define SU_TIMER_ARG_T    struct nua_client_request
Pekka Pessi's avatar
Pekka Pessi committed
50

51
#define NUA_SAVED_EVENT_T su_msg_t *
52
#define NUA_SAVED_SIGNAL_T su_msg_t *
53

Pekka Pessi's avatar
Pekka Pessi committed
54 55
#define NTA_AGENT_MAGIC_T    struct nua_s
#define NTA_LEG_MAGIC_T      struct nua_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
56
#define NTA_OUTGOING_MAGIC_T struct nua_client_request
Pekka Pessi's avatar
Pekka Pessi committed
57

58 59 60 61
#include <sofia-sip/sip.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/sip_util.h>
Pekka Pessi's avatar
Pekka Pessi committed
62

63
#include <sofia-sip/tport_tag.h>
64
#include <sofia-sip/nta.h>
65
#include <sofia-sip/nta_tport.h>
66
#include <sofia-sip/auth_client.h>
Pekka Pessi's avatar
Pekka Pessi committed
67

68
#include <sofia-sip/soa.h>
Pekka Pessi's avatar
Pekka Pessi committed
69

70 71
#include "sofia-sip/nua.h"
#include "sofia-sip/nua_tag.h"
Pekka Pessi's avatar
Pekka Pessi committed
72 73
#include "nua_stack.h"

74 75 76 77 78
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
79

80
#include <assert.h>
81

Pekka Pessi's avatar
Pekka Pessi committed
82 83 84 85 86 87
/* ========================================================================
 *
 *                       Protocol stack side
 *
 * ======================================================================== */

88 89 90 91 92 93 94
/* ---------------------------------------------------------------------- */
/* Internal types */

/** Extended event data. */
typedef struct nua_ee_data {
  nua_t *ee_nua;
  nua_event_data_t ee_data[1];
95
} nua_ee_data_t;
96 97 98 99 100 101 102 103 104 105 106 107

/** @internal Linked stack frames from nua event callback */
struct nua_event_frame_s {
  nua_event_frame_t *nf_next;
  nua_saved_event_t nf_saved[1];
};


static void nua_event_deinit(nua_ee_data_t *ee);
static void nua_application_event(nua_t *, su_msg_r, nua_ee_data_t *ee);
static void nua_stack_signal(nua_t *nua, su_msg_r, nua_ee_data_t *ee);

Pekka Pessi's avatar
Pekka Pessi committed
108 109 110 111
nua_handle_t *nh_create(nua_t *nua, tag_type_t t, tag_value_t v, ...);
static void nh_append(nua_t *nua, nua_handle_t *nh);
static void nh_remove(nua_t *nua, nua_handle_t *nh);

112
static int nh_authorize(nua_handle_t *nh,
Pekka Pessi's avatar
Pekka Pessi committed
113 114
			tag_type_t tag, tag_value_t value, ...);

115 116
static void nua_stack_timer(nua_t *nua, su_timer_t *t, su_timer_arg_t *a);

117 118
static int nua_client_request_complete(nua_client_request_t *cr);

119 120
/* ---------------------------------------------------------------------- */
/* Constant data */
Pekka Pessi's avatar
Pekka Pessi committed
121

122
/**@internal Default internal error. */
123
char const nua_internal_error[] = "Internal NUA Error";
Pekka Pessi's avatar
Pekka Pessi committed
124

125 126 127 128
char const nua_application_sdp[] = "application/sdp";

#define NUA_STACK_TIMER_INTERVAL (1000)

Pekka Pessi's avatar
Pekka Pessi committed
129 130 131 132
/* ----------------------------------------------------------------------
 * Initialization & deinitialization
 */

133
int nua_stack_init(su_root_t *root, nua_t *nua)
Pekka Pessi's avatar
Pekka Pessi committed
134 135
{
  su_home_t *home;
136
  nua_handle_t *dnh;
137

Pekka Pessi's avatar
Pekka Pessi committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
  static int initialized_logs = 0;

  enter;

  if (!initialized_logs) {
    extern su_log_t tport_log[];
    extern su_log_t nta_log[];
    extern su_log_t nea_log[];
    extern su_log_t iptsec_log[];

    su_log_init(tport_log);
    su_log_init(nta_log);
    su_log_init(nea_log);
    su_log_init(iptsec_log);

    initialized_logs = 1;
  }

156
  nua->nua_root = root;
157 158 159 160
  nua->nua_timer = su_timer_create(su_root_task(root),
				   NUA_STACK_TIMER_INTERVAL);
  if (!nua->nua_timer)
    return -1;
161

Pekka Pessi's avatar
Pekka Pessi committed
162
  home = nua->nua_home;
163 164
  nua->nua_handles_tail = &nua->nua_handles;
  sip_from_init(nua->nua_from);
Pekka Pessi's avatar
Pekka Pessi committed
165

166
  dnh = su_home_clone(nua->nua_home, sizeof (*dnh) + sizeof(*dnh->nh_prefs));
167 168
  if (!dnh)
    return -1;
169

170
  dnh->nh_prefs = (void *)(dnh + 1);
171
  dnh->nh_valid = nua_valid_handle_cookie;
172
  dnh->nh_nua = nua;
173
  nua_handle_ref(dnh); dnh->nh_ref_by_stack = 1;
174 175
  nua_handle_ref(dnh); dnh->nh_ref_by_user = 1;
  nh_append(nua, dnh);
176
  dnh->nh_identity = dnh;
177
  dnh->nh_ds->ds_local = nua->nua_from;
178
  dnh->nh_ds->ds_remote = nua->nua_from;
Pekka Pessi's avatar
Pekka Pessi committed
179

180 181 182
  if (nua_stack_set_defaults(dnh, dnh->nh_prefs) < 0)
    return -1;

183
  if (nua_stack_set_params(nua, dnh, nua_i_none, nua->nua_args) < 0)
184 185 186 187
    return -1;

  nua->nua_invite_accept = sip_accept_make(home, SDP_MIME_TYPE);

188
  nua->nua_nta = nta_agent_create(root, NONE, NULL, NULL,
189 190 191 192 193 194
				  NTATAG_MERGE_482(1),
				  NTATAG_CLIENT_RPORT(1),
				  NTATAG_UA(1),
#if HAVE_SOFIA_SMIME
				  NTATAG_SMIME(nua->sm),
#endif
Pekka Pessi's avatar
Pekka Pessi committed
195
				  TPTAG_STUN_SERVER(1),
Pekka Pessi's avatar
Pekka Pessi committed
196
				  TAG_NEXT(nua->nua_args));
Pekka Pessi's avatar
Pekka Pessi committed
197

Pekka Pessi's avatar
Pekka Pessi committed
198 199 200 201 202 203
  dnh->nh_ds->ds_leg = nta_leg_tcreate(nua->nua_nta,
				       nua_stack_process_request, dnh,
				       NTATAG_NO_DIALOG(1),
				       TAG_END());

  if (nua->nua_nta == NULL ||
204
      dnh->nh_ds->ds_leg == NULL ||
Pekka Pessi's avatar
Pekka Pessi committed
205 206 207
      nta_agent_set_params(nua->nua_nta, NTATAG_UA(1), TAG_END()) < 0 ||
      nua_stack_init_transport(nua, nua->nua_args) < 0) {
    SU_DEBUG_1(("nua: initializing SIP stack failed\n"));
208
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
209
  }
Pekka Pessi's avatar
Pekka Pessi committed
210 211

  if (nua_stack_set_from(nua, 1, nua->nua_args) < 0)
212
    return -1;
213

214
  if (nua->nua_prefs->ngp_detect_network_updates)
215
    nua_stack_launch_network_change_detector(nua);
Pekka Pessi's avatar
Pekka Pessi committed
216

217
  nua_stack_timer(nua, nua->nua_timer, NULL);
Pekka Pessi's avatar
Pekka Pessi committed
218

Pekka Pessi's avatar
Pekka Pessi committed
219 220 221
  return 0;
}

222
void nua_stack_deinit(su_root_t *root, nua_t *nua)
Pekka Pessi's avatar
Pekka Pessi committed
223 224 225
{
  enter;

226
  su_timer_destroy(nua->nua_timer), nua->nua_timer = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
227 228 229 230 231 232 233
  nta_agent_destroy(nua->nua_nta), nua->nua_nta = NULL;
}

/* ----------------------------------------------------------------------
 * Sending events to client application
 */

234
static void nua_stack_shutdown(nua_t *);
235

236 237 238 239
void
  nua_stack_authenticate(nua_t *, nua_handle_t *, nua_event_t, tagi_t const *),
  nua_stack_respond(nua_t *, nua_handle_t *, int , char const *, tagi_t const *),
  nua_stack_destroy_handle(nua_t *, nua_handle_t *, tagi_t const *);
Pekka Pessi's avatar
Pekka Pessi committed
240

241 242 243 244 245
/* Notifier */
void
  nua_stack_authorize(nua_t *, nua_handle_t *, nua_event_t, tagi_t const *),
  nua_stack_notifier(nua_t *, nua_handle_t *, nua_event_t, tagi_t const *),
  nua_stack_terminate(nua_t *, nua_handle_t *, nua_event_t, tagi_t const *);
Pekka Pessi's avatar
Pekka Pessi committed
246

247 248
int nh_notifier_shutdown(nua_handle_t *nh, nea_event_t *ev,
			 tag_type_t t, tag_value_t v, ...);
Pekka Pessi's avatar
Pekka Pessi committed
249

250 251 252 253 254 255 256 257 258 259 260 261
int nua_stack_tevent(nua_t *nua, nua_handle_t *nh, msg_t *msg,
		     nua_event_t event, int status, char const *phrase,
		     tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  int retval;
  ta_start(ta, tag, value);
  retval = nua_stack_event(nua, nh, msg, event, status, phrase, ta_args(ta));
  ta_end(ta);
  return retval;
}

Pekka Pessi's avatar
Pekka Pessi committed
262
/** @internal Send an event to the application. */
263 264
int nua_stack_event(nua_t *nua, nua_handle_t *nh, msg_t *msg,
		    nua_event_t event, int status, char const *phrase,
265
		    tagi_t const *tags)
Pekka Pessi's avatar
Pekka Pessi committed
266 267
{
  su_msg_r sumsg = SU_MSG_R_INIT;
Pekka Pessi's avatar
Pekka Pessi committed
268
  size_t e_len, len, xtra, p_len;
Pekka Pessi's avatar
Pekka Pessi committed
269

270
  if (event == nua_r_ack || event == nua_i_none)
271 272
    return event;

273 274 275
  if (nh == nua->nua_dhandle)
    nh = NULL;

Pekka Pessi's avatar
Pekka Pessi committed
276 277 278 279 280
  if (nua_log->log_level >= 5) {
    char const *name = nua_event_name(event) + 4;
    char const *p = phrase ? phrase : "";

    if (status == 0)
281
      SU_DEBUG_5(("nua(%p): event %s %s\n", (void *)nh, name, p));
Pekka Pessi's avatar
Pekka Pessi committed
282
    else
283
      SU_DEBUG_5(("nua(%p): event %s %u %s\n", (void *)nh, name, status, p));
Pekka Pessi's avatar
Pekka Pessi committed
284 285 286
  }

  if (event == nua_r_destroy) {
287
    if (msg)
Pekka Pessi's avatar
Pekka Pessi committed
288 289 290 291 292 293 294
      msg_destroy(msg);
    if (status >= 200) {
      nh_destroy(nua, nh);
    }
    return event;
  }

295 296
  if ((event > nua_r_authenticate && event <= nua_r_ack)
      || event < nua_i_error
297
      || (nh && !nh->nh_valid)
298
      || (nua->nua_shutdown && event != nua_r_shutdown &&
299
	  !nua->nua_prefs->ngp_shutdown_events)) {
300
    if (msg)
Pekka Pessi's avatar
Pekka Pessi committed
301 302 303 304
      msg_destroy(msg);
    return event;
  }

305
  if (tags) {
306
    e_len = offsetof(nua_ee_data_t, ee_data[0].e_tags);
307 308 309 310
    len = tl_len(tags);
    xtra = tl_xtra(tags, len);
  }
  else {
311
    e_len = sizeof(nua_ee_data_t), len = 0, xtra = 0;
312
  }
Pekka Pessi's avatar
Pekka Pessi committed
313 314
  p_len = phrase ? strlen(phrase) + 1 : 1;

315 316 317
  if (su_msg_new(sumsg, e_len + len + xtra + p_len) == 0) {
    nua_ee_data_t *ee = su_msg_data(sumsg);
    nua_event_data_t *e = ee->ee_data;
318
    void *p;
Pekka Pessi's avatar
Pekka Pessi committed
319

320 321 322
    if (tags) {
      tagi_t *t = e->e_tags, *t_end = (tagi_t *)((char *)t + len);
      void *b = t_end, *end = (char *)b + xtra;
Pekka Pessi's avatar
Pekka Pessi committed
323

324
      t = tl_dup(t, tags, &b); p = b;
325
      assert(t == t_end); assert(b == end); (void)end;
326 327 328
    }
    else
      p = e + 1;
Pekka Pessi's avatar
Pekka Pessi committed
329

330
    ee->ee_nua = nua_stack_ref(nua);
Pekka Pessi's avatar
Pekka Pessi committed
331
    e->e_event = event;
332
    e->e_nh = nh ? nua_handle_ref(nh) : NULL;
Pekka Pessi's avatar
Pekka Pessi committed
333
    e->e_status = status;
334
    e->e_phrase = strcpy(p, phrase ? phrase : "");
335 336
    if (msg)
      e->e_msg = msg, su_home_threadsafe(msg_home(msg));
Pekka Pessi's avatar
Pekka Pessi committed
337

338 339 340
    su_msg_deinitializer(sumsg, nua_event_deinit);

    su_msg_send_to(sumsg, nua->nua_client, nua_application_event);
Pekka Pessi's avatar
Pekka Pessi committed
341 342 343 344 345
  }

  return event;
}

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
static
void nua_event_deinit(nua_ee_data_t *ee)
{
  nua_t *nua = ee->ee_nua;
  nua_event_data_t *e = ee->ee_data;
  nua_handle_t *nh = e->e_nh;

  if (e->e_msg)
    msg_destroy(e->e_msg), e->e_msg = NULL;

  if (nh)
    nua_handle_unref(nh), e->e_nh = NULL;

  if (nua)
    nua_stack_unref(nua), ee->ee_nua = NULL;
}

/*# Receive event from protocol machine and hand it over to application */
static
void nua_application_event(nua_t *dummy, su_msg_r sumsg, nua_ee_data_t *ee)
{
  nua_t *nua = ee->ee_nua;
  nua_event_data_t *e = ee->ee_data;
  nua_handle_t *nh = e->e_nh;

  enter;

  ee->ee_nua = NULL;
  e->e_nh = NULL;

  if (nh == NULL) {
    /* Xyzzy */
  }
  else if (nh->nh_valid) {
    if (!nh->nh_ref_by_user) {
      /* Application must now call nua_handle_destroy() */
      nh->nh_ref_by_user = 1;
383
      nua_handle_ref(nh);
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    }
  }
  else if (!nh->nh_valid) {	/* Handle has been destroyed */
    if (nua_log->log_level >= 7) {
      char const *name = nua_event_name(e->e_event) + 4;
      SU_DEBUG_7(("nua(%p): event %s dropped\n", (void *)nh, name));
    }
    nua_handle_unref(nh);
    nua_stack_unref(nua);
    return;
  }

  if (e->e_event == nua_r_shutdown && e->e_status >= 200)
    nua->nua_shutdown_final = 1;

  if (nua->nua_callback) {
    nua_event_frame_t frame[1];
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    su_msg_save(frame->nf_saved, sumsg);
    frame->nf_next = nua->nua_current, nua->nua_current = frame;

    nua->nua_callback(e->e_event, e->e_status, e->e_phrase,
		      nua, nua->nua_magic,
		      nh, nh ? nh->nh_magic : NULL,
		      e->e_msg ? sip_object(e->e_msg) : NULL,
		      e->e_tags);

    su_msg_destroy(frame->nf_saved);
    nua->nua_current = frame->nf_next;
  }

  nua_handle_unref(nh);
  nua_stack_unref(nua);
}

/** Get current request message. @NEW_1_12_4.
 *
 * @note A response message is returned when processing response message.
 *
 * @sa #nua_event_e, nua_respond(), NUTAG_WITH_CURRENT()
 */
msg_t *nua_current_request(nua_t const *nua)
{
  if (nua && nua->nua_current && su_msg_is_non_null(nua->nua_current->nf_saved))
    return su_msg_data(nua->nua_current->nf_saved)->ee_data->e_msg;
  return NULL;
}

432
/** Get request message from saved nua event. @NEW_1_12_4.
433
 *
434
 * @sa nua_save_event(), nua_respond(), NUTAG_WITH_SAVED(),
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
 */
msg_t *nua_saved_event_request(nua_saved_event_t const *saved)
{
  return saved && saved[0] ? su_msg_data(saved)->ee_data->e_msg : NULL;
}

/** Save nua event and its arguments.
 *
 * @sa #nua_event_e, nua_event_data() nua_saved_event_request(), nua_destroy_event()
 */
int nua_save_event(nua_t *nua, nua_saved_event_t return_saved[1])
{
  if (return_saved) {
    if (nua && nua->nua_current) {
      su_msg_save(return_saved, nua->nua_current->nf_saved);
      return su_msg_is_non_null(return_saved);
    }
    else
      *return_saved = NULL;
  }

  return 0;
}

/* ---------------------------------------------------------------------- */

/** @internal
462
 * Post signal to stack itself
Pekka Pessi's avatar
Pekka Pessi committed
463
 */
464
void nua_stack_post_signal(nua_handle_t *nh, nua_event_t event,
465
			   tag_type_t tag, tag_value_t value, ...)
Pekka Pessi's avatar
Pekka Pessi committed
466 467 468
{
  ta_list ta;
  ta_start(ta, tag, value);
469
  nua_signal((nh)->nh_nua, nh, NULL, event, 0, NULL, ta_tags(ta));
Pekka Pessi's avatar
Pekka Pessi committed
470 471 472 473
  ta_end(ta);
}


474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
/*# Send a request to the protocol thread */
int nua_signal(nua_t *nua, nua_handle_t *nh, msg_t *msg,
	       nua_event_t event,
	       int status, char const *phrase,
	       tag_type_t tag, tag_value_t value, ...)
{
  su_msg_r sumsg = SU_MSG_R_INIT;
  size_t len, xtra, ee_len, l_len = 0, l_xtra = 0;
  ta_list ta;
  int retval = -1;

  if (nua == NULL)
    return -1;

  if (nua->nua_shutdown_started && event != nua_r_shutdown)
    return -1;

  ta_start(ta, tag, value);

  ee_len = offsetof(nua_ee_data_t, ee_data[0].e_tags);
  len = tl_len(ta_args(ta));
  xtra = tl_xtra(ta_args(ta), len);

  if (su_msg_new(sumsg, ee_len + len + l_len + xtra + l_xtra) == 0) {
    nua_ee_data_t *ee = su_msg_data(sumsg);
    nua_event_data_t *e = ee->ee_data;
    tagi_t *t = e->e_tags;
    void *b = (char *)t + len + l_len;

    tagi_t *tend = (tagi_t *)b;
    char *bend = (char *)b + xtra + l_xtra;

    t = tl_dup(t, ta_args(ta), &b);

    assert(tend == t); (void)tend; assert(b == bend); (void)bend;

    e->e_always = event == nua_r_destroy || event == nua_r_shutdown;
    e->e_event = event;
    e->e_nh = nh ? nua_handle_ref(nh) : NULL;
    e->e_status = status;
    e->e_phrase = phrase;

    su_msg_deinitializer(sumsg, nua_event_deinit);
517

518
    retval = su_msg_send_to(sumsg, nua->nua_server, nua_stack_signal);
519 520 521 522 523 524 525 526 527 528

    if (retval == 0){
      SU_DEBUG_7(("nua(%p): %s signal %s\n", (void *)nh,
		  "sent", nua_event_name(event) + 4));
    }
    else {
      SU_DEBUG_0(("nua(%p): %s signal %s\n", (void *)nh,
		  "FAILED TO SEND", nua_event_name(event) + 4));

    }
529 530 531 532 533 534 535
  }

  ta_end(ta);

  return retval;
}

Pekka Pessi's avatar
Pekka Pessi committed
536 537 538
/* ----------------------------------------------------------------------
 * Receiving events from client
 */
539 540
static
void nua_stack_signal(nua_t *nua, su_msg_r msg, nua_ee_data_t *ee)
Pekka Pessi's avatar
Pekka Pessi committed
541
{
542
  nua_event_data_t *e = ee->ee_data;
Pekka Pessi's avatar
Pekka Pessi committed
543 544
  nua_handle_t *nh = e->e_nh;
  tagi_t *tags = e->e_tags;
Pekka Pessi's avatar
Pekka Pessi committed
545 546
  nua_event_t event;
  int error = 0;
Pekka Pessi's avatar
Pekka Pessi committed
547 548 549 550 551

  if (nh) {
    if (!nh->nh_prev)
      nh_append(nua, nh);
    if (!nh->nh_ref_by_stack) {
552
      /* Mark handle as used by stack */
Pekka Pessi's avatar
Pekka Pessi committed
553
      nh->nh_ref_by_stack = 1;
554
      nua_handle_ref(nh);
555
    }
Pekka Pessi's avatar
Pekka Pessi committed
556 557 558 559
  }

  if (nua_log->log_level >= 5) {
    char const *name = nua_event_name(e->e_event);
560

Pekka Pessi's avatar
Pekka Pessi committed
561
    if (e->e_status == 0)
562
      SU_DEBUG_5(("nua(%p): %s signal %s\n", (void *)nh, "recv", name + 4));
Pekka Pessi's avatar
Pekka Pessi committed
563
    else
564
      SU_DEBUG_5(("nua(%p): recv signal %s %u %s\n",
565 566
		  (void *)nh, name + 4,
		  e->e_status, e->e_phrase ? e->e_phrase : ""));
Pekka Pessi's avatar
Pekka Pessi committed
567 568
  }

569 570
  su_msg_save(nua->nua_signal, msg);

Pekka Pessi's avatar
Pekka Pessi committed
571 572
  event = e->e_event;

573
  if (nua->nua_shutdown && !e->e_always) {
Pekka Pessi's avatar
Pekka Pessi committed
574
    /* Shutting down */
Pekka Pessi's avatar
Pekka Pessi committed
575
    nua_stack_event(nua, nh, NULL, event,
576
		    901, "Stack is going down",
577
		    NULL);
Pekka Pessi's avatar
Pekka Pessi committed
578
  }
Pekka Pessi's avatar
Pekka Pessi committed
579
  else switch (event) {
Pekka Pessi's avatar
Pekka Pessi committed
580
  case nua_r_get_params:
Pekka Pessi's avatar
Pekka Pessi committed
581
    nua_stack_get_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
582 583
    break;
  case nua_r_set_params:
Pekka Pessi's avatar
Pekka Pessi committed
584
    nua_stack_set_params(nua, nh ? nh : nua->nua_dhandle, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
585 586
    break;
  case nua_r_shutdown:
587
    nua_stack_shutdown(nua);
Pekka Pessi's avatar
Pekka Pessi committed
588 589 590
    break;
  case nua_r_register:
  case nua_r_unregister:
Pekka Pessi's avatar
Pekka Pessi committed
591
    nua_stack_register(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
592 593
    break;
  case nua_r_invite:
Pekka Pessi's avatar
Pekka Pessi committed
594
    error = nua_stack_invite(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
595 596
    break;
  case nua_r_cancel:
Pekka Pessi's avatar
Pekka Pessi committed
597
    error = nua_stack_cancel(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
598 599
    break;
  case nua_r_bye:
Pekka Pessi's avatar
Pekka Pessi committed
600
    error = nua_stack_bye(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
601 602
    break;
  case nua_r_options:
Pekka Pessi's avatar
Pekka Pessi committed
603
    error = nua_stack_options(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
604 605
    break;
  case nua_r_refer:
Pekka Pessi's avatar
Pekka Pessi committed
606
    error = nua_stack_refer(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
607 608
    break;
  case nua_r_publish:
Pekka Pessi's avatar
Pekka Pessi committed
609
  case nua_r_unpublish:
Pekka Pessi's avatar
Pekka Pessi committed
610
    error = nua_stack_publish(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
611 612
    break;
  case nua_r_info:
Pekka Pessi's avatar
Pekka Pessi committed
613
    error = nua_stack_info(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
614
    break;
615 616 617
  case nua_r_prack:
    error = nua_stack_prack(nua, nh, event, tags);
    break;
Pekka Pessi's avatar
Pekka Pessi committed
618
  case nua_r_update:
Pekka Pessi's avatar
Pekka Pessi committed
619
    error = nua_stack_update(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
620 621
    break;
  case nua_r_message:
Pekka Pessi's avatar
Pekka Pessi committed
622
    error = nua_stack_message(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
623 624 625
    break;
  case nua_r_subscribe:
  case nua_r_unsubscribe:
Pekka Pessi's avatar
Pekka Pessi committed
626
    error = nua_stack_subscribe(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
627 628
    break;
  case nua_r_notify:
Pekka Pessi's avatar
Pekka Pessi committed
629
    error = nua_stack_notify(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
630 631
    break;
  case nua_r_notifier:
Pekka Pessi's avatar
Pekka Pessi committed
632
    nua_stack_notifier(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
633 634
    break;
  case nua_r_terminate:
Pekka Pessi's avatar
Pekka Pessi committed
635
    nua_stack_terminate(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
636 637
    break;
  case nua_r_method:
Pekka Pessi's avatar
Pekka Pessi committed
638
    error = nua_stack_method(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
639 640
    break;
  case nua_r_authenticate:
Pekka Pessi's avatar
Pekka Pessi committed
641
    nua_stack_authenticate(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
642
    break;
643
  case nua_r_authorize:
Pekka Pessi's avatar
Pekka Pessi committed
644
    nua_stack_authorize(nua, nh, event, tags);
645
    break;
Pekka Pessi's avatar
Pekka Pessi committed
646
  case nua_r_ack:
Pekka Pessi's avatar
Pekka Pessi committed
647
    error = nua_stack_ack(nua, nh, event, tags);
Pekka Pessi's avatar
Pekka Pessi committed
648 649
    break;
  case nua_r_respond:
650
    nua_stack_respond(nua, nh, e->e_status, e->e_phrase, tags);
Pekka Pessi's avatar
Pekka Pessi committed
651
    break;
652
  case nua_r_destroy:
653
    nua_stack_destroy_handle(nua, nh, tags);
654 655
    su_msg_destroy(nua->nua_signal);
    return;
Pekka Pessi's avatar
Pekka Pessi committed
656 657 658 659
  default:
    break;
  }

Pekka Pessi's avatar
Pekka Pessi committed
660
  if (error < 0) {
661 662
    nua_stack_event(nh->nh_nua, nh, NULL, event,
		    NUA_ERROR_AT(__FILE__, __LINE__), NULL);
Pekka Pessi's avatar
Pekka Pessi committed
663 664
  }

665 666
  su_msg_destroy(nua->nua_signal);
}
667

668 669 670 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
/* ====================================================================== */
/* Signal and event handling */

/** Get event data.
 *
 * @sa #nua_event_e, nua_event_save(), nua_saved_event_request(), nua_destroy_event().
 */
nua_event_data_t const *nua_event_data(nua_saved_event_t const saved[1])
{
  return saved && saved[0] ? su_msg_data(saved)->ee_data : NULL;
}

/** Destroy saved event.
 *
 * @sa #nua_event_e, nua_event_save(), nua_event_data(), nua_saved_event_request().
 */
void nua_destroy_event(nua_saved_event_t saved[1])
{
  if (saved) su_msg_destroy(saved);
}

/** @internal Move signal. */
void nua_move_signal(nua_saved_signal_t a[1], nua_saved_signal_t b[1])
{
  su_msg_save(a, b);
}

void nua_destroy_signal(nua_saved_signal_t saved[1])
{
  if (saved) su_msg_destroy(saved);
}

nua_signal_data_t const *nua_signal_data(nua_saved_signal_t const saved[1])
{
  return nua_event_data(saved);
Pekka Pessi's avatar
Pekka Pessi committed
703 704
}

705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
/* ====================================================================== */

static int nh_call_pending(nua_handle_t *nh, sip_time_t time);

/**@internal
 * Timer routine.
 *
 * Go through all active handles and execute pending tasks
 */
void nua_stack_timer(nua_t *nua, su_timer_t *t, su_timer_arg_t *a)
{
  nua_handle_t *nh, *nh_next;
  sip_time_t now = sip_now();
  su_root_t *root = su_timer_root(t);

  su_timer_set(t, nua_stack_timer, a);

  if (nua->nua_shutdown) {
    nua_stack_shutdown(nua);
    return;
  }

  for (nh = nua->nua_handles; nh; nh = nh_next) {
    nh_next = nh->nh_next;
    nh_call_pending(nh, now);
    su_root_yield(root);	/* Handle received packets */
  }
}


static
int nh_call_pending(nua_handle_t *nh, sip_time_t now)
{
  nua_dialog_state_t *ds = nh->nh_ds;
  nua_dialog_usage_t *du;
  sip_time_t next = now + NUA_STACK_TIMER_INTERVAL / 1000;

  for (du = ds->ds_usage; du; du = du->du_next) {
    if (now == 0)
      break;
    if (du->du_refresh && du->du_refresh < next)
      break;
  }

  if (du == NULL)
    return 0;

  nua_handle_ref(nh);

  while (du) {
    nua_dialog_usage_t *du_next = du->du_next;

    nua_dialog_usage_refresh(nh, ds, du, now);

    if (du_next == NULL)
      break;

    for (du = nh->nh_ds->ds_usage; du; du = du->du_next)
      if (du == du_next)
	break;

    for (; du; du = du->du_next) {
      if (now == 0)
	break;
      if (du->du_refresh && du->du_refresh < next)
	break;
    }
  }

  nua_handle_unref(nh);

  return 1;
}



Pekka Pessi's avatar
Pekka Pessi committed
781 782
/* ====================================================================== */

783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
/**Shutdown a @nua stack.
 *
 * When the @nua stack is shutdown, ongoing calls are released,
 * registrations unregistered, publications un-PUBLISHed and subscriptions
 * terminated. If the stack cannot terminate everything within 30 seconds,
 * it sends the #nua_r_shutdown event with status 500.
 *
 * @param nua         Pointer to @nua stack object
 *
 * @return
 *     nothing
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     #nua_r_shutdown
 *
 * @sa #nua_r_shutdown, nua_destroy(), nua_create(), nua_bye(),
 * nua_unregister(), nua_unpublish(), nua_unsubscribe(), nua_notify(),
 * nua_handle_destroy(), nua_handle_unref()
 */

/** @NUA_EVENT nua_r_shutdown
 *
 * Answer to nua_shutdown().
 *
 * Status codes
 * - 100 shutdown started
 * - 101 shutdown in progress (sent when shutdown has been progressed)
 * - 200 shutdown was successful
 * - 500 shutdown timeout after 30 sec
 *
 * @param status shutdown status code
 * @param nh     NULL
 * @param hmagic NULL
 * @param sip    NULL
 * @param tags   empty
 *
 * @sa nua_shutdown(), nua_destroy()
 *
 * @END_NUA_EVENT
 */

Pekka Pessi's avatar
Pekka Pessi committed
827
/** @internal Shut down stack. */
828
void nua_stack_shutdown(nua_t *nua)
Pekka Pessi's avatar
Pekka Pessi committed
829 830 831 832 833 834 835 836 837
{
  nua_handle_t *nh, *nh_next;
  int busy = 0;
  sip_time_t now = sip_now();
  int status;
  char const *phrase;

  enter;

838 839
  if (!nua->nua_shutdown)
    nua->nua_shutdown = now;
Pekka Pessi's avatar
Pekka Pessi committed
840 841

  for (nh = nua->nua_handles; nh; nh = nh_next) {
842
    nua_dialog_state_t *ds = nh->nh_ds;
843

844
    nh_next = nh->nh_next;
Pekka Pessi's avatar
Pekka Pessi committed
845

846
    busy += nua_dialog_repeat_shutdown(nh, ds);
Pekka Pessi's avatar
Pekka Pessi committed
847

Pekka Pessi's avatar
Pekka Pessi committed
848 849
    if (nh->nh_soa) {
      soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
850 851
    }

852
    if (nua_client_request_pending(ds->ds_cr))
Pekka Pessi's avatar
Pekka Pessi committed
853 854 855 856 857 858 859 860
      busy++;

    if (nh_notifier_shutdown(nh, NULL, NEATAG_REASON("noresource"), TAG_END()))
      busy++;
  }

  if (!busy)
    SET_STATUS(200, "Shutdown successful");
861
  else if (now == nua->nua_shutdown)
Pekka Pessi's avatar
Pekka Pessi committed
862
    SET_STATUS(100, "Shutdown started");
863
  else if (now - nua->nua_shutdown < 30)
Pekka Pessi's avatar
Pekka Pessi committed
864 865 866 867
    SET_STATUS(101, "Shutdown in progress");
  else
    SET_STATUS(500, "Shutdown timeout");

868
  if (status >= 200) {
869 870 871
    for (nh = nua->nua_handles; nh; nh = nh_next) {
      nh_next = nh->nh_next;
      while (nh->nh_ds && nh->nh_ds->ds_usage) {
872
	nua_dialog_usage_remove(nh, nh->nh_ds, nh->nh_ds->ds_usage, NULL, NULL);
873 874
      }
    }
875
    su_timer_destroy(nua->nua_timer), nua->nua_timer = NULL;
876 877 878
    nta_agent_destroy(nua->nua_nta), nua->nua_nta = NULL;
  }

879
  nua_stack_event(nua, NULL, NULL, nua_r_shutdown, status, phrase, NULL);
Pekka Pessi's avatar
Pekka Pessi committed
880 881 882 883
}

/* ---------------------------------------------------------------------- */

Pekka Pessi's avatar
Pekka Pessi committed
884
/** @internal Create a handle */
Pekka Pessi's avatar
Pekka Pessi committed
885 886 887 888 889 890 891 892 893 894
nua_handle_t *nh_create(nua_t *nua, tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  nua_handle_t *nh;

  enter;

  ta_start(ta, tag, value);
  nh = nh_create_handle(nua, NULL, ta_args(ta));
  ta_end(ta);
895

Pekka Pessi's avatar
Pekka Pessi committed
896 897 898 899 900 901 902 903
  if (nh) {
    nh->nh_ref_by_stack = 1;
    nh_append(nua, nh);
  }

  return nh;
}

Pekka Pessi's avatar
Pekka Pessi committed
904
/** @internal Append a handle to the list of handles */
Pekka Pessi's avatar
Pekka Pessi committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
void nh_append(nua_t *nua, nua_handle_t *nh)
{
  nh->nh_next = NULL;
  nh->nh_prev = nua->nua_handles_tail;
  *nua->nua_handles_tail = nh;
  nua->nua_handles_tail = &nh->nh_next;
}

nua_handle_t *nh_validate(nua_t *nua, nua_handle_t *maybe)
{
  nua_handle_t *nh;

  if (maybe)
    for (nh = nua->nua_handles; nh; nh = nh->nh_next)
      if (nh == maybe)
	return nh;

  return NULL;
}

925
void nua_stack_destroy_handle(nua_t *nua, nua_handle_t *nh, tagi_t const *tags)
926 927
{
  if (nh->nh_notifier)
928
    nua_stack_terminate(nua, nh, 0, NULL);
929

930 931
  nua_dialog_shutdown(nh, nh->nh_ds);

932 933 934 935 936 937 938 939
  if (nh->nh_ref_by_user) {
    nh->nh_ref_by_user = 0;
    nua_handle_unref(nh);
  }

  nh_destroy(nua, nh);
}

Pekka Pessi's avatar
Pekka Pessi committed
940 941
#define nh_is_inserted(nh) ((nh)->nh_prev != NULL)

Pekka Pessi's avatar
Pekka Pessi committed
942
/** @internal Remove a handle from list of handles */
Pekka Pessi's avatar
Pekka Pessi committed
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
static
void nh_remove(nua_t *nua, nua_handle_t *nh)
{
  assert(nh_is_inserted(nh)); assert(*nh->nh_prev == nh);

  if (nh->nh_next)
    nh->nh_next->nh_prev = nh->nh_prev;
  else
    nua->nua_handles_tail = nh->nh_prev;

  *nh->nh_prev = nh->nh_next;

  nh->nh_prev = NULL;
  nh->nh_next = NULL;
}

959

Pekka Pessi's avatar
Pekka Pessi committed
960 961
void nh_destroy(nua_t *nua, nua_handle_t *nh)
{
962
  assert(nh); assert(nh != nua->nua_dhandle);
Pekka Pessi's avatar
Pekka Pessi committed
963 964 965 966

  if (nh->nh_notifier)
    nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;

Pekka Pessi's avatar
Pekka Pessi committed
967
  while (nh->nh_ds->ds_cr)
968
    nua_client_request_complete(nh->nh_ds->ds_cr);
Pekka Pessi's avatar
Pekka Pessi committed
969 970 971 972

  while (nh->nh_ds->ds_sr)
    nua_server_request_destroy(nh->nh_ds->ds_sr);

973 974
  nua_dialog_deinit(nh, nh->nh_ds);

Pekka Pessi's avatar
Pekka Pessi committed
975 976
  if (nh->nh_soa)
    soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
977 978 979 980

  if (nh_is_inserted(nh))
    nh_remove(nua, nh);

981
  nua_handle_unref(nh);		/* Remove stack reference */
Pekka Pessi's avatar
Pekka Pessi committed
982 983 984 985
}

/* ======================================================================== */

Pekka Pessi's avatar
Pekka Pessi committed
986
/**@internal
Pekka Pessi's avatar
Pekka Pessi committed
987
 * Save handle parameters and initial authentication info.
988 989 990 991
 *
 * @retval -1 upon an error
 * @retval 0 when successful
 */
992
int nua_stack_init_handle(nua_t *nua, nua_handle_t *nh, tagi_t const *tags)
Pekka Pessi's avatar
Pekka Pessi committed
993
{
994
  int retval = 0;
Pekka Pessi's avatar
Pekka Pessi committed
995

996 997 998
  if (nh == NULL)
    return -1;

999
  assert(nh != nua->nua_dhandle);
Pekka Pessi's avatar
Pekka Pessi committed
1000

1001
  if (nua_stack_set_params(nua, nh, nua_i_error, tags) < 0)
1002
    retval = -1;
1003

1004 1005
  if (retval || nh->nh_init) /* Already initialized? */
    return retval;
Pekka Pessi's avatar
Pekka Pessi committed
1006 1007 1008 1009 1010

  if (nh->nh_tags)
    nh_authorize(nh, TAG_NEXT(nh->nh_tags));

  nh->nh_init = 1;
1011 1012

  return 0;
Pekka Pessi's avatar
Pekka Pessi committed
1013 1014
}

Pekka Pessi's avatar
Pekka Pessi committed
1015
/** @internal Create a handle for processing incoming request */
1016
nua_handle_t *nua_stack_incoming_handle(nua_t *nua,
1017 1018 1019
					nta_incoming_t *irq,
					sip_t const *sip,
					int create_dialog)
Pekka Pessi's avatar
Pekka Pessi committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
{
  nua_handle_t *nh;
  url_t const *url;
  sip_to_t to[1];
  sip_from_t from[1];

  assert(sip && sip->sip_from && sip->sip_to);

  if (sip->sip_contact)
    url = sip->sip_contact->m_url;
  else
    url = sip->sip_from->a_url;

1033
  /* Strip away parameters */
Pekka Pessi's avatar
Pekka Pessi committed
1034 1035 1036 1037 1038 1039 1040
  sip_from_init(from)->a_display = sip->sip_to->a_display;
  *from->a_url = *sip->sip_to->a_url;

  sip_to_init(to)->a_display = sip->sip_from->a_display;
  *to->a_url = *sip->sip_from->a_url;

  nh = nh_create(nua,
1041 1042 1043
		 NUTAG_URL((url_string_t *)url), /* Remote target */
		 SIPTAG_TO(to), /* Local AoR */
		 SIPTAG_FROM(from), /* Remote AoR */
Pekka Pessi's avatar
Pekka Pessi committed
1044 1045
		 TAG_END());

1046
  if (nh && nua_stack_init_handle(nua, nh, NULL) < 0)
1047
    nh_destroy(nua, nh), nh = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
1048

1049 1050
  if (nh && create_dialog) {
    struct nua_dialog_state *ds = nh->nh_ds;
Pekka Pessi's avatar
Pekka Pessi committed
1051

1052
    nua_dialog_store_peer_info(nh, ds, sip);
Pekka Pessi's avatar
Pekka Pessi committed
1053

1054
    ds->ds_leg = nta_leg_tcreate(nua->nua_nta, nua_stack_process_request, nh,
1055 1056 1057 1058 1059
				 SIPTAG_CALL_ID(sip->sip_call_id),
				 SIPTAG_FROM(sip->sip_to),
				 SIPTAG_TO(sip->sip_from),
				 NTATAG_REMOTE_CSEQ(sip->sip_cseq->cs_seq),
				 TAG_END());
Pekka Pessi's avatar
Pekka Pessi committed
1060

1061
    if (!ds->ds_leg || !nta_leg_tag(ds->ds_leg, nta_incoming_tag(irq, NULL)))
1062
      nh_destroy(nua, nh), nh = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
1063 1064 1065
  }

  if (nh)
1066
    nua_dialog_uas_route(nh, nh->nh_ds, sip, 1);
Pekka Pessi's avatar
Pekka Pessi committed
1067 1068 1069 1070 1071

  return nh;
}


1072 1073 1074 1075 1076
/** Set flags and special event on handle.
 *
 * @retval 0 when successful
 * @retval -1 upon an error
 */
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
int nua_stack_set_handle_special(nua_handle_t *nh,
				 enum nh_kind kind,
				 nua_event_t special)
{
  if (nh == NULL)
    return -1;

  if (special && nh->nh_special && nh->nh_special != special)
    return -1;

  if (!nh_is_special(nh) && !nh->nh_has_invite) {
    switch (kind) {
    case nh_has_invite:    nh->nh_has_invite = 1;    break;
    case nh_has_subscribe: nh->nh_has_subscribe = 1; break;
    case nh_has_notify:    nh->nh_has_notify = 1;    break;
    case nh_has_register:  nh->nh_has_register = 1;  break;
    case nh_has_nothing:
    default:
      break;
    }

    if (special)
      nh->nh_special = special;
  }

  return 0;
}

1105
sip_replaces_t *nua_stack_handle_make_replaces(nua_handle_t *nh,
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
					       su_home_t *home,
					       int early_only)
{
  if (nh && nh->nh_ds && nh->nh_ds->ds_leg)
    return nta_leg_make_replaces(nh->nh_ds->ds_leg, home, early_only);
  else
    return NULL;
}

nua_handle_t *nua_stack_handle_by_replaces(nua_t *nua,
					   sip_replaces_t const *r)
{
  if (nua) {
    nta_leg_t *leg = nta_leg_by_replaces(nua->nua_nta, r);
    if (leg)
      return nta_leg_magic(leg, nua_stack_process_request);
  }
  return NULL;
}

Michael Jerris's avatar
Michael Jerris committed
1126 1127 1128 1129 1130 1131 1132 1133 1134
nua_handle_t *nua_stack_handle_by_call_id(nua_t *nua, const char *call_id)
{
  if (nua) {
    nta_leg_t *leg = nta_leg_by_call_id(nua->nua_nta, call_id);
    if (leg)
      return nta_leg_magic(leg, nua_stack_process_request);
  }
  return NULL;
}
1135

Pekka Pessi's avatar
Pekka Pessi committed