nua_session.c 128 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
/*
 * 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_session.c
26
 * @brief SIP session handling
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Wed Mar  8 16:17:27 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>
#include <sofia-sip/sip_protos.h>
#include <sofia-sip/sip_status.h>
#include <sofia-sip/sip_util.h>
46
#include <sofia-sip/su_uniqueid.h>
47

48
#define NTA_INCOMING_MAGIC_T struct nua_server_request
Pekka Pessi's avatar
Pekka Pessi committed
49
#define NTA_OUTGOING_MAGIC_T struct nua_client_request
50
#define NTA_RELIABLE_MAGIC_T struct nua_server_request
51 52 53 54 55 56 57 58

#include "nua_stack.h"
#include <sofia-sip/soa.h>

#ifndef SDP_H
typedef struct sdp_session_s sdp_session_t;
#endif

59
/* ---------------------------------------------------------------------- */
60

61 62 63 64 65 66 67 68 69 70 71 72 73
/** @enum nua_callstate

The states for SIP session established with INVITE.

Initially the call states follow the state of the INVITE transaction. If the
initial INVITE transaction fails, the call is terminated. The status codes
401 and 407 are an exception: if the client (on the left side in the diagram
below) receives them, it enters in #nua_callstate_authenticating state.

If a re-INVITE transaction fails, the result depends on the status code in
failure. The call can return to the ready state, be terminated immediately,
or be terminated gracefully. The proper action to take is determined with
sip_response_terminates_dialog().			   
74 75

@sa @ref nua_call_model, #nua_i_state, nua_invite(), #nua_i_invite
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
							   
@par Session State Diagram				   
							   
@code							   
  			 +----------+			   
  			 |          |---------------------+
  			 |   Init   |                     |
  			 |          |----------+          |
  			 +----------+          |          |
  			  |        |           |          |
                 --/INVITE|        |INVITE/100 |          |
                          V        V           |          |
     		+----------+      +----------+ |          |
       +--------|          |      |          | |          |
       |  18X +-| Calling  |      | Received | |INVITE/   |
       |   /- | |          |      |          | |  /18X    |
       |      V +----------+      +----------+ V          |
       |   +----------+ |           |     |  +----------+ |
       |---|          | |2XX     -/ |  -/ |  |          | |
       |   | Proceed- | | /-     2XX|  18X|  |  Early   | |INVITE/
       |   |   ing    | |           |     +->|          | |  /200
       |   +----------+ V           V        +----------+ |
       |     |  +----------+      +----------+   | -/     |
       |  2XX|  |          |      |          |<--+ 2XX    |
       |   /-|  | Complet- |      | Complete |<-----------+
       |     +->|   ing    |      |          |------+
       |        +----------+      +----------+      |
       |                  |        |      |         |
       |401,407/     -/ACK|        |ACK/- |timeout/ |
       | /ACK             V        V      | /BYE    |
       |                 +----------+     |         |
       |                 |          |     |         |
       |              +--|  Ready   |     |         |
       |              |  |          |     |         |
       |              |  +----------+     |         |
       |              |       |           |         |
       |         BYE/ |       |-/BYE      |         |BYE/
       V         /200 |       V           |         |/200
  +----------+        |  +----------+     |         |
  |          |        |  |          |     |         |
  |Authentic-|        |  | Terminat-|<----+         |
  |  ating   |        |  |   ing    |               |
  +----------+        |  +----------+               |
                      |       |                     |
                      |       |[23456]XX/-          |
                      |       V                     |
                      |  +----------+               |
                      |  |          |               |
                      +->|Terminated|<--------------+
                         |          |
                         +----------+
                              | 
                              V
                         +----------+
        		 |          |
                         |   Init   |
			 |          |
133 134
          		 +----------+
@endcode			      
135 136
*/			      
			      
137 138 139
/* ---------------------------------------------------------------------- */
/* Session event usage */

Pekka Pessi's avatar
Pekka Pessi committed
140
/** @internal @brief Session-related state. */
141 142
typedef struct nua_session_usage
{
143
  enum nua_callstate ss_state;		/**< Session status (enum nua_callstate) */
144 145 146 147 148 149 150 151
  
  unsigned        ss_100rel:1;	        /**< Use 100rel, send 183 */
  unsigned        ss_alerting:1;	/**< 180 is sent/received */
  
  unsigned        ss_update_needed:2;	/**< Send an UPDATE (do O/A if > 1) */

  unsigned        ss_precondition:1;	/**< Precondition required */

Pekka Pessi's avatar
Pekka Pessi committed
152
  unsigned        ss_reporting:1;       /**< True if reporting state */
153
  unsigned        : 0;
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

  struct session_timer {
    unsigned  interval;		/**< Negotiated expiration time */
    enum nua_session_refresher refresher; /**< Our Negotiated role */

    struct {
      unsigned expires, defaults; /**< Value of Session-Expires (delta) */
      unsigned min_se;	/**< Minimum session expires */
      /** none, local or remote */
      enum nua_session_refresher refresher;
      unsigned    supported:1, require:1, :0;
    } local, remote;

    unsigned      timer_set:1;  /**< We have active session timer. */
  } ss_timer[1];
169

Pekka Pessi's avatar
Pekka Pessi committed
170
  char const     *ss_reason;	        /**< Reason for termination. */
171 172 173

  /* Offer-Answer status */
  char const     *ss_oa_recv, *ss_oa_sent;
174
} nua_session_usage_t;
175

176 177
static char const Offer[] = "offer", Answer[] = "answer";

178
static char const *nua_session_usage_name(nua_dialog_usage_t const *du);
179
static int nua_session_usage_add(nua_handle_t *nh,
180 181
				 nua_dialog_state_t *ds,
				 nua_dialog_usage_t *du);
182
static void nua_session_usage_remove(nua_handle_t *nh,
183 184
				     nua_dialog_state_t *ds,
				     nua_dialog_usage_t *du);
185
static void nua_session_usage_refresh(nua_owner_t *,
186
				      nua_dialog_state_t *,
187 188 189
				      nua_dialog_usage_t *,
				      sip_time_t now);
static int nua_session_usage_shutdown(nua_owner_t *,
190 191
				      nua_dialog_state_t *,
				      nua_dialog_usage_t *);
192

193 194 195
static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags);
static int nua_invite_client_deinit(nua_client_request_t *cr);

196 197
static nua_usage_class const nua_session_usage[1] = {
  {
198
    sizeof (nua_session_usage_t),
199 200 201 202
    sizeof nua_session_usage,
    nua_session_usage_add,
    nua_session_usage_remove,
    nua_session_usage_name,
203 204 205
    NULL,
    nua_session_usage_refresh,
    nua_session_usage_shutdown
206 207 208 209 210 211 212
  }};

static char const *nua_session_usage_name(nua_dialog_usage_t const *du)
{
  return "session";
}

213 214
static
int nua_session_usage_add(nua_handle_t *nh,
215 216 217
			   nua_dialog_state_t *ds,
			   nua_dialog_usage_t *du)
{
218 219
  nua_session_usage_t *ss = nua_dialog_usage_private(du);  

220 221 222
  if (ds->ds_has_session)
    return -1;
  ds->ds_has_session = 1;
223
  ds->ds_got_session = 1;
224

225 226 227
  ss->ss_timer->local.refresher = nua_any_refresher;
  ss->ss_timer->remote.refresher = nua_any_refresher;

228 229 230
  return 0;
}

231 232
static
void nua_session_usage_remove(nua_handle_t *nh,
233 234 235
			       nua_dialog_state_t *ds,
			       nua_dialog_usage_t *du)
{
236
  nua_session_usage_t *ss = nua_dialog_usage_private(du);
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
  nua_client_request_t *cr, *cr_next;

  cr = du->du_cr;

  if (cr && cr->cr_orq && cr->cr_status >= 200) {
    ss->ss_reporting = 1;
    nua_invite_client_ack(cr, NULL);
    ss->ss_reporting = 0;
  }

  /* Destroy queued INVITE transactions */
  for (cr = ds->ds_cr; cr; cr = cr_next) {
    cr_next = cr->cr_next;

    if (cr->cr_method != sip_method_invite)
      continue;
    if (cr == du->du_cr)
      continue;

    nua_stack_event(nh->nh_nua, nh, 
		    NULL,
		    cr->cr_event,
		    SIP_481_NO_TRANSACTION,
		    NULL);

    nua_client_request_destroy(cr);

    cr_next = ds->ds_cr;
  }
266

Pekka Pessi's avatar
Pekka Pessi committed
267
  
268 269 270 271 272 273 274
  ds->ds_has_session = 0;
  nh->nh_has_invite = 0;
  nh->nh_active_call = 0;
  nh->nh_hold_remote = 0;

  if (nh->nh_soa)
    soa_destroy(nh->nh_soa), nh->nh_soa = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
275
}
276

Pekka Pessi's avatar
Pekka Pessi committed
277 278 279 280 281
static
nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
{
  if (ds == ((nua_handle_t *)NULL)->nh_ds)
    return NULL;
282

Pekka Pessi's avatar
Pekka Pessi committed
283
  return nua_dialog_usage_get(ds, nua_session_usage, NULL);
284 285 286
}

static
287
nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds)
288 289 290 291 292 293 294 295 296
{
  nua_dialog_usage_t *du;

  if (ds == ((nua_handle_t *)NULL)->nh_ds)
    return NULL;

  du = nua_dialog_usage_get(ds, nua_session_usage, NULL);

  return (nua_session_usage_t *)nua_dialog_usage_private(du);
297 298
}

Pekka Pessi's avatar
Pekka Pessi committed
299 300 301 302 303 304 305
/** Zap the session associated with the handle */
static
void nua_session_usage_destroy(nua_handle_t *nh,
			       nua_session_usage_t *ss)
{
  /* Remove usage */
  nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss));
306

307
  SU_DEBUG_5(("nua: terminated session %p\n", (void *)nh));
Pekka Pessi's avatar
Pekka Pessi committed
308
}
309

Pekka Pessi's avatar
Pekka Pessi committed
310 311
/* ======================================================================== */
/* INVITE and call (session) processing */
312 313 314 315

int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		    tagi_t const *tags);

316 317 318 319 320 321 322 323
static int session_timer_is_supported(struct session_timer const *t);

static void session_timer_preferences(struct session_timer *t,
				      sip_t const *sip,
				      sip_supported_t const *supported,
				      unsigned expires, int isset,
				      enum nua_session_refresher refresher,
				      unsigned min_se);
Pekka Pessi's avatar
Pekka Pessi committed
324

325 326
static void session_timer_store(struct session_timer *t,
				sip_t const *sip);
Pekka Pessi's avatar
Pekka Pessi committed
327

328 329 330
static int session_timer_check_min_se(msg_t *msg, sip_t *sip,
				      sip_t const *request,
				      unsigned long min_se);
331

332 333 334 335 336 337 338
static int session_timer_add_headers(struct session_timer *t,
				     int initial,
				     msg_t *msg, sip_t *sip);

static void session_timer_negotiate(struct session_timer *t);

static void session_timer_set(nua_session_usage_t *ss);
339

Pekka Pessi's avatar
Pekka Pessi committed
340 341 342
static int session_timer_check_restart(nua_client_request_t *cr,
				       int status, char const *phrase,
				       sip_t const *sip);
343

344 345 346
static int nh_referral_check(nua_handle_t *nh, tagi_t const *tags);
static void nh_referral_respond(nua_handle_t *,
				int status, char const *phrase);
347

348
static void signal_call_state_change(nua_handle_t *nh,
Pekka Pessi's avatar
Pekka Pessi committed
349 350 351
				      nua_session_usage_t *ss,
				      int status, char const *phrase,
				      enum nua_callstate next_state);
352 353

static
354
int session_get_description(sip_t const *sip,
355
			    char const **return_sdp,
356 357 358
			    size_t *return_len);

static
359
int session_include_description(soa_session_t *soa,
360
				int session,
361
				msg_t *msg,
362 363 364
				sip_t *sip);

static
365 366
int session_make_description(su_home_t *home,
			     soa_session_t *soa,
367
			     int session,
368 369 370 371
			     sip_content_disposition_t **return_cd,
			     sip_content_type_t **return_ct,
			     sip_payload_t **return_pl);

372
static
373 374 375
int nua_server_retry_after(nua_server_request_t *sr,
			   int status, char const *phrase,
			   int min, int max);
376

377
/**@fn void nua_invite(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
378 379 380 381 382 383 384
 *
 * Place a call using SIP INVITE method. 
 *
 * Incomplete call can be hung-up with nua_cancel(). Complete or incomplete
 * calls can be hung-up with nua_bye().
 *
 * Optionally 
385
 * - uses early media if NUTAG_EARLY_MEDIA() tag is used with non zero-value
386 387 388 389 390 391 392 393 394 395 396 397
 * - media parameters can be set by SOA tags
 * - nua_invite() can be used to change status of an existing call: 
 *   - #SOATAG_HOLD tag can be used to list the media that will be put on hold,
 *     the value "*" sets all the media beloginging to the session on hold
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
398 399 400
 *    NUTAG_URL() \n
 *    Tags of nua_set_hparams() \n
 *    NUTAG_INCLUDE_EXTRA_SDP() \n
401 402 403
 *    SOATAG_HOLD(), SOATAG_AF(), SOATAG_ADDRESS(),
 *    SOATAG_RTP_SELECT(), SOATAG_RTP_SORT(), SOATAG_RTP_MISMATCH(), 
 *    SOATAG_AUDIO_AUX(), \n
404
 *    SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() \n
405
 *    See use of tags in <sip_tag.h> below
406 407 408
 *
 * @par Events:
 *    #nua_r_invite \n
409
 *    #nua_i_state (#nua_i_active, #nua_i_terminated) \n
410 411 412 413 414 415 416 417
 *    #nua_i_media_error \n
 *    #nua_i_fork \n
 *
 * @par Populating SIP Request Message with Tagged Arguments
 * The tagged arguments can be used to pass values for any SIP headers to
 * the stack. When the INVITE message (or any other SIP message) is created,
 * the tagged values saved with nua_handle() are used first, next the tagged
 * values given with the operation (nua_invite()) are added.
418 419
 *
 * @par
420 421 422 423 424 425 426
 * When multiple tags for the same header are specified, the behaviour
 * depends on the header type. If only a single header field can be included
 * in a SIP message, the latest non-NULL value is used, e.g., @Subject. 
 * However, if the SIP header can consist of multiple lines or header fields
 * separated by comma, e.g., @Accept, all the tagged
 * values are concatenated.
 * 
427
 * @par
428 429 430
 * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), the
 * values from previous tags are ignored.
 *
431
 * @par
432 433 434 435
 * Next, values previously set with nua_set_params() or nua_set_hparams()
 * are used: @Allow, @Supported, @Organization, and @UserAgent headers are
 * added to the request if they are not already set.
 *
436
 * @par
437 438
 * Now, the target URI for the request needs to be determined.
 *
439
 * @par
440 441 442 443 444 445 446 447 448 449
 * For initial INVITE requests, values from tags are used. If NUTAG_URL() is
 * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given, it
 * is used as target URI. If neither is given, the complete request line
 * already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR() is used. 
 * If none of the tags above are given, an internal error is returned to the
 * application. At this point, the target URI is stored in the request line,
 * together with method name ("INVITE") and protocol version ("SIP/2.0"). 
 * The initial dialog information is also created: @CallID, @CSeq headers
 * are generated, if they do not exist, and tag is added to @From header.
 *
450
 * @par
451 452 453 454 455 456 457
 * For in-dialog INVITE (re-INVITE), the request URI is taken from the
 * @Contact header received from the remote party during the dialog
 * establishment. Also, the @CallID and @CSeq headers and @From and @To tags
 * are generated based on the dialog information and added to the request. 
 * If the dialog has a route (set by @RecordRoute headers), it is added to
 * the request, too.
 *
458
 * @par
459 460 461
 * @MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is
 * also added now, if it does not exist.
 * 
462
 * @par
463 464 465 466 467 468 469 470
 * Next, the stack generates a @Contact header for the request (Unless the
 * application already gave a @Contact header or it does not want to use
 * @Contact and indicates that by including SIPTAG_CONTACT(NULL) or
 * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the application
 * has registered the URI in @From header, the @Contact header used with
 * registration is used. Otherwise, the @Contact header is generated from the
 * local IP address and port number.
 *
471
 * @par
472 473 474
 * For the initial INVITE requests, @ServiceRoute set received from
 * the registrar is also added to the request message.
 *
475
 * @par
476 477
 * The INVITE request message created by nua_invite() operation is saved as
 * a template for automatic re-INVITE requests sent by the session timer
Pekka Pessi's avatar
Pekka Pessi committed
478 479 480 481 482
 * ("timer") feature (see NUTAG_SESSION_TIMER() for more details). Please
 * note that the template message is not used when ACK, PRACK, UPDATE or
 * INFO requests are created (however, these requests will include
 * dialog-specific headers like @To, @From, and @CallID as well as
 * preference headers @Allow, @Supported, @UserAgent, @Organization).
483 484 485 486 487 488 489
 *
 * @par SDP Handling
 * The initial nua_invite() creates a @ref soa_session_t "soa media session"
 * unless NUTAG_MEDIA_ENABLE(0) has been given. The SDP description of the
 * @ref soa_session_t "soa media session" is included in the INVITE request
 * as message body. 
 *
490
 * @par
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
 * The SDP in a 1XX or 2XX response message is interpreted as an answer,
 * given to the @ref soa_session_t "soa media session" object for
 * processing.
 *
 * @bug If the INVITE request already contains a message body, SDP is not
 * added. Also, if the response contains a multipart body, it is not parsed.
 *
 * @par Authentication
 * The INVITE request may need authentication. Each proxy or server
 * requiring authentication can respond with 401 or 407 response. The
 * nua_authenticate() operation stores authentication information (username
 * and password) to the handle, and stack tries to authenticate all the rest
 * of the requests (e.g., PRACK, ACK, UPDATE, re-INVITE, BYE) using same
 * username and password.
 *
506 507
 * @sa @ref nua_call_model, #nua_r_invite, #nua_i_state, \n
 *     nua_handle_has_active_call() \n
508 509 510 511 512 513 514 515
 *     nua_handle_has_call_on_hold()\n
 *     nua_handle_has_invite() \n
 *     nua_authenticate() \n
 *     nua_prack() \n
 *     nua_update() \n
 *     nua_info() \n 
 *     nua_cancel() \n
 *     nua_bye() \n
516
 *     #nua_i_invite, nua_respond()
517 518
 */

519 520 521
/* Tags not implemented
 *    NUTAG_REFER_PAUSE() \n
 */
522

Pekka Pessi's avatar
Pekka Pessi committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
static int nua_invite_client_init(nua_client_request_t *cr, 
				  msg_t *msg, sip_t *sip,
				  tagi_t const *tags);
static int nua_invite_client_request(nua_client_request_t *cr,
				     msg_t *msg, sip_t *sip,
				     tagi_t const *tags);
static int nua_invite_client_preliminary(nua_client_request_t *cr,
					 int status, char const *phrase,
					 sip_t const *sip);
static int nua_invite_client_response(nua_client_request_t *cr,
				      int status, char const *phrase,
				      sip_t const *sip);
static int nua_session_client_response(nua_client_request_t *cr,
				       int status, char const *phrase,
				       sip_t const *sip);
static int nua_invite_client_report(nua_client_request_t *cr,
				    int status, char const *phrase,
				    sip_t const *sip,
				    nta_outgoing_t *orq,
				    tagi_t const *tags);

544
nua_client_methods_t const nua_invite_client_methods = {
Pekka Pessi's avatar
Pekka Pessi committed
545 546 547 548 549 550 551 552 553 554 555 556 557
  SIP_METHOD_INVITE,
  0,
  { 
    /* create_dialog */ 1,
    /* in_dialog */ 1,
    /* target refresh */ 1
  },
  NULL,
  nua_invite_client_init,
  nua_invite_client_request,
  session_timer_check_restart,
  nua_invite_client_response,
  nua_invite_client_preliminary,
558 559
  nua_invite_client_report,
  nua_invite_client_deinit
Pekka Pessi's avatar
Pekka Pessi committed
560 561
};

562 563 564 565 566
extern nua_client_methods_t const nua_bye_client_methods;
extern nua_client_methods_t const nua_cancel_client_methods;
extern nua_client_methods_t const nua_info_client_methods;
extern nua_client_methods_t const nua_update_client_methods;
extern nua_client_methods_t const nua_prack_client_methods;
Pekka Pessi's avatar
Pekka Pessi committed
567 568 569 570 571

int nua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		     tagi_t const *tags)
{
  return nua_client_create(nh, e, &nua_invite_client_methods, tags);
572 573
}

Pekka Pessi's avatar
Pekka Pessi committed
574 575 576
static int nua_invite_client_init(nua_client_request_t *cr, 
				  msg_t *msg, sip_t *sip,
				  tagi_t const *tags)
577
{
Pekka Pessi's avatar
Pekka Pessi committed
578
  nua_handle_t *nh = cr->cr_owner;
579
  nua_dialog_usage_t *du;
580
  nua_session_usage_t *ss;
581

Pekka Pessi's avatar
Pekka Pessi committed
582
  cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds);
583
  /* Errors returned by nua_invite_client_init() 
584
     do not change the session state */
585
  cr->cr_neutral = 1;	
Pekka Pessi's avatar
Pekka Pessi committed
586 587 588 589 590 591
  
  if (nh_is_special(nh) || 
      nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error))
    return nua_client_return(cr, 900, "Invalid handle for INVITE", msg);
  else if (nh_referral_check(nh, tags) < 0)
    return nua_client_return(cr, 900, "Invalid referral", msg);
592

593 594 595 596 597 598 599 600 601
  if (du) {
    nua_server_request_t *sr;
    for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
      /* INVITE in progress? */
      if (sr->sr_usage == du && sr->sr_method == sip_method_invite &&
	  nua_server_request_is_pending(sr))
	return nua_client_return(cr, SIP_491_REQUEST_PENDING, msg);
  }
  else
Pekka Pessi's avatar
Pekka Pessi committed
602
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL);
603

Pekka Pessi's avatar
Pekka Pessi committed
604 605
  if (!du)
    return -1;
606

Pekka Pessi's avatar
Pekka Pessi committed
607 608
  if (nua_client_bind(cr, du) < 0)
    return nua_client_return(cr, 900, "INVITE already in progress", msg);
609

610 611 612 613 614
  ss = nua_dialog_usage_private(du);

  session_timer_preferences(ss->ss_timer,
			    sip,
			    NH_PGET(nh, supported),		     
Pekka Pessi's avatar
Pekka Pessi committed
615
			    NH_PGET(nh, session_timer),
616 617 618
			    NUA_PISSET(nh->nh_nua, nh, session_timer),
			    NH_PGET(nh, refresher),
			    NH_PGET(nh, min_se));
619

620 621
  cr->cr_neutral = 0;

Pekka Pessi's avatar
Pekka Pessi committed
622 623
  return 0;
}
624

Pekka Pessi's avatar
Pekka Pessi committed
625 626 627 628 629 630 631 632 633
static int nua_invite_client_request(nua_client_request_t *cr,
				     msg_t *msg, sip_t *sip,
				     tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
  nua_session_usage_t *ss = nua_dialog_usage_private(du);
  int offer_sent = 0, retval;
  sip_time_t invite_timeout;
634

Pekka Pessi's avatar
Pekka Pessi committed
635 636
  if (du == NULL)		/* Call terminated */ 
    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);
637

Pekka Pessi's avatar
Pekka Pessi committed
638
  assert(ss);
639

Pekka Pessi's avatar
Pekka Pessi committed
640 641 642
  invite_timeout = NH_PGET(nh, invite_timeout);
  if (invite_timeout == 0)
    invite_timeout = UINT_MAX;
643
  /* Send CANCEL if we don't get response within timeout*/
644
  /* nua_dialog_usage_set_expires(du, invite_timeout); Xyzzy */
645
  nua_dialog_usage_reset_refresh(du);
646

Pekka Pessi's avatar
Pekka Pessi committed
647
  /* Add session timer headers */
648 649 650
  if (session_timer_is_supported(ss->ss_timer))
    session_timer_add_headers(ss->ss_timer, ss->ss_state == nua_callstate_init,
			      msg, sip);
651

Pekka Pessi's avatar
Pekka Pessi committed
652 653 654 655
  ss->ss_100rel = NH_PGET(nh, early_media);
  ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition");
  if (ss->ss_precondition)
    ss->ss_update_needed = ss->ss_100rel = 1;
656 657

  if (nh->nh_soa) {
658 659 660
    soa_init_offer_answer(nh->nh_soa);

    if (sip->sip_payload)
Pekka Pessi's avatar
Pekka Pessi committed
661
      offer_sent = 0;		/* XXX - kludge */
662
    else if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
Pekka Pessi's avatar
Pekka Pessi committed
663
      return -1;
664 665 666
    else
      offer_sent = 1;

667 668 669
    if (offer_sent > 0 &&
	session_include_description(nh->nh_soa, 1, msg, sip) < 0)
      return nua_client_return(cr, 900, "Internal media error", msg);
670

671 672 673 674 675
    if (NH_PGET(nh, media_features) &&
	!nua_dialog_is_established(nh->nh_ds) &&
	!sip->sip_accept_contact && !sip->sip_reject_contact) {
      sip_accept_contact_t ac[1];
      sip_accept_contact_init(ac);
676

677 678
      ac->cp_params = (msg_param_t *)
	soa_media_features(nh->nh_soa, 1, msg_home(msg));
679

680 681 682 683
      if (ac->cp_params) {
	msg_header_replace_param(msg_home(msg), ac->cp_common, "explicit");
	sip_add_dup(msg, sip, (sip_header_t *)ac);
      }
684
    }
Pekka Pessi's avatar
Pekka Pessi committed
685
  }
686 687 688
  else {
    offer_sent = session_get_description(sip, NULL, NULL);
  }
689

Pekka Pessi's avatar
Pekka Pessi committed
690 691 692 693
  retval = nua_base_client_trequest(cr, msg, sip,
				    NTATAG_REL100(ss->ss_100rel),
				    TAG_NEXT(tags));
  if (retval == 0) {
694 695
    if ((cr->cr_offer_sent = offer_sent))
      ss->ss_oa_sent = Offer;
696

697
    if (!cr->cr_restarting) /* Restart logic calls nua_invite_client_report */
Pekka Pessi's avatar
Pekka Pessi committed
698 699 700
      signal_call_state_change(nh, ss, 0, "INVITE sent", 
			       nua_callstate_calling);
  }
701

Pekka Pessi's avatar
Pekka Pessi committed
702 703
  return retval;
}
704

Pekka Pessi's avatar
Pekka Pessi committed
705 706 707 708 709 710
static int nua_invite_client_response(nua_client_request_t *cr,
				      int status, char const *phrase,
				      sip_t const *sip)
{
  nua_dialog_usage_t *du = cr->cr_usage;
  nua_session_usage_t *ss = nua_dialog_usage_private(du);
711

Pekka Pessi's avatar
Pekka Pessi committed
712 713
  if (ss == NULL || sip == NULL) {
    /* Xyzzy */
714
  }
Pekka Pessi's avatar
Pekka Pessi committed
715 716
  else if (status < 300) {
    du->du_ready = 1;
717

718 719 720 721
    if (session_timer_is_supported(ss->ss_timer))
      session_timer_store(ss->ss_timer, sip);

    session_timer_set(ss);
Pekka Pessi's avatar
Pekka Pessi committed
722 723 724 725
  }
  
  return nua_session_client_response(cr, status, phrase, sip);
}
726

Pekka Pessi's avatar
Pekka Pessi committed
727 728 729 730 731 732 733
static int nua_invite_client_preliminary(nua_client_request_t *cr,
					 int status, char const *phrase,
					 sip_t const *sip)
{
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
  nua_session_usage_t *ss = nua_dialog_usage_private(du);
734

Pekka Pessi's avatar
Pekka Pessi committed
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
  assert(sip); assert(ss);

  if (ss && sip && sip->sip_rseq) {
    /* Handle 100rel responses */
    sip_rseq_t *rseq = sip->sip_rseq;

    /* Establish early dialog - we should fork here */
    if (!nua_dialog_is_established(nh->nh_ds)) {
      nta_outgoing_t *tagged;

      nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
      nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
      
      /* Tag the INVITE request */
      tagged = nta_outgoing_tagged(cr->cr_orq,
				   nua_client_orq_response, cr,
				   sip->sip_to->a_tag, sip->sip_rseq);
      if (tagged) {
	nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = tagged;
      }
      else {
	cr->cr_graceful = 1;
	ss->ss_reason = "SIP;cause=500;text=\"Cannot Create Early Dialog\"";
      }
    }
  
    if (!rseq) {
762
      SU_DEBUG_5(("nua(%p): 100rel missing RSeq\n", (void *)nh));
Pekka Pessi's avatar
Pekka Pessi committed
763 764
    }
    else if (nta_outgoing_rseq(cr->cr_orq) > rseq->rs_response) {
765
      SU_DEBUG_5(("nua(%p): 100rel bad RSeq %u (got %u)\n", (void *)nh, 
Pekka Pessi's avatar
Pekka Pessi committed
766 767 768 769 770
		  (unsigned)rseq->rs_response,
		  nta_outgoing_rseq(cr->cr_orq)));
      return 1;    /* Do not send event */
    }
    else if (nta_outgoing_setrseq(cr->cr_orq, rseq->rs_response) < 0) {
771
      SU_DEBUG_1(("nua(%p): cannot set RSeq %u\n", (void *)nh, 
Pekka Pessi's avatar
Pekka Pessi committed
772 773 774 775 776
		  (unsigned)rseq->rs_response));
      cr->cr_graceful = 1;
      ss->ss_reason = "SIP;cause=400;text=\"Bad RSeq\"";
    }
  }
777

Pekka Pessi's avatar
Pekka Pessi committed
778
  return nua_session_client_response(cr, status, phrase, sip);
779 780
}

Pekka Pessi's avatar
Pekka Pessi committed
781 782 783 784
/** Process response to a session request (INVITE, PRACK, UPDATE) */
static int nua_session_client_response(nua_client_request_t *cr,
				       int status, char const *phrase,
				       sip_t const *sip)
785
{
Pekka Pessi's avatar
Pekka Pessi committed
786 787 788 789 790 791
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
  nua_session_usage_t *ss = nua_dialog_usage_private(du);

  char const *sdp = NULL;
  size_t len;
792 793
  char const *received = NULL;

Pekka Pessi's avatar
Pekka Pessi committed
794 795
#define LOG3(m) \
  SU_DEBUG_3(("nua(%p): %s: %s %s in %u %s\n", \
796
	      (void *)nh, cr->cr_method_name, (m),		\
Pekka Pessi's avatar
Pekka Pessi committed
797 798 799
	      received ? received : "SDP", status, phrase))
#define LOG5(m) \
  SU_DEBUG_5(("nua(%p): %s: %s %s in %u %s\n", \
800
	      (void *)nh, cr->cr_method_name, (m), received, status, phrase))
801

802
  if (!ss || !sip || 300 <= status)
Pekka Pessi's avatar
Pekka Pessi committed
803 804 805 806 807 808 809
    /* Xyzzy */;
  else if (!session_get_description(sip, &sdp, &len))
    /* No SDP */;
  else if (cr->cr_answer_recv) {
    /* Ignore spurious answers after completing O/A */
    LOG3("ignoring duplicate");
    sdp = NULL;
810
  }
Pekka Pessi's avatar
Pekka Pessi committed
811
  else if (cr->cr_offer_sent) {
812
    /* case 1: answer to our offer */
Pekka Pessi's avatar
Pekka Pessi committed
813
    cr->cr_answer_recv = status;
814
    received = Answer;
815

816 817 818
    if (nh->nh_soa == NULL)
      LOG5("got SDP");
    else if (soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
      LOG3("error parsing SDP");
      sdp = NULL;
      cr->cr_graceful = 1;
      ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\"";
    }
    else if (soa_process_answer(nh->nh_soa, NULL) < 0) {
      LOG5("error processing SDP");
      /* XXX */
      sdp = NULL;
    }
    else if (soa_activate(nh->nh_soa, NULL) < 0)
      /* XXX - what about errors? */
      LOG3("error activating media after");
    else
      LOG5("processed SDP");
  }
  else if (cr->cr_method != sip_method_invite) {
    /* If non-invite request did not have offer, ignore SDP in response */
    LOG3("ignoring extra");
    sdp = NULL;
  }
  else {
841
    /* case 2: new offer */
Pekka Pessi's avatar
Pekka Pessi committed
842
    cr->cr_offer_recv = 1, cr->cr_answer_sent = 0;
843
    received = Offer;
844

845
    if (nh->nh_soa && soa_set_remote_sdp(nh->nh_soa, NULL, sdp, len) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
846 847 848 849
      LOG3("error parsing SDP");
      sdp = NULL;
      cr->cr_graceful = 1;
      ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\"";
850
    }
Pekka Pessi's avatar
Pekka Pessi committed
851 852
    else 
      LOG5("got SDP");
853
  }
854

Pekka Pessi's avatar
Pekka Pessi committed
855 856
  if (ss && received)
    ss->ss_oa_recv = received;
857

858
  if (sdp && nh->nh_soa)
Pekka Pessi's avatar
Pekka Pessi committed
859 860 861 862 863 864
    return nua_base_client_tresponse(cr, status, phrase, sip,
				     NH_REMOTE_MEDIA_TAGS(1, nh->nh_soa),
				     TAG_END());
  else
    return nua_base_client_response(cr, status, phrase, sip, NULL);
}
865

Pekka Pessi's avatar
Pekka Pessi committed
866 867 868 869 870 871 872
static int nua_invite_client_report(nua_client_request_t *cr,
				    int status, char const *phrase,
				    sip_t const *sip,
				    nta_outgoing_t *orq,
				    tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
873
  nua_dialog_state_t *ds = nh->nh_ds;
Pekka Pessi's avatar
Pekka Pessi committed
874 875 876 877
  nua_dialog_usage_t *du = cr->cr_usage;
  nua_session_usage_t *ss = nua_dialog_usage_private(du);
  unsigned next_state;
  int error;
878

879
  nh_referral_respond(nh, status, phrase); /* XXX - restarting after 401/407 */
Pekka Pessi's avatar
Pekka Pessi committed
880 881 882 883 884

  nua_stack_event(nh->nh_nua, nh, 
		  nta_outgoing_getresponse(orq),
		  cr->cr_event,
		  status, phrase,
885
		  tags);
886

887 888
  if (cr->cr_waiting)
    /* Do not report call state change if waiting for restart */
Pekka Pessi's avatar
Pekka Pessi committed
889
    return 1;
890

891 892 893 894 895
  if (ss == NULL) {
    signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated);
    return 1;
  }

896 897
  ss->ss_reporting = 1;

898 899 900 901
  if (cr->cr_neutral) {
    signal_call_state_change(nh, ss, status, phrase, ss->ss_state);
    ss->ss_reporting = 0;
    return 1;
902
  }
903

904 905 906 907
  if (orq != cr->cr_orq && cr->cr_orq) {	/* Being restarted */
    next_state = nua_callstate_calling;
  }
  else if (status == 100) {
Pekka Pessi's avatar
Pekka Pessi committed
908 909 910 911 912 913 914 915 916 917
    next_state = nua_callstate_calling;
  }
  else if (status < 300 && cr->cr_graceful) {
    next_state = nua_callstate_terminating;
    if (200 <= status) {
      nua_invite_client_ack(cr, NULL);
    }
  }
  else if (status < 200) {
    next_state = nua_callstate_proceeding;
918 919 920

    if (sip && sip->sip_rseq && 
	!SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_prack)) {
Pekka Pessi's avatar
Pekka Pessi committed
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
      sip_rack_t rack[1];

      sip_rack_init(rack);
      rack->ra_response    = sip->sip_rseq->rs_response;
      rack->ra_cseq        = sip->sip_cseq->cs_seq;
      rack->ra_method      = sip->sip_cseq->cs_method;
      rack->ra_method_name = sip->sip_cseq->cs_method_name;

      error = nua_client_tcreate(nh, nua_r_prack, &nua_prack_client_methods, 
				 SIPTAG_RACK(rack),
				 TAG_END());
      if (error < 0) {
	cr->cr_graceful = 1;
	next_state = nua_callstate_terminating;
      }
    }
  }
  else if (status < 300) {
    next_state = nua_callstate_completing;
  }
  else if (cr->cr_terminated) {
    next_state = nua_callstate_terminated;
  }
  else if (cr->cr_graceful && ss->ss_state >= nua_callstate_completing) {
    next_state = nua_callstate_terminating;
946 947
  }
  else {
Pekka Pessi's avatar
Pekka Pessi committed
948
    next_state = nua_callstate_init;
949 950
  }

Pekka Pessi's avatar
Pekka Pessi committed
951
  if (next_state == nua_callstate_calling) {
952 953
    if (sip && sip->sip_status && sip->sip_status->st_status == 100) {
      ss->ss_reporting = 0;
Pekka Pessi's avatar
Pekka Pessi committed
954
      return 1;
955
    }
Pekka Pessi's avatar
Pekka Pessi committed
956
  }
957

Pekka Pessi's avatar
Pekka Pessi committed
958 959 960 961 962
  if (next_state == nua_callstate_completing) {
    if (NH_PGET(nh, auto_ack) ||
	/* Auto-ACK response to re-INVITE unless auto_ack is set to 0 */
	(ss->ss_state == nua_callstate_ready &&
	 !NH_PISSET(nh, auto_ack))) {
963
      nua_client_request_t *cru;
964

965 966 967 968 969 970 971 972
      for (cru = ds->ds_cr; cru; cru = cru->cr_next) {
	if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv)
	  break;
      }

      if (cru)
	/* A final response to UPDATE or PRACK with answer on its way? */;
      else if (nua_invite_client_ack(cr, NULL) > 0)
Pekka Pessi's avatar
Pekka Pessi committed
973 974 975 976 977
	next_state = nua_callstate_ready;
      else
	next_state = nua_callstate_terminating;
    }
  }
978

Pekka Pessi's avatar
Pekka Pessi committed
979
  if (next_state == nua_callstate_terminating) {
980 981 982 983 984 985 986 987 988
    /* Send BYE or CANCEL */
    /* XXX - Forking - send BYE to early dialog?? */
    if (ss->ss_state > nua_callstate_proceeding || status >= 200)
      error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL);
    else
      error = nua_client_create(nh, nua_r_cancel, 
				&nua_cancel_client_methods, tags);

    if (error) {
Pekka Pessi's avatar
Pekka Pessi committed
989 990 991
      next_state = nua_callstate_terminated;
      cr->cr_terminated = 1;
    }
992
    cr->cr_graceful = 0;
993 994
  }

995 996
  ss->ss_reporting = 0;

Pekka Pessi's avatar
Pekka Pessi committed
997 998 999
  signal_call_state_change(nh, ss, status, phrase, next_state);

  return 1;
1000 1001
}

1002 1003 1004 1005 1006
/**@fn void nua_ack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
 *
 * Acknowledge a succesful response to INVITE request.
 *
 * Acknowledge a successful response (200..299) to INVITE request with the
Pekka Pessi's avatar
Pekka Pessi committed
1007
 * SIP ACK request message. This function is needed only if NUTAG_AUTOACK()
1008 1009 1010 1011 1012 1013 1014 1015 1016
 * parameter has been cleared.
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
1017
 *    Header tags defined in <sofia-sip/sip_tag.h>
1018 1019 1020
 *
 * @par Events:
 *    #nua_i_media_error \n
1021
 *    #nua_i_state  (#nua_i_active, #nua_i_terminated) 
1022 1023
 *
 * @sa NUTAG_AUTOACK(), @ref nua_call_model, #nua_i_state
1024 1025
 */

1026 1027
int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		  tagi_t const *tags)
1028
{
Pekka Pessi's avatar