nua_subnotref.c 25.9 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_unsolicited:1;	        /**< Not SUBSCRIBEd or REFERed */
65
  unsigned eu_refer:1;		        /**< Implied subscription by refer */
66 67
  unsigned eu_final_wait:1;	        /**< Waiting for final NOTIFY */
  unsigned eu_no_id:1;		        /**< Do not use "id" (even if we have one) */
68 69 70 71 72 73 74 75 76
};

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);
77
static void nua_subscribe_usage_refresh(nua_handle_t *,
78
					nua_dialog_state_t *,
79 80 81
					nua_dialog_usage_t *,
					sip_time_t);
static int nua_subscribe_usage_shutdown(nua_handle_t *,
82
					nua_dialog_state_t *,
83
					nua_dialog_usage_t *);
84 85 86 87 88 89 90

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,
91 92 93
    NULL,
    nua_subscribe_usage_refresh,
    nua_subscribe_usage_shutdown
94 95 96 97 98 99 100 101 102 103 104 105 106 107
  }};

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++;
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  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 */

124 125 126
/**@fn void nua_subscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
 *
 *  Subscribe to a SIP event. 
127 128 129 130 131 132 133 134 135 136 137 138 139 140
 *
 * 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()
141
 *    Header tags defined in <sofia-sip/sip_tag.h>
142 143 144 145 146 147 148 149
 *
 * @par Events:
 *    #nua_r_subscribe \n
 *    #nua_i_notify
 *
 * @sa NUTAG_SUBSTATE(), @RFC3265
 */

150 151 152
/**@fn void nua_unsubscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
 *
 * Unsubscribe an event. 
153 154 155 156 157 158 159 160 161 162 163 164 165 166
 *
 * 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
167
 *    Header tags defined in <sofia-sip/sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR()
168 169 170 171 172 173 174
 *
 * @par Events:
 *    #nua_r_unsubscribe 
 *
 * @sa NUTAG_SUBSTATE(), @RFC3265
 */

175 176 177 178 179 180 181 182
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,
183 184
					 sip_t const *sip);

185
static nua_client_methods_t const nua_subscribe_client_methods = {
186 187 188 189 190 191 192 193 194 195 196 197 198
  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
};
199

200
int
201 202
nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
		    tagi_t const *tags)
203
{
204 205
  return nua_client_create(nh, e, &nua_subscribe_client_methods, tags);
}
206

207 208 209 210 211 212 213
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;
214

215
  du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
216

217 218
  if (du == NULL && o == NULL)
    du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
219

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
  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;
247

248 249 250 251 252 253 254 255 256 257
  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");
258
    }
259 260 261 262

#if 0
    if (cr->cr_terminating) {
      /* Already terminated subscription? */
263 264
      if (eu->eu_substate == nua_substate_terminated ||
	  eu->eu_substate == nua_substate_embryonic) {
265
	return nua_client_return(cr, SIP_200_OK, msg);
266
      }
267
    }
268
#endif
269

270 271 272 273 274 275 276
    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;
277
    else
278 279 280 281
    /* 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. */
282
      expires = eu->eu_expires = 3600;
283

284
    eu->eu_final_wait = 0;
285

286 287 288
    if (eu->eu_substate == nua_substate_terminated)
      eu->eu_substate = nua_substate_embryonic;
  }
289

290 291 292 293 294
  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);
  }
295

296
  return nua_base_client_request(cr, msg, sip, tags);
297 298
}

299
/** @NUA_EVENT nua_r_subscribe
300
 *
301
 * Response to an outgoing SUBSCRIBE request.
302 303 304 305
 *
 * The SUBSCRIBE request may have been sent explicitly by nua_subscribe() or
 * implicitly by NUA state machine.
 *
306 307 308 309 310 311 312
 * @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
313
 * @param sip    response to SUBSCRIBE request or NULL upon an error
314 315
 *               (status code is in @a status and 
 *                descriptive message in @a phrase parameters)
316 317 318
 * @param tags   NUTAG_SUBSTATE()
 *
 * @sa nua_subscribe(), @RFC3265
319 320
 *
 * @END_NUA_EVENT
321 322
 */

323
/** @NUA_EVENT nua_r_unsubscribe
324 325 326
 *
 * Response to an outgoing un-SUBSCRIBE.
 *
327 328 329 330 331 332 333
 * @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
334
 * @param sip    response to SUBSCRIBE request or NULL upon an error
335 336
 *               (status code is in @a status and 
 *                descriptive message in @a phrase parameters)
337 338 339
 * @param tags   NUTAG_SUBSTATE()
 *
 * @sa nua_unsubscribe(), @RFC3265
340 341
 *
 * @END_NUA_EVENT
342 343
 */

344 345
static int nua_subscribe_client_response(nua_client_request_t *cr,
					 int status, char const *phrase,
346 347
					 sip_t const *sip)
{
348
  nua_handle_t *nh = cr->cr_owner;
349 350
  nua_dialog_usage_t *du = cr->cr_usage; 
  struct event_usage *eu = nua_dialog_usage_private(du);
351
  enum nua_substate substate;
352

353 354 355 356 357
  if (eu == NULL || cr->cr_terminated)
    substate = nua_substate_terminated;
  else if (status >= 300)
    substate = eu->eu_substate;
  else {
358 359 360 361
    int win_messenger_enable = NH_PGET(nh, win_messenger_enable);
    sip_time_t delta, now = sip_now();

    du->du_ready = 1;
362 363 364

    if (eu->eu_substate != nua_substate_terminated)
      /* If there is no @Expires header, 
365
	 use default value stored in eu_expires */
366
      delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date, 
367
				  eu->eu_expires, now);
368 369
    else
      delta = 0;
370

371 372 373 374 375
    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, "");
    }

376 377
    if (delta > 0) {
      nua_dialog_usage_set_refresh(du, delta);
378
    } 
379 380 381 382 383
    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;

384
      if (win_messenger_enable)
385 386 387
	delta = 4 * 60; 	/* Wait 4 minutes for NOTIFY from Messenger */

      eu->eu_final_wait = 1;
388 389 390
	
      if (eu->eu_substate == nua_substate_terminated)
	eu->eu_substate = nua_substate_embryonic;
391

392
      nua_dialog_usage_set_refresh_range(du, delta, delta);
393
    }
394
    else {
395
      eu->eu_substate = nua_substate_terminated;
396
    }
397 398 399

    substate = eu->eu_substate;

400 401 402
    if (substate == nua_substate_terminated)
      /* let nua_base_client_tresponse to remove usage */
      cr->cr_terminated = 1;	
403
  }
404

405 406
  return nua_base_client_tresponse(cr, status, phrase, sip, 
				   NUTAG_SUBSTATE(substate),
407
				   SIPTAG_EVENT(du ? du->du_event : NULL),
408
				   TAG_END());
409 410 411
}

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

427
    SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n",
428
		(void *)nh, o ? o->o_type : "(empty)",
429 430
		id ? "; id=" : "", id ? id : ""));

431 432 433 434 435
    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());
436
    nua_dialog_usage_remove(nh, ds, du);
437

438 439 440
    return;
  }

441
  if (cr) {
442
    if (nua_client_resend_request(cr, 0) >= 0)
443
      return;
444
  }
445 446 447 448 449
  else if (eu->eu_refer) {
    /*
     * XXX - If we have received a NOTIFY, we should try to terminate
     * subscription
     */
450 451
  }

452 453 454 455 456 457
  if (!eu->eu_unsolicited)
    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());
458

459
  nua_dialog_usage_remove(nh, ds, du);
460 461
}

462 463 464 465 466 467
/** Terminate subscription.
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
468
static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
469
					nua_dialog_state_t *ds,
470
					nua_dialog_usage_t *du)
471
{
472
  struct event_usage *eu = nua_dialog_usage_private(du);
473
  nua_client_request_t *cr = du->du_cr;
474

475
  assert(eu); (void)eu;
476

477
  if (cr) {
478
    if (nua_client_resend_request(cr, 1) >= 0)
479 480 481
      return 0;
  }
  
482
  nua_dialog_usage_remove(nh, ds, du);
483
  return 200;
484 485 486
}

/* ======================================================================== */
487
/* NOTIFY server */
488

489
/** @NUA_EVENT nua_i_notify
490 491 492 493
 *
 * Event for incoming NOTIFY request.
 *
 * @param status statuscode of response sent automatically by stack
494 495 496
 * @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
497 498 499 500
 * @param sip    incoming NOTIFY request
 * @param tags   NUTAG_SUBSTATE() indicating the subscription state
 *
 * @sa nua_subscribe(), nua_unsubscribe(), @RFC3265, #nua_i_subscribe
501 502
 * 
 * @END_NUA_EVENT
503 504
 */

505 506 507 508 509 510 511 512 513
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 */
    { 
514 515 516 517
      /* create_dialog: */ 1,	/* Do create dialog */
      /* in_dialog: */ 0,	/* Not always in-dialog request */
      /* target_refresh: */ 1,	/* Target refresh request  */
      /* add_contact: */ 1,	/* Add Contact to response */
518 519 520 521 522 523 524 525 526 527
    },
    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)
528
{
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
  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";
546

547 548
	sip_add_dup(sr->sr_response.msg, NULL, (sip_header_t*)w);
      }
549

550
      return SR_STATUS(sr, 481, "Subscription Does Not Exist");
551
    }
552 553
  }

554 555
  return 0;
}
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;
567
  int solicited = 1;
568 569

  du = nua_dialog_usage_get(ds, nua_subscribe_usage, o);
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");
574 575 576 577 578

    solicited = 0;    /* Let application to handle unsolicited NOTIFY */
    du = nua_dialog_usage_add(sr->sr_owner, ds, nua_subscribe_usage, o);
    if (!du)
      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
579
  }
580
  
581
  sr->sr_usage = du;
582
  eu = nua_dialog_usage_private(du); assert(eu);
583
  eu->eu_notified++;
584
  if (!o->o_id) 
585
    eu->eu_no_id = 1;
586

587
  if (subs == NULL) {
588 589 590 591
    /* Compatibility */
    unsigned long delta = eu->eu_expires;
    if (sip->sip_expires) 
      delta = sip->sip_expires->ex_delta;
592 593

    if (delta == 0)
594
      substate = nua_substate_terminated, what = "terminated";
595
    else
596
      substate = nua_substate_active, what = "active";
597
  }
598 599 600
  else if (strcasecmp(subs->ss_substate, what = "terminated") == 0) {
    substate = nua_substate_terminated;
    reason = subs->ss_reason;
601

602 603 604
    if (str0casecmp(reason, "deactivated") == 0 ||
	str0casecmp(reason, "probation") == 0) 
      substate = nua_substate_embryonic;
605
  }
606 607 608 609
  else if (strcasecmp(subs->ss_substate, what = "pending") == 0) {
    substate = nua_substate_pending;
  }
  else /* if (strcasecmp(subs->ss_substate, what = "active") == 0) */ {
610
    /* Any extended state is considered as active */
611 612
    what = subs->ss_substate;
    substate = nua_substate_active;
613
  }
614

615
  eu->eu_substate = substate;
616 617
  if (!solicited)
    eu->eu_unsolicited = 1;
618 619

  SU_DEBUG_5(("nua(%p): %s: %s (%s)\n", 
620
	      (void *)sr->sr_owner, "nua_notify_server_preprocess",
621
	      what, reason ? reason : ""));
622

623 624 625 626
  if (solicited)
    return SR_STATUS1(sr, SIP_200_OK);

  return 0;
627
}
628

629

630 631 632 633 634 635 636 637
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;
638
  sip_event_t const *o = sip->sip_event;
639 640
  int retry = -1;
  int retval;
641

642 643
  if (eu) {
    sip_subscription_state_t *subs = sip->sip_subscription_state;
644

645
    substate = eu->eu_substate;
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    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;
669 670
    }
  }
671 672 673
  
  retval = nua_base_server_treport(sr, /* can destroy sr */
				   NUTAG_SUBSTATE(substate),
674
				   SIPTAG_EVENT(o),
675 676
				   TAG_NEXT(tags)); 

677
  if (retval != 1 || du == NULL)
678 679
    return retval;

680 681 682 683
  if (eu->eu_unsolicited) {
    /* Xyzzy */;
  }
  else if (retry >= 0) {		/* Try to subscribe again */
684 685
    /* XXX - this needs through testing */
    nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
686
    nua_dialog_usage_set_refresh_range(du, retry, retry + 5);
687 688 689
  }
  else {
    nua_dialog_usage_set_refresh(du, delta);
690 691
  }

692
  return retval;
693 694
}

695

696 697 698
/* ======================================================================== */
/* REFER */

699 700 701
/**@fn void nua_refer(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
 *
 * Transfer a call. 
702
 * 
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
 * 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.
723 724 725 726 727 728 729 730 731 732
 *
 * @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
733
 *    Header tags defined in <sofia-sip/sip_tag.h>
734 735 736 737 738
 *
 * @par Events:
 *    #nua_r_refer \n
 *    #nua_i_notify
 *
739
 * @sa #nua_r_refer, NUTAG_SUBSTATE(), NUTAG_REFER_EVENT(),#nua_i_refer,
Pekka Pessi's avatar
Pekka Pessi committed
740 741 742 743
 * @RFC3515, @ReferTo, SIPTAG_REFER_TO(), SIPTAG_REFER_TO_STR(),
 * @RFC3892, @ReferredBy, SIPTAG_REFERRED_BY(), SIPTAG_REFERRED_BY_STR(),
 * @RFC3891, @Replaces, SIPTAG_REPLACES(), SIPTAG_REPLACES_STR(),
 * @RFC4488, @ReferSub, SIPTAG_REFER_SUB(), SIPTAG_REFER_SUB_STR()
744 745
 */

746
/**@NUA_EVENT nua_r_refer
747
 *
748
 * @brief Response to outgoing REFER.
749
 *
750 751 752 753 754 755 756
 * @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
757
 * @param sip    response to REFER request or NULL upon an error
758 759 760 761 762 763
 *               (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,
Pekka Pessi's avatar
Pekka Pessi committed
764
 * @RFC3515, @RFC4488, @ReferSub
765 766
 *
 * @END_NUA_EVENT
767 768
 */

769 770 771 772 773 774 775 776 777 778
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);

779
static nua_client_methods_t const nua_refer_client_methods = {
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
  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)
799
{
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
  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);
821
  }
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857

  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 */
858 859 860
    nua_stack_tevent(nh->nh_nua, nh, NULL,
		     cr->cr_event, SIP_100_TRYING,
		     NUTAG_REFER_EVENT(event),
861
		     SIPTAG_EVENT(event),
862
		     TAG_END());
863
    su_free(nh->nh_home, event);
864 865
  }

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
  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),
895
				   SIPTAG_EVENT(du ? du->du_event : NULL),
896
				   TAG_END());
897
}