nua_notifier.c 26 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 26 27
/*
 * 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_notifier.c
 * @brief SUBSCRIBE server, NOTIFY client and REFER server
 *
28 29
 * Simpler event server. See nua_event_server.c for more complex event
 * server.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 *
 * @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>
47
#include <sofia-sip/sip_extra.h>
48 49 50
#include <sofia-sip/sip_status.h>
#include <sofia-sip/sip_util.h>
#include <sofia-sip/su_uniqueid.h>
51 52
#include <sofia-sip/su_md5.h>
#include <sofia-sip/token64.h>
53 54 55 56 57 58 59 60 61

#include "nua_stack.h"

/* ---------------------------------------------------------------------- */
/* Notifier event usage */

struct notifier_usage
{
  enum nua_substate  nu_substate;	/**< Subscription state */
62 63 64 65 66 67
  sip_time_t         nu_expires; 	/**< Expiration time */
  sip_time_t         nu_requested;      /**< Requested expiration time */
  char              *nu_tag;	        /**< @ETag in last NOTIFY */
  unsigned           nu_etags:1;	/**< Subscriber supports etags */
  unsigned           nu_appl_etags:1;   /**< Application generates etags */
  unsigned           nu_no_body:1;      /**< Suppress body */
68 69 70 71 72 73 74 75 76
};

static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);
static int nua_notify_usage_add(nua_handle_t *nh, 
				   nua_dialog_state_t *ds,
				   nua_dialog_usage_t *du);
static void nua_notify_usage_remove(nua_handle_t *nh, 
				       nua_dialog_state_t *ds,
				       nua_dialog_usage_t *du);
77
static void nua_notify_usage_refresh(nua_handle_t *nh,
78
				     nua_dialog_state_t *ds,
79 80 81
				     nua_dialog_usage_t *du,
				     sip_time_t now);
static int nua_notify_usage_shutdown(nua_handle_t *nh,
82
				     nua_dialog_state_t *ds,
83
				     nua_dialog_usage_t *du);
84 85 86 87 88 89 90

static nua_usage_class const nua_notify_usage[1] = {
  {
    sizeof (struct notifier_usage), (sizeof nua_notify_usage),
    nua_notify_usage_add,
    nua_notify_usage_remove,
    nua_notify_usage_name,
91 92 93
    NULL,
    nua_notify_usage_refresh,
    nua_notify_usage_shutdown,
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
  }};

static char const *nua_notify_usage_name(nua_dialog_usage_t const *du)
{
  return "notify";
}

static 
int nua_notify_usage_add(nua_handle_t *nh, 
			   nua_dialog_state_t *ds,
			   nua_dialog_usage_t *du)
{
  ds->ds_has_events++;
  ds->ds_has_notifys++;
  return 0;
}

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

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

123
/** @NUA_EVENT nua_i_subscribe
124
 *
125 126 127 128
 * Incoming @b SUBSCRIBE request.
 *
 * @b SUBSCRIBE request is used to query SIP event state or establish a SIP
 * event subscription.
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
 *
 * Initial SUBSCRIBE requests are dropped with <i>489 Bad Event</i>
 * response, unless the application has explicitly included the @Event in
 * the list of allowed events with nua_set_params() tag NUTAG_ALLOW_EVENTS()
 * (or SIPTAG_ALLOW_EVENTS() or SIPTAG_ALLOW_EVENTS_STR()). The application
 * can decide whether to accept the SUBSCRIBE request or reject it. The
 * nua_response() call responding to a SUBSCRIBE request must have
 * NUTAG_WITH() (or NUTAG_WITH_CURRENT()/NUTAG_WITH_SAVED()) tag.
 *
 * If the application accepts the SUBSCRIBE request, it must immediately
 * send an initial NOTIFY establishing the dialog. This is because the
 * response to the SUBSCRIBE request may be lost because the SUBSCRIBE
 * request was forked by an intermediate proxy. 
 *
 * SUBSCRIBE requests modifying (usually refreshing or terminating) an
 * existing event subscription are accepted by default and a <i>200 OK</i>
 * response along with a copy of previously sent NOTIFY is sent
 * automatically.
 *
 * By default, only event subscriptions accepted are those created
 * implicitly by REFER request. See #nua_i_refer how the application must
 * handle the REFER requests.
 *
 * @param status status code of response sent automatically by stack
 * @param phrase response phrase sent automatically by stack
154 155 156
 * @param nh     operation handle associated with the incoming request
 * @param hmagic application context associated with the handle
 *               (NULL when handle is created by the stack)
157 158
 * @param sip    SUBSCRIBE request headers
 * @param tags   NUTAG_SUBSTATE()
159 160 161 162 163
 *
 * @sa @RFC3265, nua_notify(), NUTAG_SUBSTATE(), @SubscriptionState,
 * @Event, nua_subscribe(), #nua_r_subscribe, #nua_i_refer, nua_refer()
 *
 * @END_NUA_EVENT
164 165
 */

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
static int nua_subscribe_server_init(nua_server_request_t *sr);
static int nua_subscribe_server_preprocess(nua_server_request_t *sr);
static int nua_subscribe_server_respond(nua_server_request_t*, tagi_t const *);
static int nua_subscribe_server_report(nua_server_request_t*, tagi_t const *);

nua_server_methods_t const nua_subscribe_server_methods = 
  {
    SIP_METHOD_SUBSCRIBE,
    nua_i_subscribe,		/* Event */
    { 
      1,			/* Create dialog */
      0,			/* Initial request */
      1,			/* Target refresh request  */
      1,			/* Add Contact */
    },
    nua_subscribe_server_init,
    nua_subscribe_server_preprocess,
    nua_base_server_params,
    nua_subscribe_server_respond,
    nua_subscribe_server_report,
  };

int nua_subscribe_server_init(nua_server_request_t *sr)
189
{
190 191 192 193
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_state_t *ds = nh->nh_ds;
  sip_allow_events_t const *allow_events = NH_PGET(nh, allow_events);
  sip_t const *sip = sr->sr_request.sip;
194
  sip_event_t *o = sip->sip_event;
195 196
  char const *event = o ? o->o_type : NULL;
  
197
  if (sr->sr_initial || !nua_dialog_usage_get(ds, nua_notify_usage, o)) {
198 199
    if (event && str0cmp(event, "refer") == 0)
      /* refer event subscription should be initiated with REFER */
200 201
      return SR_STATUS1(sr, SIP_403_FORBIDDEN);

202
    /* XXX - event is case-sensitive, should use msg_header_find_item() */
203 204
    if (!event || !msg_header_find_param(allow_events->k_common, event))
      return SR_STATUS1(sr, SIP_489_BAD_EVENT);
205
  }
206

207 208
  return 0;
}
209

210 211 212 213 214 215 216 217 218 219 220
int nua_subscribe_server_preprocess(nua_server_request_t *sr)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_state_t *ds = nh->nh_ds;
  nua_dialog_usage_t *du;
  struct notifier_usage *nu;
  sip_t const *sip = sr->sr_request.sip;
  sip_event_t *o = sip->sip_event;
  char const *event = o ? o->o_type : NULL;
  /* Maximum expiration time */
  unsigned long expires = 3600;
221

222 223 224
  assert(nh && nh->nh_nua->nua_dhandle != nh);
  
  du = nua_dialog_usage_get(ds, nua_notify_usage, o);
225

226 227 228 229 230 231 232 233 234 235
  if (du == NULL) {
    /* Create a new subscription */
    du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o);
    if (du == NULL)
      return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
  }
  else {
    /* Refresh existing subscription */
    if (str0cmp(event, "refer") == 0)
      expires = NH_PGET(nh, refer_expires);
236 237

    SR_STATUS1(sr, SIP_200_OK);
238 239
  }

240
  nu = nua_dialog_usage_private(du);
241

242 243 244
  if (sip->sip_expires && sip->sip_expires->ex_delta < expires)
    expires = sip->sip_expires->ex_delta;
  nu->nu_requested = sip_now() + expires;
245

246 247 248 249 250
  nu->nu_etags = 
    sip_suppress_body_if_match(sip) ||
    sip_suppress_notify_if_match(sip) ||
    sip_has_feature(sr->sr_request.sip->sip_supported, "etags");
  
251
  sr->sr_usage = du;
252

253
  return sr->sr_status <= 100 ? 0 : sr->sr_status;
254 255
}

256
/** @internal Respond to a SUBSCRIBE request.
257 258 259
 *
 */
static
260
int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags)
261
{
262
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
263

264 265
  msg_t *msg = sr->sr_response.msg;
  sip_t *sip = sr->sr_response.sip;
266

267 268
  if (200 <= sr->sr_status && sr->sr_status < 300) {
    sip_expires_t ex[1]; 
269

270
    sip_expires_init(ex);
271

272 273
    if (nu) {
      sip_time_t now = sip_now();
274

275 276 277 278 279 280
      if (nu->nu_requested) {
	if (nu->nu_requested > nu->nu_expires)
	  nu->nu_expires = nu->nu_requested;
	else if (nu->nu_expires <= now || nu->nu_requested <= now)
	  nu->nu_substate = nua_substate_terminated;
      }
281

282 283 284 285 286 287
      if (nu->nu_expires > now)
	ex->ex_delta = nu->nu_expires - now;
    }
    else {
      /* Add header Expires: 0 */
    }
288

289 290
    if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta)
      sip_add_dup(msg, sip, (sip_header_t *)ex);
291
  }
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

  return nua_base_server_respond(sr, tags);
}

static
int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  nua_dialog_state_t *ds = nh->nh_ds;
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
  enum nua_substate substate = nua_substate_terminated;
  int notify = 0;
  int retval;

  if (nu && !sr->sr_terminating) {
    substate = nu->nu_substate;
  }

  /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */
  if (nu && nu->nu_requested && substate != nua_substate_embryonic) {
    sip_t const *sip = sr->sr_request.sip;
    sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip);
    sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip);
    
    if (!nu->nu_tag)
      notify = 1;
    else if (snim && !strcasecmp(snim->snim_tag, nu->nu_tag))
      notify = 0;
    else if (sbim && !strcasecmp(snim->snim_tag, nu->nu_tag))
      notify = 1, nu->nu_no_body = 1;
    else 
      notify = 1;
  }

  retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), TAG_END());

  if (retval >= 2 || nu == NULL)
    return retval;
  
  if (notify) {
    /* Send NOTIFY (and terminate subscription, when needed) */
    nua_dialog_usage_refresh(nh, ds, sr->sr_usage, sip_now());
334 335
  }
  
336
  return retval;
337
}
338 339

/* ======================================================================== */
340
/* NOTIFY client */
341

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
/**@fn void nua_notify(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
 *
 * Send a SIP NOTIFY request message.
 *
 * This function is used when the application implements itself the
 * notifier. The application must provide valid @SubscriptionState and
 * @Event headers using SIP tags. If there is no @SubscriptionState header,
 * the subscription state can be modified with NUTAG_SUBSTATE().
 *
 * @bug If the @Event is not given by application, stack uses the @Event
 * header from the first subscription usage on handle.
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
 * @return 
 *    nothing
 *
 * @par Related Tags:
361 362
 *    NUTAG_SUBSTATE() \n
 *    Tags of nua_set_hparams() \n
363 364 365 366 367 368 369 370
 *    Tags in <sip_tag.h>
 *
 * @par Events:
 *    #nua_r_notify
 *
 * @sa @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_ALLOW_EVENTS()
 */

371 372 373
static int nua_notify_client_init(nua_client_request_t *cr, 
				  msg_t *, sip_t *,
				  tagi_t const *tags);
374 375 376
static int nua_notify_client_init_etag(nua_client_request_t *cr,
				       msg_t *msg, sip_t *sip,
				       tagi_t const *tags);
377 378 379
static int nua_notify_client_request(nua_client_request_t *cr,
				     msg_t *, sip_t *,
				     tagi_t const *tags);
380 381 382 383 384
static int nua_notify_client_report(nua_client_request_t *cr,
				    int status, char const *phrase,
				    sip_t const *sip,
				    nta_outgoing_t *orq,
				    tagi_t const *tags);
385

386
static nua_client_methods_t const nua_notify_client_methods = {
387 388 389 390 391 392 393 394 395 396 397
  SIP_METHOD_NOTIFY,
  0,
  { 
    /* create_dialog */ 1,
    /* in_dialog */ 1,
    /* target refresh */ 1
  },
  /* nua_notify_client_template */ NULL,
  nua_notify_client_init,
  nua_notify_client_request,
  /* nua_notify_client_check_restart */ NULL,
398 399 400
  /* nua_notify_client_response */ NULL,
  /* nua_notify_client_preliminary */ NULL,
  nua_notify_client_report
401 402
};

403 404 405 406 407
/**@internal Send NOTIFY. */
int nua_stack_notify(nua_t *nua,
		     nua_handle_t *nh,
		     nua_event_t e,
		     tagi_t const *tags)
408
{
409
  return nua_client_create(nh, e, &nua_notify_client_methods, tags);
410 411
}

412 413 414
static int nua_notify_client_init(nua_client_request_t *cr,
				  msg_t *msg, sip_t *sip,
				  tagi_t const *tags)
415
{
416 417
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du;
418
  struct notifier_usage *nu;
419 420 421 422 423
  sip_event_t const *o = sip->sip_event;
  sip_subscription_state_t *ss = sip->sip_subscription_state;
  sip_time_t now = sip_now();
    
  if (o == NULL && nh->nh_ds->ds_has_notifys == 1)
424 425 426
    o = NONE;

  du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o);
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

  if (!du) {
    tagi_t const *newsub = tl_find_last(tags, nutag_newsub);

    if (!newsub || !newsub->t_value)
      return 0; /* Rejected eventually by nua_notify_client_request() */

    /* Create new notifier */
    du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
    if (du == NULL)
      return -1;

    nu = nua_dialog_usage_private(du);
    nu->nu_expires = now;
  }
  else
    nu = nua_dialog_usage_private(du);

445

446 447 448 449
  if (nu->nu_substate == nua_substate_terminated) {
    /*Xyzzy*/;
  }
  else if (ss != NULL) {
450
    /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
451
    nu->nu_substate = nua_substate_make(ss->ss_substate);
452

453 454
    if (ss->ss_expires) {
      unsigned long expires = strtoul(ss->ss_expires, NULL, 10);
455 456
      if (now + expires < now)
	expires = SIP_TIME_MAX - now - 1;
457 458 459 460

      /* Notifier can only shorten the subscription time */ 
      if (nu->nu_requested == 0 || nu->nu_requested >= now + expires)
	nu->nu_expires = nu->nu_requested = now + expires;
461 462 463
    }
  }
  else {
464
    enum nua_substate substate = nu->nu_substate;
465

466
    if (nu->nu_expires > now) {
467 468
      tagi_t const *t = tl_find_last(tags, nutag_substate);
      if (t)
469
        substate = (enum nua_substate)t->t_value;
470
    }
471 472
    else
      substate = nua_substate_terminated;
473 474

    switch (substate) {
475 476 477
    case nua_substate_embryonic:
      /*FALLTHROUGH*/
    case nua_substate_pending:
478
      nu->nu_substate = nua_substate_pending;
479 480 481
      break;
    case nua_substate_active:
    default:
482
      nu->nu_substate = nua_substate_active;
483 484
      break;
    case nua_substate_terminated:
485
      nu->nu_substate = nua_substate_terminated;
486 487
      break;
    }
488
  }
489

490 491
  if (nu->nu_substate == nua_substate_terminated)
    cr->cr_terminating = 1;
492

493
  cr->cr_usage = du;
494

495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
  return nua_notify_client_init_etag(cr, msg, sip, tags);
}

static int nua_notify_client_init_etag(nua_client_request_t *cr,
				       msg_t *msg, sip_t *sip,
				       tagi_t const *tags)
{
  nua_handle_t *nh = cr->cr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(cr->cr_usage);
  nua_server_request_t *sr;

  if (nu->nu_tag)
    su_free(nh->nh_home, nu->nu_tag), nu->nu_tag = NULL;
    nu->nu_no_body = 0;

  if (sip->sip_etag) {
    nu->nu_appl_etags = 1;
    nu->nu_tag = su_strdup(nh->nh_home, sip->sip_etag->g_string);
  }
  else if (!nu->nu_appl_etags && nu->nu_etags) {
    su_md5_t md5[1];
    unsigned char digest[SU_MD5_DIGEST_SIZE];
    sip_payload_t pl[1] = { SIP_PAYLOAD_INIT() };
    char token[2 * 16];

    su_md5_init(md5);

    if (sip->sip_payload) *pl = *sip->sip_payload;

    if (pl->pl_len)
      su_md5_update(md5, pl->pl_data, pl->pl_len);
    su_md5_update(md5, &pl->pl_len, sizeof(pl->pl_len));

    if (sip->sip_content_type)
      su_md5_striupdate(md5, sip->sip_content_type->c_type);

    su_md5_digest(md5, digest);
    token64_e(token, sizeof token, digest, sizeof digest);
    token[(sizeof token) - 1] = '\0';
    nu->nu_tag = su_strdup(nh->nh_home, token);
  }

  if (!nu->nu_requested || !nu->nu_tag)
    return 0;

  /* Check if SUBSCRIBE had matching suppression */
  for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
    if (sr->sr_usage == cr->cr_usage && sr->sr_method == sip_method_subscribe)
      break;

  if (sr) {
    sip_t const *sip = sr->sr_request.sip;

    sip_suppress_body_if_match_t *sbim;
    sip_suppress_notify_if_match_t *snim;
    
    if (cr->cr_usage->du_ready) {
      snim = sip_suppress_notify_if_match(sip);

      if (snim && !strcasecmp(snim->snim_tag, nu->nu_tag)) {
	if (nu->nu_requested > nu->nu_expires)
	  nu->nu_expires = nu->nu_requested;
	nu->nu_requested = 0;
	return nua_client_return(cr, 202, "NOTIFY Suppressed", msg);
      }
    }

    sbim = sip_suppress_body_if_match(sip);
    if (sbim && !strcasecmp(sbim->sbim_tag, nu->nu_tag))
      nu->nu_no_body = 1;
  }

567 568
  return 0;
}
569

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
static
int nua_notify_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; 
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  su_home_t *home = msg_home(msg);
  sip_time_t now = sip_now();
  sip_subscription_state_t *ss = sip->sip_subscription_state;
  char const *expires;

  if (du == NULL)		/* Subscription has been terminated */
    return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg);

  assert(du && nu);

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

590 591 592 593 594
  if (nu->nu_requested)
    nu->nu_expires = nu->nu_requested;
  nu->nu_requested = 0;

  if (nu->nu_expires <= now || du->du_shutdown) {
595 596 597 598 599
    nu->nu_substate = nua_substate_terminated;
    expires = "expires=0";
  }
  else {
    expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now);
600 601
  }

602
  if (ss == NULL || nua_substate_make(ss->ss_substate) != nu->nu_substate) {
603 604 605 606 607 608
    if (nu->nu_substate == nua_substate_terminated)
      expires = nu->nu_expires > now ? "noresource" : "timeout";

    ss = sip_subscription_state_format(home, "%s;%s", 
				       nua_substate_name(nu->nu_substate),
				       expires);
609

610 611
    msg_header_insert(msg, (void *)sip, (void *)ss);
  }
612
  else if (nu->nu_substate != nua_substate_terminated) {
613
    msg_header_replace_param(home, ss->ss_common, expires);
614 615
  }

616 617 618 619 620 621 622 623 624
  if (nu->nu_tag && !sip->sip_etag)
    msg_header_add_make(msg, (void *)sip, sip_etag_class, nu->nu_tag);

  if (nu->nu_no_body) {
    nu->nu_no_body = 0;
    msg_header_remove(msg, (void *)sip, (void *)sip->sip_payload);
    msg_header_remove(msg, (void *)sip, (void *)sip->sip_content_length);
  }

625 626
  if (nu->nu_substate == nua_substate_terminated)
    cr->cr_terminating = 1;
627

628 629
  if (du->du_event && !sip->sip_event)
    sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event);
630

631
  return nua_base_client_request(cr, msg, sip, tags);
632 633
}

634
/** @NUA_EVENT nua_r_notify
635
 *
636
 * Response to an outgoing @b NOTIFY request.
637
 *
638 639
 * The @b NOTIFY may be sent explicitly by nua_notify() or implicitly by NUA
 * state machine. Implicit @b NOTIFY is sent when an established dialog is
640 641 642
 * refreshed by client or it is terminated (either by client or because of a
 * timeout)
 *
643 644 645 646 647 648 649 650 651 652 653
 * @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
 * @param sip    response to @b NOTIFY request or NULL upon an error 
 *               (status code is in @a status and 
 *               descriptive message in @a phrase parameters)
 * @param tags   NUTAG_SUBSTATE() indicating subscription state
654
 *               SIPTAG_EVENT() indicating subscription event
655
 *
656 657 658
 * @sa nua_notify(), @RFC3265, #nua_i_subscribe, #nua_i_refer
 *
 * @END_NUA_EVENT
659 660
 */

661 662 663 664 665
static int nua_notify_client_report(nua_client_request_t *cr,
				    int status, char const *phrase,
				    sip_t const *sip,
				    nta_outgoing_t *orq,
				    tagi_t const *tags)
666
{
667 668
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
669
  struct notifier_usage *nu = nua_dialog_usage_private(du);
670 671
  enum nua_substate substate = nua_substate_terminated;

672
  if (nu && !cr->cr_terminated)
673 674
    substate = nu->nu_substate;

675 676 677 678 679 680 681 682 683
  nua_stack_tevent(nh->nh_nua, nh, 
		   nta_outgoing_getresponse(orq),
		   cr->cr_event,
		   status, phrase,
		   NUTAG_SUBSTATE(substate),
		   SIPTAG_EVENT(du ? du->du_event : NULL),
		   TAG_NEXT(tags));

  return 0;
684 685
}

686

687
static void nua_notify_usage_refresh(nua_handle_t *nh,
688
				     nua_dialog_state_t *ds,
689 690 691
				     nua_dialog_usage_t *du,
				     sip_time_t now)
{
692 693
  nua_client_request_t *cr = du->du_cr;
  nua_event_t e = nua_r_notify;
694

695 696
  if (cr) {
    int terminating;
697

698 699
    if (nua_client_is_queued(cr)) /* Already notifying. */
      return;
700

701
    terminating = du->du_expires && du->du_expires <= now;
702

703 704
    if (nua_client_resend_request(cr, terminating, NULL) >= 0)
      return;
705 706
  }
  else {
707 708
    if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0)
      return;
709
  }
710

711 712 713
  nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_INTERNAL_ERROR,
		   NUTAG_SUBSTATE(nua_substate_terminated),
		   TAG_END());
714 715

  nua_dialog_usage_remove(nh, ds, du);
716 717 718 719 720 721 722 723 724
}

/** @interal Shut down NOTIFY usage. 
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
static int nua_notify_usage_shutdown(nua_handle_t *nh,
725
				     nua_dialog_state_t *ds,
726 727
				     nua_dialog_usage_t *du)
{
728 729
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  nua_client_request_t *cr = du->du_cr;
730

731 732
  if (nua_client_is_queued(cr)) /* Already notifying. */
    return -1;  /* Request in progress */
733

734
  nu->nu_substate = nua_substate_terminated;
735

736 737 738 739 740 741 742 743 744
  if (cr) {
    if (nua_client_resend_request(cr, 1, NULL) >= 0)
      return 0;
  }
  else {
    if (nua_client_create(nh, nua_r_notify, 
			  &nua_notify_client_methods, NULL) >= 0)
      return 0;
  }
745

746 747 748
  nua_dialog_usage_remove(nh, ds, du);
  return 200;
}
749 750 751 752

/* ======================================================================== */
/* REFER */
/* RFC 3515 */
753

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
static int nua_refer_server_init(nua_server_request_t *sr);
static int nua_refer_server_preprocess(nua_server_request_t *sr);
static int nua_refer_server_respond(nua_server_request_t*, tagi_t const *);
static int nua_refer_server_report(nua_server_request_t*, tagi_t const *);

nua_server_methods_t const nua_refer_server_methods = 
  {
    SIP_METHOD_REFER,
    nua_i_refer,		/* Event */
    { 
      1,			/* Create dialog */
      0,			/* Initial request */
      1,			/* Target refresh request  */
      1,			/* Add Contact */
    },
    nua_refer_server_init,
    nua_refer_server_preprocess,
    nua_base_server_params,
    nua_refer_server_respond,
    nua_refer_server_report,
  };

static int nua_refer_server_init(nua_server_request_t *sr)
{
  return 0;
}

static int nua_refer_server_preprocess(nua_server_request_t *sr)
{
  nua_handle_t *nh = sr->sr_owner;
  sip_t const *sip = sr->sr_request.sip;
  struct notifier_usage *nu;
  sip_event_t *o;

  if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id))
    o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
  else
    o = sip_event_make(nh->nh_home, "refer");

  if (o) {
    sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
    msg_header_free(nh->nh_home, (msg_header_t *)o);
  }

  if (!sr->sr_usage)
    return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);

  nu = nua_dialog_usage_private(sr->sr_usage);
  nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires);

  return 0;
}

static
int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags)
{
  nua_handle_t *nh = sr->sr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
  sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip);

  if (sr->sr_status < 200 || nu == NULL) {
  }
  else if (sr->sr_status < 300 && 
	   /* Application included Refer-Sub: false in response */
	   (rs == NULL || str0casecmp("false", rs->rs_value))) {
    sr->sr_usage->du_ready = 1;

    nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires);

    if (sr->sr_application)	/* Application responded to REFER */
      nu->nu_substate = nua_substate_active;
  }
  else {
    /* Destroy the implicit subscription usage */
    sr->sr_terminating = 1;
  }

  return nua_base_server_respond(sr, tags);
}

834

835
/** @NUA_EVENT nua_i_refer
836
 *
837
 * Incoming @b REFER request used to transfer calls.
838
 *
839 840 841 842 843
 * @param status status code of response sent automatically by stack
 * @param phrase a short textual description of @a status code
 * @param nh     operation handle associated with the incoming request
 * @param hmagic application context associated with the handle
 *               (NULL if outside of an already established session)
844 845 846
 * @param sip    incoming REFER request
 * @param tags   NUTAG_REFER_EVENT() \n
 *               SIPTAG_REFERRED_BY()
847 848 849
 *  
 * @sa nua_refer(), #nua_r_refer, @ReferTo, NUTAG_REFER_EVENT(), 
 * SIPTAG_REFERRED_BY(), @ReferredBy, NUTAG_NOTIFY_REFER(),
850
 * NUTAG_REFER_WITH_ID(), @RFC3515.
851 852
 *
 * @END_NUA_EVENT
853 854
 */

855 856
static
int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags)
857
{
858 859 860 861 862 863 864
  nua_handle_t *nh = sr->sr_owner;
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
  sip_t const *sip = sr->sr_request.sip;
  sip_referred_by_t *by = sip->sip_referred_by, default_by[1];
  sip_event_t const *o = sr->sr_usage->du_event;
  enum nua_substate substate = nua_substate_terminated;
  int initial = sr->sr_initial, retval;
865

866 867 868
  if (nu) {
    if (!sr->sr_terminating)
      substate = nu->nu_substate;
869
  }
870

871 872
  if (by == NULL) {
     by = sip_referred_by_init(default_by);
873

874 875
    by->b_display = sip->sip_from->a_display;
    *by->b_url = *sip->sip_from->a_url;
876 877
  }

878 879 880 881 882
  retval = nua_base_server_treport(sr,
				   NUTAG_SUBSTATE(substate),
				   NUTAG_REFER_EVENT(o),
				   TAG_IF(by, SIPTAG_REFERRED_BY(by)),
				   TAG_END());
883

884 885
  if (retval >= 2 || nu == NULL)
    return retval;
886

887 888 889 890 891 892 893
  if (initial)
    nua_stack_post_signal(nh,
			  nua_r_notify,
			  SIPTAG_EVENT(o),
			  SIPTAG_CONTENT_TYPE_STR("message/sipfrag"),
			  SIPTAG_PAYLOAD_STR("SIP/2.0 100 Trying\r\n"),
			  TAG_END());
894

895
  return retval;
896
}