nua_notifier.c 30.3 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
  sip_time_t         nu_expires; 	/**< Expiration time */
  sip_time_t         nu_requested;      /**< Requested expiration time */
64
#if SU_HAVE_EXPERIMENTAL
65 66 67 68
  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 */
69
#endif
70 71 72
};

static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);
73
static int nua_notify_usage_add(nua_handle_t *nh,
74 75
				   nua_dialog_state_t *ds,
				   nua_dialog_usage_t *du);
76
static void nua_notify_usage_remove(nua_handle_t *nh,
77 78 79 80
				    nua_dialog_state_t *ds,
				    nua_dialog_usage_t *du,
				    nua_client_request_t *cr,
				    nua_server_request_t *sr);
81
static void nua_notify_usage_refresh(nua_handle_t *nh,
82
				     nua_dialog_state_t *ds,
83 84 85
				     nua_dialog_usage_t *du,
				     sip_time_t now);
static int nua_notify_usage_shutdown(nua_handle_t *nh,
86
				     nua_dialog_state_t *ds,
87
				     nua_dialog_usage_t *du);
88 89 90 91 92 93 94

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,
95
    nua_base_usage_update_params,
96 97 98
    NULL,
    nua_notify_usage_refresh,
    nua_notify_usage_shutdown,
99 100 101 102 103 104 105
  }};

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

106 107
static
int nua_notify_usage_add(nua_handle_t *nh,
108 109
			 nua_dialog_state_t *ds,
			 nua_dialog_usage_t *du)
110 111 112 113 114 115
{
  ds->ds_has_events++;
  ds->ds_has_notifys++;
  return 0;
}

116 117
static
void nua_notify_usage_remove(nua_handle_t *nh,
118 119 120 121
			     nua_dialog_state_t *ds,
			     nua_dialog_usage_t *du,
			     nua_client_request_t *cr,
			     nua_server_request_t *sr)
122
{
123 124
  ds->ds_has_events--;
  ds->ds_has_notifys--;
125 126 127 128 129
}

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

130
/** @NUA_EVENT nua_i_subscribe
131
 *
132 133 134 135
 * Incoming @b SUBSCRIBE request.
 *
 * @b SUBSCRIBE request is used to query SIP event state or establish a SIP
 * event subscription.
136
 *
137 138 139 140 141 142 143 144
 * @param status status code of response sent automatically by stack
 * @param phrase response phrase sent automatically by stack
 * @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)
 * @param sip    SUBSCRIBE request headers
 * @param tags   NUTAG_SUBSTATE()
 *
145 146 147
 * 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()
148 149 150
 * (or SIPTAG_ALLOW_EVENTS() or SIPTAG_ALLOW_EVENTS_STR()).
 *
 * If the event has been allowed the application
151 152
 * can decide whether to accept the SUBSCRIBE request or reject it. The
 * nua_response() call responding to a SUBSCRIBE request must have
153
 * NUTAG_WITH() (or NUTAG_WITH_THIS()/NUTAG_WITH_SAVED()) tag.
154 155 156
 *
 * If the application accepts the SUBSCRIBE request, it must immediately
 * send an initial NOTIFY establishing the dialog. This is because the
157 158
 * response to the SUBSCRIBE request may be lost by an intermediate proxy
 * because it had forked the SUBSCRIBE request.
159 160 161 162
 *
 * 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
163
 * automatically to the subscriber.
164 165 166 167 168
 *
 * 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.
 *
169 170 171 172 173 174 175
 * @par Subscription Lifetime and Terminating Subscriptions
 *
 * Accepting the SUBSCRIBE request creates a dialog with a <i>notifier
 * dialog usage</i> on the handle. The dialog usage is active, until the
 * subscriber terminates the subscription, it times out or the application
 * terminates the usage with nua_notify() call containing the tag
 * NUTAG_SUBSTATE(nua_substate_terminated) or @SubscriptionState header with
176
 * state "terminated" and/or expiration time 0.
177 178 179 180 181 182
 *
 * When the subscriber terminates the subscription, the application is
 * notified of an termination by a #nua_i_subscribe event with
 * NUTAG_SUBSTATE(nua_substate_terminated) tag. When the subscription times
 * out, nua automatically initiates a NOTIFY transaction. When it is
 * terminated, the application is sent a #nua_r_notify event with
183
 * NUTAG_SUBSTATE(nua_substate_terminated) tag.
184 185 186 187 188
 *
 * @sa @RFC3265, nua_notify(), NUTAG_SUBSTATE(), @SubscriptionState,
 * @Event, nua_subscribe(), #nua_r_subscribe, #nua_i_refer, nua_refer()
 *
 * @END_NUA_EVENT
189 190
 */

191 192 193 194 195
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 *);

196
nua_server_methods_t const nua_subscribe_server_methods =
197 198 199
  {
    SIP_METHOD_SUBSCRIBE,
    nua_i_subscribe,		/* Event */
200
    {
201 202 203 204 205 206 207 208 209 210 211 212 213
      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)
214
{
215 216 217 218
  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;
219
  sip_event_t *o = sip->sip_event;
220
  char const *event = o ? o->o_type : NULL;
221

222
  if (sr->sr_initial || !nua_dialog_usage_get(ds, nua_notify_usage, o)) {
223 224
    if (event && str0cmp(event, "refer") == 0)
      /* refer event subscription should be initiated with REFER */
225 226
      return SR_STATUS1(sr, SIP_403_FORBIDDEN);

227
    /* XXX - event is case-sensitive, should use msg_header_find_item() */
228 229
    if (!event || !msg_header_find_param(allow_events->k_common, event))
      return SR_STATUS1(sr, SIP_489_BAD_EVENT);
230
  }
231

232 233
  return 0;
}
234

235 236 237 238 239 240 241 242 243 244
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 */
245 246
  unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600;
  sip_time_t now = sip_now();
247

248
  assert(nh && nh->nh_nua->nua_dhandle != nh);
249

250
  du = nua_dialog_usage_get(ds, nua_notify_usage, o);
251

252 253 254 255 256 257 258 259 260 261
  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);
262 263

    SR_STATUS1(sr, SIP_200_OK);
264 265
  }

266
  nu = nua_dialog_usage_private(du);
267

268 269 270 271
  if (now + expires >= now)
    nu->nu_requested = now + expires;
  else
    nu->nu_requested = SIP_TIME_MAX - 1;
272

273
#if SU_HAVE_EXPERIMENTAL
274
  nu->nu_etags =
275 276 277
    sip_suppress_body_if_match(sip) ||
    sip_suppress_notify_if_match(sip) ||
    sip_has_feature(sr->sr_request.sip->sip_supported, "etags");
278 279
#endif

280
  sr->sr_usage = du;
281

282
  return sr->sr_status <= 100 ? 0 : sr->sr_status;
283 284
}

285
/** @internal Respond to a SUBSCRIBE request.
286 287 288
 *
 */
static
289
int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags)
290
{
291
  struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage);
292

293 294
  msg_t *msg = sr->sr_response.msg;
  sip_t *sip = sr->sr_response.sip;
295

296
  if (200 <= sr->sr_status && sr->sr_status < 300) {
297
    sip_expires_t ex[1];
298

299
    sip_expires_init(ex);
300

301 302
    if (nu) {
      sip_time_t now = sip_now();
303

304
      if (nu->nu_requested) {
305 306
	if (sip->sip_expires) {
	  /* Expires in response can only shorten the expiration time */
307
	  if (nu->nu_requested > now + sip->sip_expires->ex_delta)
308 309 310 311 312 313 314 315 316
	    nu->nu_requested = now + sip->sip_expires->ex_delta;
	}
	else {
	  unsigned sub_expires = NH_PGET(sr->sr_owner, sub_expires);
	  if (nu->nu_requested > now + sub_expires)
	    nu->nu_requested = now + sub_expires;
	}

	if (nu->nu_requested >= now)
317
	  nu->nu_expires = nu->nu_requested;
318 319 320 321
	else
	  nu->nu_expires = now;

	if (nu->nu_expires <= now)
322 323
	  nu->nu_substate = nua_substate_terminated;
      }
324

325 326 327 328
      if (nu->nu_expires > now)
	ex->ex_delta = nu->nu_expires - now;
    }
    else {
329
      /* Always add header Expires: 0 */
330
    }
331

332 333
    if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta)
      sip_add_dup(msg, sip, (sip_header_t *)ex);
334
  }
335 336 337 338 339 340 341 342 343

  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;
344 345
  nua_dialog_usage_t *du = sr->sr_usage;
  struct notifier_usage *nu = nua_dialog_usage_private(du);
346 347 348 349 350 351 352 353 354 355
  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) {
356
#if SU_HAVE_EXPERIMENTAL
357 358 359
    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);
360

361 362 363 364 365 366
    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;
367
    else
368
#endif
369
      notify = 1;
370 371

    notify = notify && du->du_cr != NULL;
372 373 374 375
  }

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

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

379 380
  if (notify) {
    /* Send NOTIFY (and terminate subscription, when needed) */
381
    nua_dialog_usage_refresh(nh, ds, du, sip_now());
382
  }
383

384
  return retval;
385
}
386 387

/* ======================================================================== */
388
/* NOTIFY client */
389

390 391 392 393 394 395
/**@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
396 397 398
 * @Event headers using SIP tags. The subscription state can be modified
 * with NUTAG_SUBSTATE(), however, its effect is overriden by
 * @SubscriptionState header included in the nua_notify() tags.
399 400 401
 *
 * @bug If the @Event is not given by application, stack uses the @Event
 * header from the first subscription usage on handle.
402
 *
403 404 405 406 407 408 409 410 411
 * If there is no active <i>notifier dialog usage</i> or no notifier dialog
 * usage matches the @Event header given by the application the nua_notify()
 * request is rejected locally by the stack with status code 481. The local
 * rejection can be bypassed if NUTAG_NEWSUB(1) is included in tags.
 *
 * Please note that including NUTAG_NEWSUB(1) in nua_notify() tags if there
 * is a valid subscription may lead to an extra NOTIFY sent to subscriber if
 * the subscription had been terminated by the subscriber or by a timeout
 * before the nua_notify() is processed.
412 413 414 415
 *
 * @param nh              Pointer to operation handle
 * @param tag, value, ... List of tagged parameters
 *
416
 * @return
417 418 419
 *    nothing
 *
 * @par Related Tags:
420
 *    NUTAG_SUBSTATE() \n
421
 *    NUTAG_NEWSUB() \n
422
 *    Tags of nua_set_hparams() \n
423
 *    Header tags defined in <sofia-sip/sip_tag.h>
424 425 426 427 428 429 430
 *
 * @par Events:
 *    #nua_r_notify
 *
 * @sa @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_ALLOW_EVENTS()
 */

431
static int nua_notify_client_init(nua_client_request_t *cr,
432 433
				  msg_t *, sip_t *,
				  tagi_t const *tags);
434 435 436
static int nua_notify_client_init_etag(nua_client_request_t *cr,
				       msg_t *msg, sip_t *sip,
				       tagi_t const *tags);
437 438 439
static int nua_notify_client_request(nua_client_request_t *cr,
				     msg_t *, sip_t *,
				     tagi_t const *tags);
440 441 442 443 444
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);
445

446
static nua_client_methods_t const nua_notify_client_methods = {
447 448 449
  SIP_METHOD_NOTIFY,		/* crm_method, crm_method_name */
  0,				/* crm_extra */
  {				/* crm_flags */
450 451 452 453
    /* create_dialog */ 1,
    /* in_dialog */ 1,
    /* target refresh */ 1
  },
454 455 456 457 458 459 460 461
  NULL,				/* crm_template */
  nua_notify_client_init,	/* crm_init */
  nua_notify_client_request,	/* crm_send */
  NULL,				/* crm_check_restart */
  NULL,				/* crm_recv */
  NULL,				/* crm_preliminary */
  nua_notify_client_report,	/* crm_report */
  NULL,				/* crm_complete */
462 463
};

464 465 466 467 468
/**@internal Send NOTIFY. */
int nua_stack_notify(nua_t *nua,
		     nua_handle_t *nh,
		     nua_event_t e,
		     tagi_t const *tags)
469
{
470
  return nua_client_create(nh, e, &nua_notify_client_methods, tags);
471 472
}

473 474 475
static int nua_notify_client_init(nua_client_request_t *cr,
				  msg_t *msg, sip_t *sip,
				  tagi_t const *tags)
476
{
477 478
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du;
479
  struct notifier_usage *nu;
480 481 482
  sip_event_t const *o = sip->sip_event;
  sip_subscription_state_t *ss = sip->sip_subscription_state;
  sip_time_t now = sip_now();
483

484
  if (o == NULL && nh->nh_ds->ds_has_notifys == 1)
485 486 487
    o = NONE;

  du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o);
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505

  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);

506

507 508 509 510
  if (nu->nu_substate == nua_substate_terminated) {
    /*Xyzzy*/;
  }
  else if (ss != NULL) {
511
    /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
512
    nu->nu_substate = nua_substate_make(ss->ss_substate);
513

514 515
    if (ss->ss_expires) {
      unsigned long expires = strtoul(ss->ss_expires, NULL, 10);
516 517
      if (now + expires < now)
	expires = SIP_TIME_MAX - now - 1;
518

519 520 521
      /* We can change the lifetime of unsolicited subscription at will */
      if (nu->nu_requested == 0)
	nu->nu_expires = nu->nu_requested = now + expires;
522
      /* Notifier can only shorten the subscribed time */
523
      else if (nu->nu_requested >= now + expires)
524
	nu->nu_expires = nu->nu_requested = now + expires;
525
    }
526 527 528 529 530
    else {
      if (nu->nu_requested >= nu->nu_expires)
	nu->nu_expires = nu->nu_requested;
    }

531 532
  }
  else {
533
    enum nua_substate substate = nu->nu_substate;
534

535 536 537
    if (nu->nu_requested >= nu->nu_expires)
      nu->nu_expires = nu->nu_requested;

538
    if (nu->nu_expires > now) {
539 540
      tagi_t const *t = tl_find_last(tags, nutag_substate);
      if (t)
541
        substate = (enum nua_substate)t->t_value;
542
    }
543 544
    else
      substate = nua_substate_terminated;
545 546

    switch (substate) {
547 548 549
    case nua_substate_embryonic:
      /*FALLTHROUGH*/
    case nua_substate_pending:
550
      nu->nu_substate = nua_substate_pending;
551 552 553
      break;
    case nua_substate_active:
    default:
554
      nu->nu_substate = nua_substate_active;
555 556
      break;
    case nua_substate_terminated:
557
      nu->nu_substate = nua_substate_terminated;
558 559
      break;
    }
560
  }
561

562
  cr->cr_usage = du;
563

564 565 566 567 568 569 570
  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)
{
571
#if SU_HAVE_EXPERIMENTAL
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
  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;
620

621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    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;
  }
636
#endif
637

638 639
  return 0;
}
640

641 642 643 644 645
static
int nua_notify_client_request(nua_client_request_t *cr,
			      msg_t *msg, sip_t *sip,
			      tagi_t const *tags)
{
646
  nua_dialog_usage_t *du = cr->cr_usage;
647 648 649 650 651 652 653 654 655 656 657 658 659 660
  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;

661 662 663 664 665
  if (nu->nu_requested)
    nu->nu_expires = nu->nu_requested;
  nu->nu_requested = 0;

  if (nu->nu_expires <= now || du->du_shutdown) {
666 667 668 669 670
    nu->nu_substate = nua_substate_terminated;
    expires = "expires=0";
  }
  else {
    expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now);
671 672
  }

673
  if (ss == NULL || nua_substate_make(ss->ss_substate) != nu->nu_substate) {
674 675 676
    if (nu->nu_substate == nua_substate_terminated)
      expires = nu->nu_expires > now ? "noresource" : "timeout";

677
    ss = sip_subscription_state_format(home, "%s;%s",
678 679
				       nua_substate_name(nu->nu_substate),
				       expires);
680

681 682
    msg_header_insert(msg, (void *)sip, (void *)ss);
  }
683
  else if (nu->nu_substate != nua_substate_terminated) {
684
    msg_header_replace_param(home, ss->ss_common, expires);
685 686
  }

687
#if SU_HAVE_EXPERIMENTAL
688 689 690 691 692 693 694 695
  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);
  }
696
#endif
697

698
  if (nu->nu_substate == nua_substate_terminated)
699
    nua_client_set_terminating(cr, 1);
700

701 702 703 704 705 706
  if (cr->cr_terminating) {
    nua_server_request_t *sr;
    for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) {
      if (sr->sr_usage == du) {
	/* If subscribe has not been responded, don't terminate usage by NOTIFY */
	sr->sr_terminating = 1;
707
	nua_client_set_terminating(cr, 0);
708 709 710 711 712
	break;
      }
    }
  }

713 714
  if (du->du_event && !sip->sip_event)
    sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event);
715

716
  return nua_base_client_request(cr, msg, sip, tags);
717 718
}

719
/** @NUA_EVENT nua_r_notify
720
 *
721
 * Response to an outgoing @b NOTIFY request.
722
 *
723 724
 * 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
725
 * refreshed by client or it is terminated (either by client or because of a
726 727 728 729 730 731
 * timeout).
 *
 * The current subscription state is included in NUTAG_SUBSTATE() tag. The
 * nua_substate_terminated indicates that the subscription is terminated,
 * the notifier usage has been removed and when there was no other usages of
 * the dialog the dialog state is also removed.
732
 *
733 734 735 736 737 738 739
 * @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
740 741
 * @param sip    response to @b NOTIFY request or NULL upon an error
 *               (status code is in @a status and
742 743
 *               descriptive message in @a phrase parameters)
 * @param tags   NUTAG_SUBSTATE() indicating subscription state
744
 *               SIPTAG_EVENT() indicating subscription event
745
 *
746
 * @sa nua_notify(), @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_SUBSTATE()
747 748
 *
 * @END_NUA_EVENT
749 750
 */

751 752 753 754 755
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)
756
{
757 758
  nua_handle_t *nh = cr->cr_owner;
  nua_dialog_usage_t *du = cr->cr_usage;
759
  struct notifier_usage *nu = nua_dialog_usage_private(du);
760 761
  enum nua_substate substate = nua_substate_terminated;

762
  if (nu && !cr->cr_terminated)
763 764
    substate = nu->nu_substate;

765
  nua_stack_tevent(nh->nh_nua, nh,
766 767 768 769 770 771 772
		   nta_outgoing_getresponse(orq),
		   cr->cr_event,
		   status, phrase,
		   NUTAG_SUBSTATE(substate),
		   SIPTAG_EVENT(du ? du->du_event : NULL),
		   TAG_NEXT(tags));

773 774 775 776 777 778
  if (du && du->du_cr == cr && !cr->cr_terminated) {
    if (nu->nu_requested) {
      /* Re-SUBSCRIBEd while NOTIFY was in progress, resend NOTIFY */
      nua_client_resend_request(cr, 0);
    }
    else if (nu->nu_expires) {
779
      nua_dialog_usage_set_refresh_at(du, nu->nu_expires);
780 781
    }
  }
782

783
  return 0;
784 785
}

786

787
static void nua_notify_usage_refresh(nua_handle_t *nh,
788
				     nua_dialog_state_t *ds,
789 790 791
				     nua_dialog_usage_t *du,
				     sip_time_t now)
{
792
  struct notifier_usage *nu = nua_dialog_usage_private(du);
793 794
  nua_client_request_t *cr = du->du_cr;
  nua_event_t e = nua_r_notify;
795

796
  if (cr) {
797
    int terminating = 0;
798

799
    if (nu->nu_expires && nu->nu_expires <= now)
800 801 802
      terminating = 1;
    else if (nu->nu_requested && nu->nu_requested <= now)
      terminating = 1;
803

804
    if (nua_client_resend_request(cr, terminating) >= 0)
805
      return;
806 807
  }
  else {
808 809
    if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0)
      return;
810
  }
811

812
  nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__),
813 814
		   NUTAG_SUBSTATE(nua_substate_terminated),
		   TAG_END());
815

816
  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
817 818
}

819
/** @interal Shut down NOTIFY usage.
820 821 822 823 824 825
 *
 * @retval >0  shutdown done
 * @retval 0   shutdown in progress
 * @retval <0  try again later
 */
static int nua_notify_usage_shutdown(nua_handle_t *nh,
826
				     nua_dialog_state_t *ds,
827 828
				     nua_dialog_usage_t *du)
{
829 830
  struct notifier_usage *nu = nua_dialog_usage_private(du);
  nua_client_request_t *cr = du->du_cr;
831

832
  nu->nu_substate = nua_substate_terminated;
833

834
  if (cr) {
835
    if (nua_client_resend_request(cr, 1) >= 0)
836 837 838
      return 0;
  }
  else {
839 840
    if (nua_client_tcreate(nh, nua_r_notify,
			   &nua_notify_client_methods,
841 842 843
			   SIPTAG_EVENT(du->du_event),
			   NUTAG_SUBSTATE(nua_substate_terminated),
			   TAG_END()) >= 0)
844 845
      return 0;
  }
846

847
  nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
848 849
  return 200;
}
850 851 852 853

/* ======================================================================== */
/* REFER */
/* RFC 3515 */
854

855 856 857 858 859
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 *);

860
nua_server_methods_t const nua_refer_server_methods =
861 862 863
  {
    SIP_METHOD_REFER,
    nua_i_refer,		/* 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 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
      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) {
  }
917
  else if (sr->sr_status < 300 &&
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
	   /* 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);
}

935

936
/** @NUA_EVENT nua_i_refer
937
 *
938 939 940 941 942 943 944
 * Incoming @b REFER request used to transfer calls. The tag list will
 * contain tag NUTAG_REFER_EVENT() with the @Event header constructed from
 * the REFER request. It will also contain the SIPTAG_REFERRED_BY() tag with
 * the @ReferredBy header containing the identity of the party sending the
 * REFER. The @ReferredBy structure contained in the tag is constructed from
 * the @From header if the @ReferredBy header was not present in the REFER
 * request.
945
 *
946 947 948 949
 * The application can let the nua to send NOTIFYs from the call it
 * initiates with nua_invite() if it includes in the nua_invite() arguments
 * both the NUTAG_NOTIFY_REFER() with the handle with which nua_i_refer was
 * received and the NUTAG_REFER_EVENT() from #nua_i_refer event tags.
950
 *
951 952 953 954 955
 * @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)
956 957 958
 * @param sip    incoming REFER request
 * @param tags   NUTAG_REFER_EVENT() \n
 *               SIPTAG_REFERRED_BY()
959 960
 *
 * @sa nua_refer(), #nua_r_refer, @ReferTo, NUTAG_REFER_EVENT(),
961
 * SIPTAG_REFERRED_BY(), @ReferredBy, NUTAG_NOTIFY_REFER(),
962
 * NUTAG_REFER_WITH_ID(), @RFC3515.
963 964
 *
 * @END_NUA_EVENT
965 966
 */

967 968
static
int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags)
969
{
970 971 972 973 974 975 976
  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;
977

978 979 980
  if (nu) {
    if (!sr->sr_terminating)
      substate = nu->nu_substate;
981
  }
982

983 984
  if (by == NULL) {
     by = sip_referred_by_init(default_by);
985

986 987
    by->b_display = sip->sip_from->a_display;
    *by->b_url = *sip->sip_from->a_url;
988 989
  }

990 991 992 993 994
  retval = nua_base_server_treport(sr,
				   NUTAG_SUBSTATE(substate),
				   NUTAG_REFER_EVENT(o),
				   TAG_IF(by, SIPTAG_REFERRED_BY(by)),
				   TAG_END());
995

996 997
  if (retval >= 2 || nu == NULL)
    return retval;
998

999 1000 1001 1002 1003 1004 1005
  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());
1006

1007
  return retval;
1008
}