nua.c 28.9 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3
/*
 * This file is part of the Sofia-SIP package
 *
4
 * Copyright (C) 2006 Nokia Corporation.
Pekka Pessi's avatar
Pekka Pessi committed
5 6 7
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * 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
 *
 */

25
/**@file nua.c High-Level User Agent Library - "nua" Implementation.
Pekka Pessi's avatar
Pekka Pessi committed
26 27
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
28 29
 * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
 * @author Pasi Rinne-Rahkola
Pekka Pessi's avatar
Pekka Pessi committed
30 31 32 33 34 35
 *
 * @date Created: Wed Feb 14 18:32:58 2001 ppessi
 */

#include "config.h"

36 37 38
#include <sofia-sip/su_tag.h>
#include <sofia-sip/su_tag_class.h>
#include <sofia-sip/su_tagarg.h>
Pekka Pessi's avatar
Pekka Pessi committed
39

40
#include <sofia-sip/su_tag_io.h>
Pekka Pessi's avatar
Pekka Pessi committed
41 42

#define SU_LOG (nua_log)
43
#include <sofia-sip/su_debug.h>
Pekka Pessi's avatar
Pekka Pessi committed
44 45 46

#define SU_ROOT_MAGIC_T   struct nua_s
#define SU_MSG_ARG_T      struct event_s
47 48
#define NUA_SAVED_EVENT_T su_msg_t *

49
#include <sofia-sip/sip_status.h>
50
#include <sofia-sip/sip_header.h>
51
#include <sofia-sip/nta.h>
Pekka Pessi's avatar
Pekka Pessi committed
52

53 54
#include "sofia-sip/nua.h"
#include "sofia-sip/nua_tag.h"
Pekka Pessi's avatar
Pekka Pessi committed
55 56
#include "nua_stack.h"

57 58 59 60 61
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
Pekka Pessi's avatar
Pekka Pessi committed
62

Pekka Pessi's avatar
Pekka Pessi committed
63 64 65
/* From AM_INIT/AC_INIT in our "config.h" */
char const nua_version[] = VERSION;

66
/**Environment variable determining the debug log level for @nua module.
Pekka Pessi's avatar
Pekka Pessi committed
67 68
 *
 * The NUA_DEBUG environment variable is used to determine the debug logging
69
 * level for @nua module. The default level is 3.
Pekka Pessi's avatar
Pekka Pessi committed
70
 * 
71
 * @sa <sofia-sip/su_debug.h>, nua_log, SOFIA_DEBUG
Pekka Pessi's avatar
Pekka Pessi committed
72 73 74 75 76 77 78
 */
extern char const NUA_DEBUG[];

#ifndef SU_DEBUG
#define SU_DEBUG 3
#endif

79
/**Debug log for @nua module. 
Pekka Pessi's avatar
Pekka Pessi committed
80
 * 
81
 * The nua_log is the log object used by @nua module. The level of
Pekka Pessi's avatar
Pekka Pessi committed
82 83 84 85
 * #nua_log is set using #NUA_DEBUG environment variable.
 */
su_log_t nua_log[] = { SU_LOG_INIT("nua", "NUA_DEBUG", SU_DEBUG) };

86
/**Create a @nua agent.
Pekka Pessi's avatar
Pekka Pessi committed
87
 *
88
 * This function creates a Sofia-SIP User Agent stack object (@nua) and
Pekka Pessi's avatar
Pekka Pessi committed
89 90 91 92 93 94 95
 * initializes its parameters by given tagged values.
 *
 * @param root            Pointer to a root object
 * @param callback        Pointer to event callback function
 * @param magic           Pointer to callback context
 * @param tag,value,... List of tagged parameters
 *
96
 * @retval !=NULL a pointer to a @nua stack object \n
Pekka Pessi's avatar
Pekka Pessi committed
97 98 99
 * @retval NULL upon an error
 *
 * @par Related tags:
100 101 102 103 104 105
 *     NUTAG_PROXY()            \n
 *     NUTAG_URL()              \n
 *     NUTAG_SIPS_URL()         \n
 *     NUTAG_SIP_PARSER()       \n
 *     NUTAG_UICC()             \n
 *     NUTAG_CERTIFICATE_DIR()  \n
106 107
 *     and all tags listed in nua_set_params(), \n
 *     and all relevant NTATAG_* are passed to NTA.
Pekka Pessi's avatar
Pekka Pessi committed
108
 *
Pekka Pessi's avatar
Pekka Pessi committed
109
 * @note
110 111 112 113 114 115
 * From the @VERSION_1_12_2 all the nua_set_params() tags are processed. 
 * Previously all nutags except NUTAG_SOA_NAME() and NUTAG_MEDIA_ENABLE()
 * were ignored.
 *
 * @note
 * Both the NUTAG_URL() and NUTAG_SIPS_URL() are used to pass arguments to
Pekka Pessi's avatar
Pekka Pessi committed
116 117
 * nta_agent_add_tport(). 
 *
Pekka Pessi's avatar
Pekka Pessi committed
118 119
 * @par Events:
 *     none
120 121
 *
 * @sa nua_shutdown(), nua_destroy(), nua_handle()
Pekka Pessi's avatar
Pekka Pessi committed
122 123 124 125 126 127 128 129 130 131
 */
nua_t *nua_create(su_root_t *root,
		  nua_callback_f callback,
		  nua_magic_t *magic,
		  tag_type_t tag, tag_value_t value, ...)
{
  nua_t *nua = NULL;

  enter;

132 133
  if (callback == NULL)
    return (void)(errno = EFAULT), NULL;
Pekka Pessi's avatar
Pekka Pessi committed
134

135 136 137
  if (root == NULL)
    return (void)(errno = EFAULT), NULL;

138
  if ((nua = su_home_new(sizeof(*nua)))) {
Pekka Pessi's avatar
Pekka Pessi committed
139 140 141 142 143 144 145
    ta_list ta;

    su_home_threadsafe(nua->nua_home);
    nua->nua_api_root = root;

    ta_start(ta, tag, value);

146
    nua->nua_args = tl_adup(nua->nua_home, ta_args(ta));
Pekka Pessi's avatar
Pekka Pessi committed
147 148 149 150 151 152 153 154 155 156 157 158

    su_task_copy(nua->nua_client, su_root_task(root));

    /* XXX: where to put this in the nua_server case? */
#if HAVE_SMIME		/* Start NRC Boston */
      nua->sm = sm_create();
#endif                  /* End NRC Boston */

#ifndef NUA_SERVER
    if (su_clone_start(root,
		       nua->nua_clone,
		       nua,
159 160
		       nua_stack_init,
		       nua_stack_deinit) == SU_SUCCESS) {
Pekka Pessi's avatar
Pekka Pessi committed
161 162 163 164 165
      su_task_copy(nua->nua_server, su_clone_task(nua->nua_clone));
      nua->nua_callback = callback;
      nua->nua_magic = magic;
    }
    else {
166
      su_home_unref(nua->nua_home);
Pekka Pessi's avatar
Pekka Pessi committed
167 168 169 170 171 172 173 174 175 176
      nua = NULL;
    }
#endif

    ta_end(ta);
  }

  return nua;
}

177 178
/* nua_shutdown() is documented with nua_stack_shutdown() */

Pekka Pessi's avatar
Pekka Pessi committed
179 180 181 182
void nua_shutdown(nua_t *nua)
{
  enter;

183 184
  if (nua)
    nua->nua_shutdown_started = 1;
Pekka Pessi's avatar
Pekka Pessi committed
185 186 187
  nua_signal(nua, NULL, NULL, 1, nua_r_shutdown, 0, NULL, TAG_END());
}

188
/** Destroy the @nua stack.
Pekka Pessi's avatar
Pekka Pessi committed
189 190 191
 *
 * Before calling nua_destroy() the application 
 * should call nua_shutdown and wait for successful #nua_r_shutdown event.
192
 * Shuts down and destroys the @nua stack. Ongoing calls, registrations, 
Pekka Pessi's avatar
Pekka Pessi committed
193 194
 * and subscriptions are left as they are.
 *
195
 * @param nua         Pointer to @nua stack object
Pekka Pessi's avatar
Pekka Pessi committed
196 197 198 199 200 201 202 203 204
 *
 * @return
 *     nothing
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
205 206
 *
 * @sa nua_shutdown(), nua_create(), nua_handle_destroy(), nua_handle_unref()
Pekka Pessi's avatar
Pekka Pessi committed
207 208 209 210 211 212
 */
void nua_destroy(nua_t *nua)
{
  enter;

  if (nua) {
213 214 215 216 217 218 219
    if (!nua->nua_shutdown_final) {
      SU_DEBUG_0(("nua_destroy(%p): FATAL: nua_shutdown not completed\n", nua));
      assert(nua->nua_shutdown);
      return;
    }

    su_task_deinit(nua->nua_server);
220 221
    su_task_deinit(nua->nua_client);

Pekka Pessi's avatar
Pekka Pessi committed
222 223 224 225
    su_clone_wait(nua->nua_api_root, nua->nua_clone);
#if HAVE_SMIME		/* Start NRC Boston */
    sm_destroy(nua->sm);
#endif			/* End NRC Boston */
226
    su_home_unref(nua->nua_home);
Pekka Pessi's avatar
Pekka Pessi committed
227 228 229
  }
}

230 231 232 233 234 235 236 237
/** Fetch callback context from nua.
 *
 * @param nua         Pointer to @nua stack object
 *
 * @return Callback context pointer.
 */
nua_magic_t *nua_magic(nua_t *nua)
{
238
  return nua ? nua->nua_magic : NULL;
239 240
}

241
/** Obtain default operation handle of the @nua stack object.
Pekka Pessi's avatar
Pekka Pessi committed
242 243 244 245
 *
 * A default operation can be used for operations where the 
 * ultimate result is not important or can be discarded.
 *
246
 * @param nua         Pointer to @nua stack object
Pekka Pessi's avatar
Pekka Pessi committed
247
 *
248
 * @retval !=NULL Pointer to @nua operation handle
Pekka Pessi's avatar
Pekka Pessi committed
249 250 251 252 253 254 255 256
 * @retval NULL   No default operation exists
 *
 * @par Related tags:
 *    none
 *
 * @par Events:
 *    none
 *
Pekka Pessi's avatar
Pekka Pessi committed
257
 */
Pekka Pessi's avatar
Pekka Pessi committed
258 259
nua_handle_t *nua_default(nua_t *nua)
{
Pekka Pessi's avatar
Pekka Pessi committed
260
  return nua ? nua->nua_handles : NULL;
Pekka Pessi's avatar
Pekka Pessi committed
261 262 263 264 265 266
}

/** Create an operation handle 
 *
 * Allocates a new operation handle and associated storage.
 *
267
 * @param nua         Pointer to @nua stack object
Pekka Pessi's avatar
Pekka Pessi committed
268 269 270 271 272 273 274
 * @param hmagic      Pointer to callback context
 * @param tag, value, ... List of tagged parameters
 *
 * @retval !=NULL  Pointer to operation handle
 * @retval NULL    Creation failed
 *
 * @par Related tags:
275 276 277 278 279 280
 *     Duplicates the provided tags for use with every operation. Note that
 *     NUTAG_URL() is converted to SIPTAG_TO() if there is no SIPTAG_TO(). 
 *     And also vice versa, request-URI is taken from SIPTAG_TO() if there
 *     is no NUTAG_URL(). Note that certain SIP headers cannot be saved with
 *     the handle. They include @ContentLength, @CSeq, @RSeq, @RAck, and
 *     @Timestamp.
Pekka Pessi's avatar
Pekka Pessi committed
281
 *
282 283 284 285
 * @par
 *     nua_handle() accepts all the tags accepted by nua_set_hparams(), too.
 *
 *
Pekka Pessi's avatar
Pekka Pessi committed
286 287 288
 * @par Events:
 *     none
 *
289 290
 * @sa nua_handle_bind(), nua_handle_destroy(), nua_handle_ref(),
 * nua_handle_unref().
Pekka Pessi's avatar
Pekka Pessi committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
 */
nua_handle_t *nua_handle(nua_t *nua, nua_hmagic_t *hmagic,
			 tag_type_t tag, tag_value_t value, ...)
{
  nua_handle_t *nh = NULL;

  if (nua) {
    ta_list ta;

    ta_start(ta, tag, value);

    nh = nh_create_handle(nua, hmagic, ta_args(ta));
    
    if (nh)
      nh->nh_ref_by_user = 1;

    ta_end(ta);
  }

  return nh;
}

/** Bind a callback context to an operation handle. 
 *
 * @param nh          Pointer to operation handle
 * @param hmagic      Pointer to callback context
 *
 * @return
 *     nothing
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
void nua_handle_bind(nua_handle_t *nh, nua_hmagic_t *hmagic)
{
  enter;

  if (NH_IS_VALID(nh))
    nh->nh_magic = hmagic;
}

335 336 337 338 339 340 341 342 343 344 345 346 347
/** Fetch a callback context from an operation handle. 
 *
 * @param nh          Pointer to operation handle
 *
 * @return
 *     Pointer to callback context
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
348
nua_hmagic_t *nua_handle_magic(nua_handle_t *nh)
349 350 351 352 353 354 355 356 357 358
{
  nua_hmagic_t *magic = NULL;
  enter;

  if (NH_IS_VALID(nh))
    magic = nh->nh_magic;
  
  return magic;
}

Pekka Pessi's avatar
Pekka Pessi committed
359 360 361 362
/* ---------------------------------------------------------------------- */

/** Check if operation handle is used for INVITE
 *
363 364
 * Check if operation handle has been used with either outgoing or incoming
 * INVITE request.
Pekka Pessi's avatar
Pekka Pessi committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0 no invite in operation or operation handle is invalid 
 * @retval 1 operation has invite 
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_invite(nua_handle_t const *nh)
{
  return nh ? nh->nh_has_invite : 0;
}

/**Check if operation handle has active event subscriptions. 
 *
384 385
 * Active subscription can be established either by nua_subscribe() or
 * nua_refer() calls.
Pekka Pessi's avatar
Pekka Pessi committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0    no event subscriptions in operation or 
 *              operation handle is invalid 
 * @retval !=0  operation has event subscriptions
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_events(nua_handle_t const *nh)
{
  return nh ? nh->nh_ds->ds_has_events : 0;
}

/** Check if operation handle has active registrations
 *
406 407
 * A registration is active when either when a REGISTER operation is going
 * on or when it has successfully completed so that @nua stack is expected to
408 409 410 411
 * refresh the registration in the future. Normally, a handle has active
 * registration after nua_register() until nua_unregister() completes,
 * unless the initial nua_register() had either expiration time of 0 or it
 * had SIPTAG_CONTACT(NULL) as an argument.
Pekka Pessi's avatar
Pekka Pessi committed
412 413 414 415 416 417 418 419 420 421 422 423
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0 no active registration in operation or 
 *           operation handle is invalid
 * @retval 1 operation has registration
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
424 425
 *
 * @sa nua_register(), nua_unregister(), #nua_r_register, #nua_r_unregister
Pekka Pessi's avatar
Pekka Pessi committed
426 427 428 429 430 431 432 433 434 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 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
 */
int nua_handle_has_registrations(nua_handle_t const *nh)
{
  return nh && nh->nh_ds->ds_has_register;
}

/** Check if operation handle has been used with outgoing SUBSCRIBE of REFER request. 
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0 no active subscription in operation or 
 *           operation handle is invalid 
 * @retval 1 operation has subscription.
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_subscribe(nua_handle_t const *nh)
{
  return nh ? nh->nh_has_subscribe : 0;
}

/** Check if operation handle has been used with nua_register() or nua_unregister().
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0 no active register in operation or operation handle is invalid
 * @retval 1 operation has been used with nua_register() or nua-unregister()
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_register(nua_handle_t const *nh)
{
  return nh ? nh->nh_has_register : 0;
}

/** Check if operation handle has an active call 
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0 no active call in operation or operation handle is invalid
 * @retval 1 operation has established call or pending call request.
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_active_call(nua_handle_t const *nh)
{
484
  return nh ? nh->nh_active_call : 0;
Pekka Pessi's avatar
Pekka Pessi committed
485 486 487 488 489 490
}

/** Check if operation handle has a call on hold 
 *
 * Please note that this status is not affected by remote end putting 
 * this end on hold. Remote end can put each media separately on hold 
491 492
 * and status is reflected on SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO() 
 * and SOATAG_ACTIVE_CHAT() tag values in #nua_i_state event.
Pekka Pessi's avatar
Pekka Pessi committed
493 494 495 496 497
 *
 * @param nh          Pointer to operation handle
 *
 * @retval 0  if no call on hold in operation or operation handle is invalid 
 * @retval 1  if operation has call on hold, for example nua_invite() or 
498 499
 *            nua_update() has been called with SOATAG_HOLD() with non-NULL
 *            argument.
Pekka Pessi's avatar
Pekka Pessi committed
500 501 502 503 504 505 506 507 508
 *
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
int nua_handle_has_call_on_hold(nua_handle_t const *nh)
{
509
  return nh ? nh->nh_hold_remote : 0;
Pekka Pessi's avatar
Pekka Pessi committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
}

/** Get the remote address (From/To header) of operation handle
 *
 * Remote address is used as To header in outgoing operations and 
 * derived from From: header in incoming operations.
 *
 * @param nh          Pointer to operation handle
 *
 * @retval NULL   no remote address for operation or operation handle invalid
 * @retval !=NULL pointer to remote address for operation
 *     
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
sip_to_t const *nua_handle_remote(nua_handle_t const *nh)
{
  return nh ? nh->nh_ds->ds_remote : NULL;
}

/** Get the local address (From/To header) of operation handle
 *
 * Local address is used as From header in outgoing operations and 
 * derived from To: header in incoming operations.
 *
 * @param nh          Pointer to operation handle
 *
 * @retval NULL   no local address for operation or operation handle invalid
 * @retval !=NULL pointer to local address for operation
 *     
 * @par Related tags:
 *     none
 *
 * @par Events:
 *     none
 */
sip_to_t const *nua_handle_local(nua_handle_t const *nh)
{
  return nh ? nh->nh_ds->ds_local : NULL;
}

554
/* Documented with nua_stack_set_params() */
Pekka Pessi's avatar
Pekka Pessi committed
555 556 557 558 559 560 561 562 563 564 565 566
void nua_set_params(nua_t *nua, tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  ta_start(ta, tag, value);

  enter;

  nua_signal(nua, NULL, NULL, 0, nua_r_set_params, 0, NULL, ta_tags(ta));

  ta_end(ta);
}

567
/* Documented with nua_stack_get_params() */
Pekka Pessi's avatar
Pekka Pessi committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
void nua_get_params(nua_t *nua, tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  ta_start(ta, tag, value);

  enter;

  nua_signal(nua, NULL, NULL, 0, nua_r_get_params, 0, NULL, ta_tags(ta));

  ta_end(ta);
}

#define NUA_SIGNAL(nh, event, tag, value) \
  enter; \
  if (NH_IS_VALID((nh))) { \
    ta_list ta; \
    ta_start(ta, tag, value); \
    nua_signal((nh)->nh_nua, nh, NULL, 0, event, 0, NULL, ta_tags(ta));	\
    ta_end(ta); \
587 588
  } \
  else { \
Pekka Pessi's avatar
Pekka Pessi committed
589
    SU_DEBUG_1(("nua: " #event " with invalid handle %p\n", nh));	\
Pekka Pessi's avatar
Pekka Pessi committed
590 591
  }

592
/* Documented with nua_stack_set_params() */
593
void nua_set_hparams(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
594 595 596 597
{
  NUA_SIGNAL(nh, nua_r_set_params, tag, value);
}

598
/* Documented with nua_stack_get_params() */
599
void nua_get_hparams(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
600 601 602
{
  NUA_SIGNAL(nh, nua_r_get_params, tag, value);
}
Pekka Pessi's avatar
Pekka Pessi committed
603

604
/* Documented with nua_stack_register() */
Pekka Pessi's avatar
Pekka Pessi committed
605 606 607 608 609 610 611 612 613 614
void nua_register(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_register, tag, value);
}

void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_unregister, tag, value);
}

615
/* Documented with nua_stack_invite() */
Pekka Pessi's avatar
Pekka Pessi committed
616 617 618 619 620
void nua_invite(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_invite, tag, value);
}

621
/* Documented with nua_stack_ack() */
Pekka Pessi's avatar
Pekka Pessi committed
622 623 624 625 626
void nua_ack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_ack, tag, value);
}

627
/* Documented with nua_stack_bye() */
Pekka Pessi's avatar
Pekka Pessi committed
628 629 630 631 632
void nua_bye(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_bye, tag, value);
}

633
/* Documented with nua_stack_cancel() */
Pekka Pessi's avatar
Pekka Pessi committed
634 635 636 637 638
void nua_cancel(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_cancel, tag, value);
}

639
/* Documented with nua_stack_options() */
Pekka Pessi's avatar
Pekka Pessi committed
640 641 642 643 644
void nua_options(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_options, tag, value);
}

645
/* Documented with nua_stack_message() */
Pekka Pessi's avatar
Pekka Pessi committed
646 647 648 649 650 651 652 653
void nua_message(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_message, tag, value);
}

/** Send a chat message. 
 *
 * A chat channel can be established during call setup using "message" media. 
654 655
 * An active chat channel is indicated using #nua_i_state event containing 
 * SOATAG_ACTIVE_CHAT() tag. Chat messages can be sent using this channel with 
Pekka Pessi's avatar
Pekka Pessi committed
656 657 658 659 660 661 662 663 664 665
 * nua_chat() function. Currently this is implemented using SIP MESSAGE 
 * requests but in future MSRP (message session protocol) will replace it.
*
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
666 667 668 669 670
 *    SIPTAG_CONTENT_TYPE() \n
 *    SIPTAG_PAYLOAD()      \n
 *    SIPTAG_FROM()         \n
 *    SIPTAG_TO()           \n
 *    Use of other SIP tags is deprecated
Pekka Pessi's avatar
Pekka Pessi committed
671 672 673 674 675 676 677 678 679
 *
 * @par Events:
 *    #nua_r_chat
 */
void nua_chat(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_chat, tag, value);
}

680
/* Documented with nua_stack_subscribe() */
Pekka Pessi's avatar
Pekka Pessi committed
681 682 683 684 685
void nua_subscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_subscribe, tag, value);
}

686
/* Documented with nua_stack_subscribe() */
Pekka Pessi's avatar
Pekka Pessi committed
687 688 689 690 691
void nua_unsubscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_unsubscribe, tag, value);
}

692
/* Documented with nua_stack_notify() */
Pekka Pessi's avatar
Pekka Pessi committed
693 694 695 696 697
void nua_notify(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_notify, tag, value);
}

698 699
/* nua_r_notify is documented with process_response_to_notify() */

Pekka Pessi's avatar
Pekka Pessi committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
/** Create an event server. 
 *
 * This function create an event server taking care of sending NOTIFY 
 * requests and responding to further SUBSCRIBE requests. The event 
 * server can accept multiple subscriptions from several sources and 
 * takes care for distributing the notifications. Unlike other functions 
 * this call only accepts the SIP tags listed below.
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
715
 *    NUTAG_URL() \n
716 717 718 719
 *    SIPTAG_EVENT() or SIPTAG_EVENT_STR() \n
 *    SIPTAG_CONTENT_TYPE() or SIPTAG_CONTENT_TYPE_STR() \n
 *    SIPTAG_PAYLOAD() or SIPTAG_PAYLOAD_STR() \n
 *    SIPTAG_ACCEPT() or SIPTAG_ACCEPT_STR() \n
Pekka Pessi's avatar
Pekka Pessi committed
720 721 722 723 724 725 726 727 728 729 730
 *
 * @par Events:
 *    #nua_r_notify
 */
void nua_notifier(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_notifier, tag, value);
}

/** Terminate an event server. 
 *
731 732
 * Terminate an event server with matching event and content type. The event
 * server was created earlier with nua_notifier() function.
Pekka Pessi's avatar
Pekka Pessi committed
733 734 735 736 737 738 739 740
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
741 742 743 744
 *    SIPTAG_EVENT() \n
 *    SIPTAG_CONTENT_TYPE() \n
 *    SIPTAG_PAYLOAD() \n
 *    NEATAG_REASON()
Pekka Pessi's avatar
Pekka Pessi committed
745 746 747
 *
 * @par Events:
 *    #nua_r_terminate
748 749
 *
 * @sa nua_notifier(), nua_authorize().
Pekka Pessi's avatar
Pekka Pessi committed
750 751 752 753 754 755
 */
void nua_terminate(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_terminate, tag, value);
}

756
/* Documented with nua_stack_refer() */
Pekka Pessi's avatar
Pekka Pessi committed
757 758 759 760 761
void nua_refer(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_refer, tag, value);
}

762
/* Documented with nua_stack_publish() */
Pekka Pessi's avatar
Pekka Pessi committed
763 764 765 766 767
void nua_publish(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_publish, tag, value);
}

768
/* Documented with nua_stack_publish() */
Pekka Pessi's avatar
Pekka Pessi committed
769 770 771 772 773
void nua_unpublish(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_unpublish, tag, value);
}

774
/* Documented with nua_stack_info() */
Pekka Pessi's avatar
Pekka Pessi committed
775 776 777 778 779
void nua_info(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_info, tag, value);
}

780
/* Documented with nua_stack_prack() */
Pekka Pessi's avatar
Pekka Pessi committed
781 782 783 784 785
void nua_prack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_prack, tag, value);
}

786
/* Documented with nua_stack_update() */
Pekka Pessi's avatar
Pekka Pessi committed
787 788 789 790 791 792 793 794 795
void nua_update(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_update, tag, value);
}

/** Authenticate an operation.
 *
 * - 401 / 407 response with www-authenticate header/ proxy-authenticate header
 * - application should provide stack with username&password for each realm
796
 *   with NUTAG_AUTH() tag
Pekka Pessi's avatar
Pekka Pessi committed
797 798 799 800 801 802 803 804 805
 * - restarts operation
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
806
 *    NUTAG_AUTH()
Pekka Pessi's avatar
Pekka Pessi committed
807 808
 *
 * @par Events:
Pekka Pessi's avatar
Pekka Pessi committed
809
 *    (any operation events)
Pekka Pessi's avatar
Pekka Pessi committed
810 811 812 813 814 815
 */
void nua_authenticate(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_authenticate, tag, value);
}

816 817
/** Authorize a subscriber.
 *
818 819 820 821 822 823
 * After creating a local presence server by nua_notifier(), an incoming
 * SUBSCRIBE request causes #nua_i_subscription event. Each subscriber is
 * identified with NEATAG_SUB() tag in the #nua_i_subscription event. 
 * Application can either authorize the subscriber with
 * NUTAG_SUBSTATE(#nua_substate_active) or terminate the subscription with
 * NUTAG_SUBSTATE(#nua_substate_terminated).
824 825 826 827 828 829 830 831
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
832
 *    NEATAG_SUB() \n
833
 *    NUTAG_SUBSTATE()
834 835
 *
 * @par Events:
836 837 838
 *    #nua_i_subscription
 *
 * @sa nua_notifier(), nua_terminate()
839 840 841 842 843 844
 */
void nua_authorize(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_authorize, tag, value);
}

Pekka Pessi's avatar
Pekka Pessi committed
845 846 847 848 849 850
/*# Redirect an operation. */
void nua_redirect(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
{
  NUA_SIGNAL(nh, nua_r_redirect, tag, value);
}

851 852
/* Documented with nua_stack_respond() */

Pekka Pessi's avatar
Pekka Pessi committed
853 854 855 856 857 858 859 860 861 862 863 864 865 866
void nua_respond(nua_handle_t *nh,
		 int status, char const *phrase,
		 tag_type_t tag, tag_value_t value,
		 ...)
{
  enter;

  if (NH_IS_VALID(nh)) {
    ta_list ta;
    ta_start(ta, tag, value);
    nua_signal(nh->nh_nua, nh, NULL, 0, nua_r_respond,
	       status, phrase, ta_tags(ta));
    ta_end(ta);
  }
867
  else {
Pekka Pessi's avatar
Pekka Pessi committed
868
    SU_DEBUG_1(("nua: respond with invalid handle %p\n", nh));
869
  }
Pekka Pessi's avatar
Pekka Pessi committed
870 871 872 873
}

/** Destroy a handle 
 *
874 875 876 877 878 879 880 881 882
 * Terminate the protocol state associated with an operation handle. The
 * stack discards resources and terminates the ongoing dialog usage,
 * sessions and transactions associated with this handle. For example, calls
 * are terminated with BYE request. Also, the reference count for the handle
 * is also decremented.
 *
 * The handles use reference counting for memory management. In order to
 * make it more convenient for programmer, nua_handle_destroy() decreases
 * the reference count, too.
Pekka Pessi's avatar
Pekka Pessi committed
883 884 885 886 887 888 889 890 891 892 893
 *
 * @param nh              Pointer to operation handle
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
 *    none
 *
 * @par Events:
 *    none
894
 *
895 896
 * @sa nua_handle(), nua_handle_bind(), nua_handle_ref(), nua_handle_unref(),
 * nua_unregister(), nua_unpublish(), nua_unsubscribe(), nua_bye().
Pekka Pessi's avatar
Pekka Pessi committed
897 898 899 900 901
 */
void nua_handle_destroy(nua_handle_t *nh)
{
  enter;

902
  if (NH_IS_VALID(nh) && !NH_IS_DEFAULT(nh)) {
Pekka Pessi's avatar
Pekka Pessi committed
903 904 905 906 907 908 909 910 911 912 913 914
    nh->nh_valid = NULL;	/* Events are no more delivered to appl. */
    nua_signal(nh->nh_nua, nh, NULL, 1, nua_r_destroy, 0, NULL, TAG_END());
  }
}

/*# Send a request to the protocol thread */
void nua_signal(nua_t *nua, nua_handle_t *nh, msg_t *msg, int always,
		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;
915
  size_t len, xtra, e_len, l_len = 0, l_xtra = 0;
Pekka Pessi's avatar
Pekka Pessi committed
916 917
  ta_list ta;

918 919 920
  if (nua == NULL || (nua->nua_shutdown_started && event != nua_r_shutdown))
    return;

Pekka Pessi's avatar
Pekka Pessi committed
921 922 923 924 925 926
  ta_start(ta, tag, value);

  e_len = offsetof(event_t, e_tags);
  len = tl_len(ta_args(ta));
  xtra = tl_xtra(ta_args(ta), len);

927
  if (su_msg_create(sumsg, nua->nua_server, su_task_null,
928
		    nua_stack_signal,
Pekka Pessi's avatar
Pekka Pessi committed
929 930 931 932 933 934 935 936 937 938
		    e_len + len + l_len + xtra + l_xtra) == 0) {
    event_t *e = su_msg_data(sumsg);
    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);

939
    assert(tend == t); (void)tend; assert(b == bend); (void)bend;
Pekka Pessi's avatar
Pekka Pessi committed
940 941 942

    e->e_always = always;
    e->e_event = event;
943
    e->e_nh = event == nua_r_destroy ? nh : nua_handle_ref(nh);
Pekka Pessi's avatar
Pekka Pessi committed
944 945 946 947
    e->e_status = status;
    e->e_phrase = phrase;

    if (su_msg_send(sumsg) != 0)
948
      nua_handle_unref(nh);
Pekka Pessi's avatar
Pekka Pessi committed
949 950
  } 
  else {
951 952
    /* XXX  - we should return error code to application */
    assert(ENOMEM == 0);
Pekka Pessi's avatar
Pekka Pessi committed
953 954 955 956 957 958 959 960 961 962 963 964 965
  }

  ta_end(ta);
}

/*# Receive event from protocol machine and hand it over to application */
void nua_event(nua_t *root_magic, su_msg_r sumsg, event_t *e)
{
  nua_t *nua;
  nua_handle_t *nh = e->e_nh;

  enter;

966
  if (nh) {
967
    if (!nh->nh_ref_by_user && nh->nh_valid) {
Pekka Pessi's avatar
Pekka Pessi committed
968
      nh->nh_ref_by_user = 1;
969
      nua_handle_ref(nh);
Pekka Pessi's avatar
Pekka Pessi committed
970 971 972 973
    }
  }

  if (!nh || !nh->nh_valid) {	/* Handle has been destroyed */
974
    if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
975 976
      SU_DEBUG_9(("nua(%p): freed by application\n", nh));
    }
Pekka Pessi's avatar
Pekka Pessi committed
977
    if (e->e_msg)
978
      msg_destroy(e->e_msg), e->e_msg = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
979 980 981
    return;
  }

982 983 984 985
  nua = nh->nh_nua; assert(nua);

  if (e->e_event == nua_r_shutdown && e->e_status >= 200)
    nua->nua_shutdown_final = 1;
Pekka Pessi's avatar
Pekka Pessi committed
986 987 988 989

  if (!nua->nua_callback)
    return;

990
  if (NH_IS_DEFAULT(nh))
Pekka Pessi's avatar
Pekka Pessi committed
991 992
    nh = NULL;

993 994 995 996
  su_msg_save(nua->nua_current, sumsg);

  e->e_nh = NULL;

Pekka Pessi's avatar
Pekka Pessi committed
997 998 999 1000 1001 1002
  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);

1003 1004 1005 1006
  if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
    SU_DEBUG_9(("nua(%p): freed by application\n", nh));
  }

1007 1008 1009
  if (!su_msg_is_non_null(nua->nua_current))
    return;

Pekka Pessi's avatar
Pekka Pessi committed
1010
  if (e->e_msg)
1011
    msg_destroy(e->e_msg), e->e_msg = NULL;
1012 1013 1014 1015

  su_msg_destroy(nua->nua_current);
}

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
/** Get current request message. */
msg_t *nua_current_request(nua_t const *nua)
{
  return nua && nua->nua_current ? su_msg_data(nua->nua_current)->e_msg : NULL;
}

/** Get request message from saved nua event. */
msg_t *nua_saved_event_request(nua_saved_event_t const *saved)
{
  return saved ? su_msg_data(saved)->e_msg : NULL;
}

1028 1029 1030 1031 1032
/** Save nua event and its arguments */
int nua_save_event(nua_t *nua, nua_saved_event_t return_saved[1])
{
  if (nua && return_saved) {
    su_msg_save(return_saved, nua->nua_current);
1033 1034 1035 1036 1037
    if (su_msg_is_non_null(return_saved)) {
      /* Remove references to tasks */
      su_msg_remove_refs(return_saved);
      return 1;
    }
1038 1039 1040 1041
  }
  return 0;
}

1042 1043
/** Get event data */
nua_event_data_t const *nua_event_data(nua_saved_event_t const saved[1])
1044
{
1045
  return saved ? su_msg_data(saved) : NULL;
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
}

/** Destroy saved event */
void nua_destroy_event(nua_saved_event_t saved[1])
{
  if (su_msg_is_non_null(saved)) {
    event_t *e = su_msg_data(saved);
    nua_handle_t *nh = e->e_nh;

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

1058
    if (nh && !NH_IS_DEFAULT(nh) && nua_handle_unref(nh)) {
1059 1060 1061 1062 1063
      SU_DEBUG_9(("nua(%p): freed by application\n", nh));
    }

    su_msg_destroy(saved);
  }
Pekka Pessi's avatar
Pekka Pessi committed
1064
}
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089

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

struct nua_stack_handle_make_replaces_args {
  sip_replaces_t *retval;
  nua_handle_t *nh;
  su_home_t *home;
  int early_only;
};

static int nua_stack_handle_make_replaces_call(void *arg)
{
  struct nua_stack_handle_make_replaces_args *a = arg;

  a->retval = nua_stack_handle_make_replaces(a->nh, a->home, a->early_only);

  return 0;
}


/**Generate a @Replaces header for handle.
 *
 * @since New in @VERSION_1_12_4.
 *
 * @sa nua_handle_by_replaces(), @Replaces, @RFC3891, nua_refer(),
1090
 * #nua_i_refer, @ReferTo, nta_leg_make_replaces()
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
 */
sip_replaces_t *nua_handle_make_replaces(nua_handle_t *nh, 
					 su_home_t *home,
					 int early_only)
{
  if (nh && nh->nh_valid && nh->nh_nua) {
    struct nua_stack_handle_make_replaces_args a = { NULL, nh, home, early_only };

    if (su_task_execute(nh->nh_nua->nua_server, 
			nua_stack_handle_make_replaces_call, (void *)&a, 
			NULL) == 0) {
      return a.retval;
    }
  }
  return NULL;
}

struct nua_stack_handle_by_replaces_args {
  nua_handle_t *retval;
  nua_t *nua;
  sip_replaces_t const *r;
};

static int nua_stack_handle_by_replaces_call(void *arg)
{
  struct nua_stack_handle_by_replaces_args *a = arg;

  a->retval = nua_stack_handle_by_replaces(a->nua, a->r);

  return 0;
}

/** Obtain a new reference to an existing handle based on @Replaces header.
 *
 * @since New in @VERSION_1_12_4.
 *
 * @note 
 * You should release the reference with nua_handle_unref() when you are
 * done with handle.
 *
 * @sa nua_handle_make_replaces(), @Replaces, @RFC3891, nua_refer(),
1132
 * #nua_i_refer, @ReferTo, nta_leg_by_replaces()
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
 */
nua_handle_t *nua_handle_by_replaces(nua_t *nua, sip_replaces_t const *r)
{
  if (nua) {
    struct nua_stack_handle_by_replaces_args a = { NULL, nua, r };

    if (su_task_execute(nua->nua_server, 
			nua_stack_handle_by_replaces_call, (void *)&a, 
			NULL) == 0) {
      nua_handle_t *nh = a.retval;

      if (nh && !NH_IS_DEFAULT(nh) && nh->nh_valid)
	return nua_handle_ref(nh);
    }
  }
  return NULL;
}