nua_subnotref.c 24.8 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_subnotref.c
26 27
 * @brief Subscriber (event watcher)
 *
28 29 30 31
 * This file contains implementation SUBSCRIBE UAC, NOTIFY UAS, REFER UAC.
 * The implementation of SUBSCRIBE UAS, NOTIFY UAC and REFER UAS is in
 * nua_notifier.c.
 * Alternative implementation using nea is in nua_event_server.c.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Wed Mar  8 15:10:08 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>
50
#include <sofia-sip/sip_extra.h>
51
#include <sofia-sip/sip_util.h>
52
#include <sofia-sip/su_uniqueid.h>
53

54
#include "nua_stack.h"
55 56

/* ---------------------------------------------------------------------- */
57
/* Subcriber event usage */
58 59 60

struct event_usage
{
61
  enum nua_substate eu_substate;	/**< Subscription state */
62
  sip_time_t eu_expires;	        /**< Proposed expiration time */
63
  unsigned eu_notified;		        /**< Number of NOTIFYs received */
64
  unsigned eu_refer:1;		        /**< Implied subscription by refer */
65 66
  unsigned eu_final_wait:1;	        /**< Waiting for final NOTIFY */
  unsigned eu_no_id:1;		        /**< Do not use "id" (even if we have one) */
67 68 69 70 71 72 73 74 75
};

static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du);
static int nua_subscribe_usage_add(nua_handle_t *nh, 
				   nua_dialog_state_t *ds,
				   nua_dialog_usage_t *du);
static void nua_subscribe_usage_remove(nua_handle_t *nh, 
				       nua_dialog_state_t *ds,
				       nua_dialog_usage_t *du);
76
static void nua_subscribe_usage_refresh(nua_handle_t *,
77
					nua_dialog_state_t *,
78 79 80
					nua_dialog_usage_t *,
					sip_time_t);
static int nua_subscribe_usage_shutdown(nua_handle_t *,
81
					nua_dialog_state_t *,
82
					nua_dialog_usage_t *);
83 84 85 86 87 88 89

static nua_usage_class const nua_subscribe_usage[1] = {
  {
    sizeof (struct event_usage), (sizeof nua_subscribe_usage),
    nua_subscribe_usage_add,
    nua_subscribe_usage_remove,
    nua_subscribe_usage_name,
90 91 92
    NULL,
    nua_subscribe_usage_refresh,
    nua_subscribe_usage_shutdown
93 94 95 96 97 98 99 100 101 102 103 104 105 106
  }};

static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du)
{
  return "subscribe";
}

static 
int nua_subscribe_usage_add(nua_handle_t *nh, 
			   nua_dialog_state_t *ds,
			   nua_dialog_usage_t *du)
{
  ds->ds_has_events++;
  ds->ds_has_subscribes++;
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
  return 0;
}

static 
void nua_subscribe_usage_remove(nua_handle_t *nh, 
			       nua_dialog_state_t *ds,
			       nua_dialog_usage_t *du)
{
  ds->ds_has_events--;	
  ds->ds_has_subscribes--;	
}

/* ====================================================================== */
/* SUBSCRIBE */

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/** Subscribe to a SIP event. 
 *
 * Subscribe a SIP event using the SIP SUBSCRIBE request. If the 
 * SUBSCRBE is successful a subscription state is established and 
 * the subscription is refreshed regularly. The refresh requests will
 * generate #nua_r_subscribe events.
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
 *    NUTAG_URL()
 *    Tags in <sip_tag.h>
 *
 * @par Events:
 *    #nua_r_subscribe \n
 *    #nua_i_notify
 *
 * @sa NUTAG_SUBSTATE(), @RFC3265
 */

/** Unsubscribe an event. 
 *
 * Unsubscribe an active or pending subscription with SUBSCRIBE request 
 * containing Expires: header with value 0. The dialog associated with 
 * subscription will be destroyed if there is no other subscriptions or 
 * call using this dialog.
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
 *    SIPTAG_EVENT() or SIPTAG_EVENT_STR() \n
 *    Tags in <sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR()
 *
 * @par Events:
 *    #nua_r_unsubscribe 
 *
 * @sa NUTAG_SUBSTATE(), @RFC3265
 */

170 171 172 173 174 175 176 177
static int nua_subscribe_client_init(nua_client_request_t *cr, 
				     msg_t *, sip_t *,
				     tagi_t const *tags);
static int nua_subscribe_client_request(nua_client_request_t *cr,
					msg_t *, sip_t *,
					tagi_t const *tags);
static int nua_subscribe_client_response(nua_client_request_t *cr,
					 int status, char const *phrase,
178 179
					 sip_t const *sip);

180
static nua_client_methods_t const nua_subscribe_client_methods = {
181 182 183 184 185 186 187 188 189 190 191 192 193
  SIP_METHOD_SUBSCRIBE,
  0,
  { 
    /* create_dialog */ 1,
    /* in_dialog */ 1,
    /* target refresh */ 1
  },
  NULL,
  nua_subscribe_client_init,
  nua_subscribe_client_request,
  /* nua_subscribe_client_check_restart */ NULL,
  nua_subscribe_client_response
};
194

195
int
196 197
nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		    tagi_t const *tags)
198
{
199 200
  return nua_client_create(nh, e, &nua_subscribe_client_methods, tags);
}
201

202 203 204 205 206 207 208
static int nua_subscribe_client_init(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;
  sip_event_t *o = sip->sip_event;
209

210
  du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
211

212 213
  if (du == NULL && o == NULL)
    du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
214

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
  if (du) {
    if (du->du_event && o == NULL)
      /* Add Event header */
      sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
  }
  else if (cr->cr_event == nua_r_subscribe) {	
    /* Create dialog usage */
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, o);
    /* Note that we allow SUBSCRIBE without event */
  }

  cr->cr_usage = du;

  return 0;
}

static int nua_subscribe_client_request(nua_client_request_t *cr,
					msg_t *msg, sip_t *sip,
					tagi_t const *tags)
{
  nua_dialog_usage_t *du = cr->cr_usage; 
  sip_time_t expires = 0;

  if (cr->cr_event != nua_r_subscribe ||
      (du && du->du_shutdown) ||
      (sip->sip_expires && sip->sip_expires->ex_delta == 0))
    cr->cr_terminating = 1;
242

243 244 245 246 247 248 249 250 251 252
  if (du) {
    struct event_usage *eu = nua_dialog_usage_private(du);
    sip_event_t *o = sip->sip_event;

    if (nua_client_bind(cr, du) < 0)
      return -1;

    if (eu->eu_no_id && o && o->o_id) {
      /* Notifier does not handle id properly, remove it */
      msg_header_remove_param(o->o_common, "id");
253
    }
254 255 256 257

#if 0
    if (cr->cr_terminating) {
      /* Already terminated subscription? */
258 259
      if (eu->eu_substate == nua_substate_terminated ||
	  eu->eu_substate == nua_substate_embryonic) {
260
	return nua_client_return(cr, SIP_200_OK, msg);
261
      }
262
    }
263
#endif
264

265 266 267 268 269 270 271
    nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */
    
    if (cr->cr_terminating)
      expires = eu->eu_expires = 0;
    else if (sip->sip_expires)
      /* Use value specified by application or negotiated with Min-Expires */
      expires = eu->eu_expires = sip->sip_expires->ex_delta;
272
    else
273 274 275 276
    /* We just use common default value, but the default is actually
       package-specific according to the RFC 3265 section 4.4.4:
       [Event] packages MUST also define a
       default "Expires" value to be used if none is specified. */
277
      expires = eu->eu_expires = 3600;
278

279
    eu->eu_final_wait = 0;
280

281 282 283
    if (eu->eu_substate == nua_substate_terminated)
      eu->eu_substate = nua_substate_embryonic;
  }
284

285 286 287 288 289
  if (!sip->sip_expires || sip->sip_expires->ex_delta != expires) {
    sip_expires_t ex[1];
    sip_expires_init(ex)->ex_delta = expires;
    sip_add_dup(msg, sip, (sip_header_t *)ex);
  }
290

291
  return nua_base_client_request(cr, msg, sip, tags);
292 293
}

294
/** @NUA_EVENT nua_r_subscribe
295
 *
296
 * Response to an outgoing SUBSCRIBE request.
297 298 299 300
 *
 * The SUBSCRIBE request may have been sent explicitly by nua_subscribe() or
 * implicitly by NUA state machine.
 *
301 302 303 304 305 306 307
 * @param status response status code
 *               (if the request is retried, @a status is 100, the @a
 *               sip->sip_status->st_status contain the real status code
 *               from the response message, e.g., 302, 401, or 407)
 * @param phrase a short textual description of @a status code
 * @param nh     operation handle associated with the subscription
 * @param hmagic application context associated with the handle
308
 * @param sip    response to SUBSCRIBE request or NULL upon an error
309 310
 *               (status code is in @a status and 
 *                descriptive message in @a phrase parameters)
311 312 313
 * @param tags   NUTAG_SUBSTATE()
 *
 * @sa nua_subscribe(), @RFC3265
314 315
 *
 * @END_NUA_EVENT
316 317
 */

318
/** @NUA_EVENT nua_r_unsubscribe
319 320 321
 *
 * Response to an outgoing un-SUBSCRIBE.
 *
322 323 324 325 326 327 328
 * @param status response status code
 *               (if the request is retried, @a status is 100, the @a
 *               sip->sip_status->st_status contain the real status code
 *               from the response message, e.g., 302, 401, or 407)
 * @param phrase a short textual description of @a status code
 * @param nh     operation handle associated with the subscription
 * @param hmagic application context associated with the handle
329
 * @param sip    response to SUBSCRIBE request or NULL upon an error
330 331
 *               (status code is in @a status and 
 *                descriptive message in @a phrase parameters)
332 333 334
 * @param tags   NUTAG_SUBSTATE()
 *
 * @sa nua_unsubscribe(), @RFC3265
335 336
 *
 * @END_NUA_EVENT
337 338
 */

339 340
static int nua_subscribe_client_response(nua_client_request_t *cr,
					 int status, char const *phrase,
341 342
					 sip_t const *sip)
{
343
  nua_handle_t *nh = cr->cr_owner;
344 345
  nua_dialog_usage_t *du = cr->cr_usage; 
  struct event_usage *eu = nua_dialog_usage_private(du);
346
  enum nua_substate substate;
347

348 349 350 351 352
  if (eu == NULL || cr->cr_terminated)
    substate = nua_substate_terminated;
  else if (status >= 300)
    substate = eu->eu_substate;
  else {
353 354 355 356
    int win_messenger_enable = NH_PGET(nh, win_messenger_enable);
    sip_time_t delta, now = sip_now();

    du->du_ready = 1;
357 358 359

    if (eu->eu_substate != nua_substate_terminated)
      /* If there is no @Expires header, 
360
	 use default value stored in eu_expires */
361
      delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date, 
362
				  eu->eu_expires, now);
363 364
    else
      delta = 0;
365

366 367 368 369 370
    if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) {
      /* Notify from messanger does not match with dialog tag */ 
      nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, "");
    }

371 372
    if (delta > 0) {
      nua_dialog_usage_set_refresh(du, delta);
373
    } 
374 375 376 377 378
    else if (!eu->eu_notified) {
      /* This is a fetch: subscription was really terminated
	 but we wait 32 seconds for NOTIFY. */
      delta = 64 * NTA_SIP_T1 / 1000;

379
      if (win_messenger_enable)
380 381 382
	delta = 4 * 60; 	/* Wait 4 minutes for NOTIFY from Messenger */

      eu->eu_final_wait = 1;
383 384 385
	
      if (eu->eu_substate == nua_substate_terminated)
	eu->eu_substate = nua_substate_embryonic;
386

387
      nua_dialog_usage_refresh_range(du, delta, delta);
388
    }
389
    else {
390
      eu->eu_substate = nua_substate_terminated;
391
    }
392 393 394

    substate = eu->eu_substate;

395 396 397
    if (substate == nua_substate_terminated)
      /* let nua_base_client_tresponse to remove usage */
      cr->cr_terminated = 1;	
398
  }
399 400 401 402
  
  return nua_base_client_tresponse(cr, status, phrase, sip, 
				   NUTAG_SUBSTATE(substate),
				   TAG_END());
403 404 405
}

/** Refresh subscription */
406
static void nua_subscribe_usage_refresh(nua_handle_t *nh,
407
					nua_dialog_state_t *ds,
408 409
					nua_dialog_usage_t *du,
					sip_time_t now)
410
{
411
  nua_client_request_t *cr = du->du_cr;
412
  struct event_usage *eu = nua_dialog_usage_private(du);
413
  
414 415 416
  assert(eu);
  
  if (eu->eu_final_wait) {
417
    /* Did not receive NOTIFY for fetch */
418 419 420
    sip_event_t const *o = du->du_event;
    char const *id = o ? o->o_id : NULL;

421
    SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n",
422 423 424
		nh, o ? o->o_type : "(empty)",
		id ? "; id=" : "", id ? id : ""));

425 426 427 428 429
    nua_stack_tevent(nh->nh_nua, nh,  NULL,
		     nua_i_notify, 408, "Fetch Timeouts without NOTIFY", 
		     NUTAG_SUBSTATE(nua_substate_terminated),
		     SIPTAG_EVENT(du->du_event),
		     TAG_END());
430
    nua_dialog_usage_remove(nh, ds, du);
431

432 433 434
    return;
  }

435 436 437 438
  if (cr) {
    if (nua_client_is_queued(cr) || /* Already refreshing */
	nua_client_resend_request(cr, 0, NULL) >= 0)
      return;
439
  }
440 441 442 443 444
  else if (eu->eu_refer) {
    /*
     * XXX - If we have received a NOTIFY, we should try to terminate
     * subscription
     */
445 446
  }

447 448 449 450 451
  nua_stack_tevent(nh->nh_nua, nh, NULL,
		   nua_i_notify, NUA_INTERNAL_ERROR,
		   NUTAG_SUBSTATE(nua_substate_terminated),
		   SIPTAG_EVENT(du->du_event),
		   TAG_END());
452

453
  nua_dialog_usage_remove(nh, ds, du);
454 455
}

456 457 458 459 460 461
/** Terminate subscription.
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
462
static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
463
					nua_dialog_state_t *ds,
464
					nua_dialog_usage_t *du)
465
{
466
  struct event_usage *eu = nua_dialog_usage_private(du);
467
  nua_client_request_t *cr = du->du_cr;
468

469
  assert(eu); (void)eu;
470

471 472 473
  if (cr) {
    if (nua_client_is_queued(cr)) /* Subscribing. */
      return -1;  /* Request in progress */
474

475 476 477 478
    if (nua_client_resend_request(cr, 1, NULL) >= 0)
      return 0;
  }
  
479
  nua_dialog_usage_remove(nh, ds, du);
480
  return 200;
481 482 483
}

/* ======================================================================== */
484
/* NOTIFY server */
485

486
/** @NUA_EVENT nua_i_notify
487 488 489 490
 *
 * Event for incoming NOTIFY request.
 *
 * @param status statuscode of response sent automatically by stack
491 492 493
 * @param phrase a short textual description of @a status code
 * @param nh     operation handle associated with the subscription
 * @param hmagic application context associated with the handle
494 495 496 497
 * @param sip    incoming NOTIFY request
 * @param tags   NUTAG_SUBSTATE() indicating the subscription state
 *
 * @sa nua_subscribe(), nua_unsubscribe(), @RFC3265, #nua_i_subscribe
498 499
 * 
 * @END_NUA_EVENT
500 501
 */

502 503 504 505 506 507 508 509 510
int nua_notify_server_init(nua_server_request_t *sr);
int nua_notify_server_preprocess(nua_server_request_t *sr);
int nua_notify_server_report(nua_server_request_t *, tagi_t const *);

nua_server_methods_t const nua_notify_server_methods = 
  {
    SIP_METHOD_NOTIFY,
    nua_i_notify,		/* Event */
    { 
511 512
      1,			/* Do create dialog */
      0,			/* Not always in-dialog request */
513
      1,			/* Target refresh request  */
514
      1,			/* Add Contact to response */
515 516 517 518 519 520 521 522 523 524
    },
    nua_notify_server_init,
    nua_notify_server_preprocess,
    nua_base_server_params,
    nua_base_server_respond,
    nua_notify_server_report,
  };


int nua_notify_server_init(nua_server_request_t *sr)
525
{
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
  if (!sr->sr_initial) {
    nua_dialog_state_t *ds = sr->sr_owner->nh_ds;

    /* Check for forked subscription. */
    if (ds->ds_remote_tag && ds->ds_remote_tag[0] && 
	str0cmp(ds->ds_remote_tag, sr->sr_request.sip->sip_from->a_tag)) {
      sip_contact_t const *m = NULL;

      m = nua_stack_get_contact(sr->sr_owner->nh_nua->nua_registrations);
      
      if (m) {
	sip_warning_t w[1];
	
	sip_warning_init(w)->w_code = 399;
	w->w_host = m->m_url->url_host;
	w->w_port = m->m_url->url_port;
	w->w_text = "Forking SUBSCRIBEs are not supported";
543

544 545
	sip_add_dup(sr->sr_response.msg, NULL, (sip_header_t*)w);
      }
546

547
      return SR_STATUS(sr, 481, "Subscription Does Not Exist");
548
    }
549 550
  }

551 552
  return 0;
}
553

554 555 556 557 558 559 560 561 562 563 564 565 566
int nua_notify_server_preprocess(nua_server_request_t *sr)
{
  nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
  nua_dialog_usage_t *du;
  struct event_usage *eu;
  sip_t const *sip = sr->sr_request.sip;
  sip_event_t *o = sip->sip_event;
  enum nua_substate substate = nua_substate_terminated;
  sip_subscription_state_t *subs = sip->sip_subscription_state;
  char const *what = "", *reason = NULL;

  du = nua_dialog_usage_get(ds, nua_subscribe_usage, o);
  sr->sr_usage = du;
567 568 569 570 571 572 573

  if (du == NULL) {
    if (!sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), SIP_METHOD_NOTIFY))
      return SR_STATUS(sr, 481, "Subscription Does Not Exist");
    /* Let application to handle unsolicited NOTIFY */
    return 0;
  }
574
  
575
  eu = nua_dialog_usage_private(du); assert(eu);
576
  eu->eu_notified++;
577
  if (!o->o_id) 
578
    eu->eu_no_id = 1;
579

580
  if (subs == NULL) {
581 582 583 584
    /* Compatibility */
    unsigned long delta = eu->eu_expires;
    if (sip->sip_expires) 
      delta = sip->sip_expires->ex_delta;
585 586

    if (delta == 0)
587
      substate = nua_substate_terminated, what = "terminated";
588
    else
589
      substate = nua_substate_active, what = "active";
590
  }
591 592 593
  else if (strcasecmp(subs->ss_substate, what = "terminated") == 0) {
    substate = nua_substate_terminated;
    reason = subs->ss_reason;
594

595 596 597
    if (str0casecmp(reason, "deactivated") == 0 ||
	str0casecmp(reason, "probation") == 0) 
      substate = nua_substate_embryonic;
598
  }
599 600 601 602
  else if (strcasecmp(subs->ss_substate, what = "pending") == 0) {
    substate = nua_substate_pending;
  }
  else /* if (strcasecmp(subs->ss_substate, what = "active") == 0) */ {
603
    /* Any extended state is considered as active */
604 605
    what = subs->ss_substate;
    substate = nua_substate_active;
606
  }
607

608 609 610 611 612
  eu->eu_substate = substate;

  SU_DEBUG_5(("nua(%p): %s: %s (%s)\n", 
	      sr->sr_owner, "nua_notify_server_preprocess",
	      what, reason ? reason : ""));
613

614 615
  return SR_STATUS1(sr, SIP_200_OK);
}
616

617

618 619 620 621 622 623 624 625 626 627
int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_usage_t *du = sr->sr_usage;
  struct event_usage *eu = nua_dialog_usage_private(du);
  sip_t const *sip = sr->sr_request.sip;
  enum nua_substate substate = nua_substate_terminated;
  sip_time_t delta = SIP_TIME_MAX;
  int retry = -1;
  int retval;
628

629 630
  if (eu) {
    sip_subscription_state_t *subs = sip->sip_subscription_state;
631

632
    substate = eu->eu_substate;
633

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
    if (substate == nua_substate_active || substate == nua_substate_pending) {
      if (subs && subs->ss_expires)
	delta = strtoul(subs->ss_expires, NULL, 10);
      else
	delta = eu->eu_expires;
    }
    else if (substate == nua_substate_embryonic) {
      if (subs && subs->ss_reason) {
	if (str0casecmp(subs->ss_reason, "deactivated") == 0) {
	  retry = 0;		/* retry immediately */
	} 
	else if (str0casecmp(subs->ss_reason, "probation") == 0) {
	  retry = 30;
	  if (subs->ss_retry_after)
	    retry = strtoul(subs->ss_retry_after, NULL, 10);
	  if (retry > 3600)
	    retry = 3600;
	}
      }
    }
    else if (substate == nua_substate_terminated) {
      sr->sr_terminating = 1;
656 657
    }
  }
658 659 660 661 662 663 664 665 666 667 668 669
  
  retval = nua_base_server_treport(sr, /* can destroy sr */
				   NUTAG_SUBSTATE(substate),
				   TAG_NEXT(tags)); 

  if (retval >= 2 || du == NULL)
    return retval;

  if (retry >= 0) {		/* Try to subscribe again */
    /* XXX - this needs through testing */
    nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
    nua_dialog_usage_refresh_range(du, retry, retry + 5);
670 671 672
  }
  else {
    nua_dialog_usage_set_refresh(du, delta);
673 674
  }

675
  return retval;
676 677
}

678

679 680 681
/* ======================================================================== */
/* REFER */

682 683
/** Transfer a call. 
 * 
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
 * Send a REFER request asking the recipient to transfer the call. 
 *
 * The REFER request also establishes an implied subscription to the "refer"
 * event. The "refer" event can have an "id" parameter, which has the value
 * of CSeq number in the REFER request. After initiating the REFER request,
 * the nua engine sends application a #nua_r_refer event with status 100 and
 * tag NUTAG_REFER_EVENT() containing a matching event header with id
 * parameter.
 *
 * Note that the @Event header in the locally generated #nua_r_refer event
 * contains the @a id parameter. The @a id parameter contains the @CSeq
 * number of the REFER request, and it may get incremented if the request is
 * retried because it got challenged or redirected. In that case, the
 * application gets a new #nua_r_refer event with status 100 and tag
 * NUTAG_REFER_EVENT(). Also the recipient of the REFER request may or may
 * not include the @a id parameter with the @Event header in the NOTIFY
 * requests messages which it sends to the sender of the REFER request.
 *
 * Therefore the application is not able to modify the state of the implied
 * subscription before receiving the first NOTIFY request.
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
 *    NUTAG_URL() \n
 *    Tags of nua_set_hparams() \n
 *    Tags in <sip_tag.h>
 *
 * @par Events:
 *    #nua_r_refer \n
 *    #nua_i_notify
 *
720 721
 * @sa #nua_r_refer, NUTAG_SUBSTATE(), NUTAG_REFER_EVENT(),#nua_i_refer,
 * @RFC3515, @ReferTo, @RFC3892, @ReferredBy
722 723
 */

724
/**@NUA_EVENT nua_r_refer
725
 *
726
 * @brief Response to outgoing REFER.
727
 *
728 729 730 731 732 733 734
 * @param status response status code
 *               (if the request is retried, @a status is 100, the @a
 *               sip->sip_status->st_status contain the real status code
 *               from the response message, e.g., 302, 401, or 407)
 * @param phrase a short textual description of @a status code
 * @param nh     operation handle associated with the REFER request
 * @param hmagic application context associated with the handle
735
 * @param sip    response to REFER request or NULL upon an error
736 737 738 739 740 741 742 743 744
 *               (status code is in @a status and 
 *                descriptive message in @a phrase parameters)
 * @param tags    NUTAG_REFER_EVENT() \n
 *                NUTAG_SUBSTATE()
 *
 * @sa nua_refer(), NUTAG_SUBSTATE(), #nua_i_refer,
 * @RFC3515, @ReferTo, @RFC3892, @ReferredBy
 *
 * @END_NUA_EVENT
745 746
 */

747 748 749 750 751 752 753 754 755 756
static int nua_refer_client_init(nua_client_request_t *cr, 
				 msg_t *, sip_t *,
				 tagi_t const *tags);
static int nua_refer_client_request(nua_client_request_t *cr,
				    msg_t *, sip_t *,
				    tagi_t const *tags);
static int nua_refer_client_response(nua_client_request_t *cr,
				     int status, char const *phrase,
				     sip_t const *sip);

757
static nua_client_methods_t const nua_refer_client_methods = {
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
  SIP_METHOD_REFER,
  0,
  { 
    /* create_dialog */ 1,
    /* in_dialog */ 1,
    /* target refresh */ 1
  },
  /*nua_refer_client_template*/ NULL,
  nua_refer_client_init,
  nua_refer_client_request,
  /* nua_refer_client_check_restart */ NULL,
  nua_refer_client_response,
  nua_refer_client_response,	/* Preliminary */
  NULL
};

int
nua_stack_refer(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		    tagi_t const *tags)
777
{
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
  return nua_client_create(nh, e, &nua_refer_client_methods, tags);
}

static int nua_refer_client_init(nua_client_request_t *cr,
				 msg_t *msg, sip_t *sip,
				 tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;

  if (sip->sip_referred_by == NULL) {
    sip_from_t *a = sip->sip_from;
    sip_referred_by_t by[1];

    sip_referred_by_init(by);

    if (a == NULL)
      a = nh->nh_nua->nua_from;
    by->b_display = a->a_display;
    *by->b_url = *a->a_url;

    sip_add_dup(msg, sip, (sip_header_t *)by);
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 827 828 829 830 831 832 833 834 835

  if (sip->sip_event)
    sip_header_remove(msg, sip, (sip_header_t *)sip->sip_event);

  return 0;
}

static int nua_refer_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;
  struct event_usage *eu;
  sip_event_t *event;
  int error;

  cr->cr_usage = NULL;

  if (du)
    nua_dialog_usage_remove(nh, nh->nh_ds, du);

  event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
  if (!event)
    return -1;
  du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event);
  if (!du)
    return -1;

  eu = nua_dialog_usage_private(cr->cr_usage = du);
  eu ->eu_refer = 1;

  error = nua_base_client_request(cr, msg, sip, tags);

  if (!error) {
    /* Give application an Event header for matching NOTIFYs with REFER */
836 837 838 839
    nua_stack_tevent(nh->nh_nua, nh, NULL,
		     cr->cr_event, SIP_100_TRYING,
		     NUTAG_REFER_EVENT(event),
		     TAG_END());
840
    su_free(nh->nh_home, event);
841 842
  }

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
  return error;
}

static int nua_refer_client_response(nua_client_request_t *cr,
				     int status, char const *phrase,
				     sip_t const *sip)
{
  nua_dialog_usage_t *du = cr->cr_usage; 
  enum nua_substate substate = nua_substate_terminated;

  if (du) {
    struct event_usage *eu = nua_dialog_usage_private(du);

    if (status < 200) {
      substate = eu->eu_substate;      
    } 
    else if (status < 300) {
      sip_refer_sub_t const *rs = sip_refer_sub(sip);

      if (rs && strcasecmp("false", rs->rs_value) == 0)
	cr->cr_terminated = 1;

      if (!cr->cr_terminated)
	substate = eu->eu_substate;
    }
  }
  
  return nua_base_client_tresponse(cr, status, phrase, sip, 
				   NUTAG_SUBSTATE(substate),
				   TAG_END());
873
}