sip_feature.c 17.1 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * 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
 *
 */

25
/**@CFILE sip_feature.c
Pekka Pessi's avatar
Pekka Pessi committed
26 27 28 29 30 31 32 33 34 35
 *
 * @brief Feature-related SIP header handling
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
 *
 * @date Created: Tue Jun 13 02:57:51 2000 ppessi
 */

#include "config.h"

36 37 38 39 40 41
/* Avoid casting sip_t to msg_pub_t and sip_header_t to msg_header_t */
#define MSG_PUB_T       struct sip_s
#define MSG_HDR_T       union sip_header_u

#include "sofia-sip/sip_parser.h"

42
#include <stdio.h>
Pekka Pessi's avatar
Pekka Pessi committed
43 44 45 46 47 48 49 50 51 52
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* ====================================================================== */

/**@SIP_HEADER sip_allow Allow Header
 *
 * The Allow header lists the set of methods supported by the user agent
53
 * generating the message.  Its syntax is defined in @RFC3261 as
Pekka Pessi's avatar
Pekka Pessi committed
54
 * follows:
55
 *
Pekka Pessi's avatar
Pekka Pessi committed
56 57 58 59
 * @code
 *    Allow  =  "Allow" HCOLON [Method *(COMMA Method)]
 * @endcode
 *
60
 * The parsed Allow header is stored in #sip_allow_t structure.
61 62 63 64 65 66
 *
 * Note that SIP methods are case-sensitive: "INVITE" method is different from
 * "Invite".
 *
 * @sa msg_header_find_item(), msg_header_replace_item(),
 * msg_header_remove_item()
Pekka Pessi's avatar
Pekka Pessi committed
67 68
 */

69
/**@ingroup sip_allow
70
 * @typedef struct msg_list_s sip_allow_t;
71
 *
72
 * The structure #sip_allow_t contains representation of an @Allow header.
73
 *
74
 * The #sip_allow_t is defined as follows:
75
 * @code
76
 * typedef struct msg_allow_s
77 78 79 80
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
 *   msg_list_t        *k_next;	      // Link to next header
 *   msg_param_t       *k_items;      // List of items
81
 *   uint32_t           k_bitmap;     // Bitmap of allowed methods
82 83
 * } sip_allow_t;
 * @endcode
84 85
 *
 * @note The field @a k_bitmap was added in @VERSION_1_12_5.
86
 */
Pekka Pessi's avatar
Pekka Pessi committed
87

88 89 90 91
#define sip_allow_dup_xtra msg_list_dup_xtra
#define sip_allow_dup_one  msg_list_dup_one
static msg_update_f sip_allow_update;

92
msg_hclass_t sip_allow_class[] =
93
SIP_HEADER_CLASS(allow, "Allow", "", k_items, list, allow);
Pekka Pessi's avatar
Pekka Pessi committed
94

95
issize_t sip_allow_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
96 97
{
  sip_allow_t *k = (sip_allow_t *)h;
98 99 100
  issize_t retval = msg_commalist_d(home, &s, &k->k_items, msg_token_scan);
  msg_header_update_params(k->k_common, 0);
  return retval;
Pekka Pessi's avatar
Pekka Pessi committed
101 102
}

103
issize_t sip_allow_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
Pekka Pessi's avatar
Pekka Pessi committed
104 105 106 107 108
{
  assert(sip_is_allow(h));
  return msg_list_e(b, bsiz, h, f);
}

109
static int sip_allow_update(msg_common_t *h,
110 111 112 113 114 115 116 117 118 119 120
			  char const *name, isize_t namelen,
			  char const *value)
{
  sip_allow_t *k = (sip_allow_t *)h;

  if (name == NULL) {
    k->k_bitmap = 0;
  }
  else {
    sip_method_t method = sip_method_code(name);

121
    if (method >= 0 && method < 32)
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
      k->k_bitmap |= 1 << method;
  }

  return 0;
}

/** Return true if the method is listed in @Allow header. */
int sip_is_allowed(sip_allow_t const *allow,
		   sip_method_t method,
		   char const *name)
{
  if (method < sip_method_unknown || !allow)
    return 0;

  if (sip_method_unknown < method && method < 32)
    /* Well-known method */
    return (allow->k_bitmap & (1 << method)) != 0;

  if (method == sip_method_unknown &&
      (allow->k_bitmap & (1 << sip_method_unknown)) == 0)
    return 0;

  return msg_header_find_item(allow->k_common, name) != NULL;
}


Pekka Pessi's avatar
Pekka Pessi committed
148 149 150 151 152
/* ====================================================================== */

/**@SIP_HEADER sip_proxy_require Proxy-Require Header
 *
 * The Proxy-Require header is used to indicate proxy-sensitive features
153
 * that @b MUST be supported by the proxy.  Its syntax is defined in @RFC3261
Pekka Pessi's avatar
Pekka Pessi committed
154
 * as follows:
155
 *
Pekka Pessi's avatar
Pekka Pessi committed
156 157 158 159
 * @code
 *    Proxy-Require  =  "Proxy-Require" HCOLON option-tag *(COMMA option-tag)
 * @endcode
 *
160 161
 *
 * The parsed Proxy-Require header is stored in #sip_proxy_require_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
162 163
 */

164
/**@ingroup sip_proxy_require
165
 * @typedef struct msg_list_s sip_proxy_require_t;
166
 *
167
 * The structure #sip_proxy_require_t contains representation of an
168
 * @ProxyRequire header.
169
 *
170
 * The #sip_proxy_require_t is defined as follows:
171 172 173 174
 * @code
 * typedef struct msg_list_s
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
175
 *   msg_list_t        *k_next;	      // Dummy link
176 177 178 179 180
 *   msg_param_t       *k_items;      // List of items
 * } sip_proxy_require_t;
 * @endcode
 */

181
msg_hclass_t sip_proxy_require_class[] =
Pekka Pessi's avatar
Pekka Pessi committed
182 183
SIP_HEADER_CLASS_LIST(proxy_require, "Proxy-Require", "", list);

184
issize_t sip_proxy_require_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
185 186 187 188 189
{
  sip_proxy_require_t *k = (sip_proxy_require_t *)h;
  return msg_commalist_d(home, &s, &k->k_items, msg_token_scan);
}

190
issize_t sip_proxy_require_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
Pekka Pessi's avatar
Pekka Pessi committed
191 192 193 194 195 196 197 198 199 200 201
{
  assert(sip_is_proxy_require(h));
  return msg_list_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_require Require Header
 *
 * The Require header is used by clients to tell user agent servers about
 * options that the client expects the server to support in order to
202
 * properly process the request.  Its syntax is defined in @RFC3261
Pekka Pessi's avatar
Pekka Pessi committed
203
 * as follows:
204
 *
Pekka Pessi's avatar
Pekka Pessi committed
205 206 207 208
 * @code
 *    Require       =  "Require" HCOLON option-tag *(COMMA option-tag)
 * @endcode
 *
209
 * The parsed Require header is stored in #sip_require_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
210 211
 */

212
/**@ingroup sip_require
213
 * @typedef struct msg_list_s sip_require_t;
214
 *
215
 * The structure #sip_require_t contains representation of an
216
 * @Require header.
217
 *
218
 * The #sip_require_t is defined as follows:
219 220 221 222 223 224 225 226 227 228
 * @code
 * typedef struct msg_list_s
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
 *   msg_list_t        *k_next;	      // Link to next header
 *   msg_param_t       *k_items;      // List of items
 * } sip_require_t;
 * @endcode
 */

229
msg_hclass_t sip_require_class[] =
Pekka Pessi's avatar
Pekka Pessi committed
230 231
SIP_HEADER_CLASS_LIST(require, "Require", "", list);

232
issize_t sip_require_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
233 234 235 236 237
{
  sip_require_t *k = (sip_require_t *)h;
  return msg_commalist_d(home, &s, &k->k_items, msg_token_scan);
}

238
issize_t sip_require_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
Pekka Pessi's avatar
Pekka Pessi committed
239 240 241 242 243 244 245 246 247 248
{
  assert(sip_is_require(h));
  return msg_list_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_supported Supported Header
 *
 * The Supported header enumerates all the capabilities of the client or
249
 * server.  Its syntax is defined in @RFC3261 as follows:
250
 *
Pekka Pessi's avatar
Pekka Pessi committed
251 252 253 254 255
 * @code
 *    Supported  =  ( "Supported" / "k" ) HCOLON
 *                  [option-tag *(COMMA option-tag)]
 * @endcode
 *
256 257
 * The parsed option-tags of Supported header
 * are stored in #sip_supported_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
258 259
 */

260
/**@ingroup sip_supported
261
 * @typedef struct msg_list_s sip_supported_t;
262
 *
263
 * The structure #sip_supported_t contains representation of an
264
 * @Supported header.
265
 *
266
 * The #sip_supported_t is defined as follows:
267 268 269 270 271 272 273 274 275 276 277
 * @code
 * typedef struct msg_list_s
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
 *   msg_list_t        *k_next;	      // Link to next header
 *   msg_param_t       *k_items;      // List of items
 * } sip_supported_t;
 * @endcode
 */


278
msg_hclass_t sip_supported_class[] =
Pekka Pessi's avatar
Pekka Pessi committed
279 280
SIP_HEADER_CLASS_LIST(supported, "Supported", "k", list);

281
issize_t sip_supported_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
282 283 284 285 286
{
  sip_supported_t *k = (sip_supported_t *)h;
  return msg_commalist_d(home, &s, &k->k_items, msg_token_scan);
}

287
issize_t sip_supported_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
Pekka Pessi's avatar
Pekka Pessi committed
288 289 290 291 292 293 294 295 296 297
{
  assert(sip_is_supported(h));
  return msg_list_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_unsupported Unsupported Header
 *
 * The Unsupported header lists the features not supported by the server.
298
 * Its syntax is defined in @RFC3261 as follows:
299
 *
Pekka Pessi's avatar
Pekka Pessi committed
300
 * @code
301
 *    Unsupported  =  "Unsupported" HCOLON [option-tag *(COMMA option-tag)]
Pekka Pessi's avatar
Pekka Pessi committed
302 303
 * @endcode
 *
304 305
 *
 * The parsed Unsupported header is stored in #sip_unsupported_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
306 307
 */

308
/**@ingroup sip_unsupported
309
 * @typedef struct msg_list_s sip_unsupported_t;
310
 *
311
 * The structure #sip_unsupported_t contains representation of an
312
 * @Unsupported header.
313
 *
314
 * The #sip_unsupported_t is defined as follows:
315 316 317 318 319 320 321 322 323 324
 * @code
 * typedef struct msg_list_s
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
 *   msg_list_t        *k_next;	      // Link to next header
 *   msg_param_t       *k_items;      // List of items
 * } sip_unsupported_t;
 * @endcode
 */

325
msg_hclass_t sip_unsupported_class[] =
Pekka Pessi's avatar
Pekka Pessi committed
326 327
SIP_HEADER_CLASS_LIST(unsupported, "Unsupported", "", list);

328
issize_t sip_unsupported_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
329 330 331 332 333
{
  sip_unsupported_t *k = (sip_unsupported_t *)h;
  return msg_commalist_d(home, &s, &k->k_items, msg_token_scan);
}

334
issize_t sip_unsupported_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
Pekka Pessi's avatar
Pekka Pessi committed
335 336 337 338 339
{
  assert(sip_is_unsupported(h));
  return msg_list_e(b, bsiz, h, f);
}

340
/** Check if required feature is not supported.
341 342
 *
 * @retval NULL if all the required features are supported
343
 * @retval pointer to a @Unsupported header or
344
 *         #SIP_NONE if @a home is NULL
Pekka Pessi's avatar
Pekka Pessi committed
345 346
 */
sip_unsupported_t *sip_has_unsupported(su_home_t *home,
347
				       sip_supported_t const *support,
Pekka Pessi's avatar
Pekka Pessi committed
348 349
				       sip_require_t const *require)
{
350
  return sip_has_unsupported_any(home, support, NULL, NULL,
Pekka Pessi's avatar
Pekka Pessi committed
351 352 353 354
				 require, NULL, NULL);
}


355
/** Check if required feature is not supported.
Pekka Pessi's avatar
Pekka Pessi committed
356 357
 *
 * @retval NULL if all the required features are supported
358
 * @retval pointer to a @Unsupported header or
359
 *         #SIP_NONE if @a home is NULL
Pekka Pessi's avatar
Pekka Pessi committed
360 361 362 363 364 365 366
 */
sip_unsupported_t *
sip_has_unsupported2(su_home_t *home,
		     sip_supported_t const *support,
		     sip_require_t const *support_by_require,
		     sip_require_t const *require)
{
367 368 369
  return
    sip_has_unsupported_any(home,
			    support, support_by_require, NULL,
Pekka Pessi's avatar
Pekka Pessi committed
370 371 372 373
			    require, NULL, NULL);
}


374
/** Check if required features are not supported.
Pekka Pessi's avatar
Pekka Pessi committed
375
 *
376
 * The supported features can be listed in @Supported, @Require or
377
 * @ProxyRequire headers (in @a supported, @a by_require, or @a
378
 * by_proxy_require parameters, respectively)
Pekka Pessi's avatar
Pekka Pessi committed
379
 *
380 381
 * @param home (optional) home pointer for allocating @Unsupported header
 * @param supported @Supported features (may be NULL) [IN]
382 383 384
 * @param by_require  supported features listed by
 *                    @Require (may be NULL) [IN]
 * @param by_proxy_require supported features listed
385
 *                         by @ProxyRequire (may be NULL) [IN]
Pekka Pessi's avatar
Pekka Pessi committed
386 387 388 389 390 391
 *
 * @param require   list of required features (may be NULL) [IN]
 * @param require2  2nd list of required features (may be NULL) [IN]
 * @param require3  3rd list of required features (may be NULL) [IN]
 *
 * @retval NULL if all the required features are supported
392
 * @retval pointer to a @Unsupported header or
393
 *         #SIP_NONE if @a home is NULL
Pekka Pessi's avatar
Pekka Pessi committed
394 395 396 397 398 399 400 401 402 403
 */
sip_unsupported_t *
sip_has_unsupported_any(su_home_t *home,
			sip_supported_t const *supported,
			sip_require_t const *by_require,
			sip_proxy_require_t const *by_proxy_require,
			sip_require_t const *require,
			sip_require_t const *require2,
			sip_require_t const *require3)
{
404
  size_t i, j;
Pekka Pessi's avatar
Pekka Pessi committed
405
  sip_unsupported_t *unsupported = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
406 407 408 409
  msg_param_t const empty[1] = { NULL };
  msg_param_t const *slist = empty;
  msg_param_t const *rlist = empty;
  msg_param_t const *prlist = empty;
Pekka Pessi's avatar
Pekka Pessi committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

  if (require2 == NULL)
    require2 = require3, require3 = NULL;
  if (require == NULL)
    require = require2, require2 = NULL;

  if (require && require->k_items) {
    if (supported && supported->k_items)
      slist = supported->k_items;
    if (by_require && by_require->k_items)
      rlist = by_require->k_items;
    if (by_proxy_require && by_proxy_require->k_items)
      prlist = by_proxy_require->k_items;

    for (i = 0; require->k_items && require->k_items[i];) {
Pekka Pessi's avatar
Pekka Pessi committed
425
      msg_param_t feature = require->k_items[i++];
Pekka Pessi's avatar
Pekka Pessi committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448

      for (j = 0; slist[j]; j++)
	if (strcasecmp(feature, slist[j]) == 0) {
	  feature = NULL;
	  break;
	}

      if (feature)
	for (j = 0; rlist[j]; j++)
	  if (strcasecmp(feature, rlist[j]) == 0) {
	    feature = NULL;
	    break;
	  }

      if (feature)
	for (j = 0; prlist[j]; j++)
	  if (strcasecmp(feature, prlist[j]) == 0) {
	    feature = NULL;
	    break;
	  }

      if (feature) {
	if (home) {
449
	  if (unsupported == NULL)
Pekka Pessi's avatar
Pekka Pessi committed
450 451
	    unsupported = sip_unsupported_make(home, feature);
	  else
452 453
	    msg_params_add(home,
			   (msg_param_t **)&unsupported->k_items,
Pekka Pessi's avatar
Pekka Pessi committed
454 455 456 457 458 459 460 461 462 463 464 465
			   feature);
	}
	else {
	  return (sip_unsupported_t *)SIP_NONE;
	}
      }

      if (require->k_items[i] == NULL && require2 && require2->k_items) {
	i = 0, require = require2, require2 = require3, require3 = NULL;
      }
    }
  }
466

Pekka Pessi's avatar
Pekka Pessi committed
467 468 469 470 471 472
  return unsupported;
}


int sip_has_feature(msg_list_t const *supported, char const *feature)
{
473
  size_t i;
474

Pekka Pessi's avatar
Pekka Pessi committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
  if (!feature || !feature[0])
    return 1;			/* Empty feature is always supported */

  for (; supported; supported = supported->k_next)
    if (supported->k_items)
      for (i = 0; supported->k_items[i]; i++)
	if (strcasecmp(feature, supported->k_items[i]) == 0)
	  return 1;

  return 0;
}

/** Check that a feature is supported. */
int sip_has_supported(sip_supported_t const *supported, char const *feature)
{
  return sip_has_feature(supported, feature);
}

/* ====================================================================== */

/**@SIP_HEADER sip_path Path Header
 *
497
 * The Path header field is a SIP extension header field (@RFC3327) with
498
 * syntax very similar to the @RecordRoute header field. It is used in
Pekka Pessi's avatar
Pekka Pessi committed
499 500
 * conjunction with SIP REGISTER requests and with 200 class messages in
 * response to REGISTER (REGISTER responses).
501
 *
Pekka Pessi's avatar
Pekka Pessi committed
502 503 504 505 506
 * @code
 *    Path        =  "Path" HCOLON path-value *(COMMA path-value)
 *    path-value  =  name-addr *( SEMI rr-param )
 * @endcode
 *
507 508
 *
 * The parsed Path header is stored in #sip_path_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
509 510 511 512 513
 */

/**@ingroup sip_path
 * @typedef typedef struct sip_route_s sip_path_t;
 *
514
 * The structure #sip_path_t contains representation of SIP @Path header.
Pekka Pessi's avatar
Pekka Pessi committed
515 516 517 518 519
 *
 * The #sip_path_t is defined as follows:
 * @code
 * typedef struct sip_route_s {
 *   sip_common_t        r_common[1];   // Common fragment info
520
 *   sip_path_t         *r_next;        // Link to next @Path
Pekka Pessi's avatar
Pekka Pessi committed
521
 *   char const         *r_display;     // Display name
522
 *   url_t               r_url[1];      // @Path URL
Pekka Pessi's avatar
Pekka Pessi committed
523
 *   msg_param_t const  *r_params;      // List of parameters
Pekka Pessi's avatar
Pekka Pessi committed
524 525 526 527 528 529 530
 * } sip_path_t;
 * @endcode
 */

msg_hclass_t sip_path_class[] =
SIP_HEADER_CLASS(path, "Path", "", r_params, prepend, any_route);

531
issize_t sip_path_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
532 533 534 535
{
  return sip_any_route_d(home, h, s, slen);
}

536
issize_t sip_path_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
537 538 539 540 541 542 543 544 545
{
  assert(sip_is_path(h));
  return sip_any_route_e(b, bsiz, h, flags);
}

/* ====================================================================== */

/**@SIP_HEADER sip_service_route Service-Route Header
 *
546
 * The "Service-Route" is a SIP extension header field (@RFC3608), which can
Pekka Pessi's avatar
Pekka Pessi committed
547 548 549
 * contain a route vector that will direct requests through a specific
 * sequence of proxies. A registrar may use a Service-Route header field to
 * inform a UA of a service route that, if used by the UA, will provide
550
 * services from a proxy or set of proxies associated with that registrar.
Pekka Pessi's avatar
Pekka Pessi committed
551 552 553 554 555 556 557 558 559
 * The Service-Route header field may be included by a registrar in the
 * response to a REGISTER request. The syntax for the Service-Route header
 * field is:
 *
 * @code
 *    Service-Route = "Service-Route" HCOLON sr-value *(COMMA sr-value)
 *    sr-value  =  name-addr *( SEMI rr-param )
 * @endcode
 *
560 561 562
 * The parsed Service-Route header is stored in #sip_service_route_t structure.
 *
 * @sa @RFC3608, @Path, @Route, @RecordRoute
Pekka Pessi's avatar
Pekka Pessi committed
563 564 565 566 567
 */

/**@ingroup sip_service_route
 * @typedef typedef struct sip_route_s sip_service_route_t;
 *
568
 * The structure #sip_service_route_t contains representation of SIP
569
 * @ServiceRoute header.
Pekka Pessi's avatar
Pekka Pessi committed
570 571 572 573 574
 *
 * The #sip_service_route_t is defined as follows:
 * @code
 * typedef struct sip_route_s {
 *   sip_common_t        r_common[1];   // Common fragment info
575
 *   sip_service_route_t*r_next;        // Link to next @ServiceRoute
Pekka Pessi's avatar
Pekka Pessi committed
576 577
 *   char const         *r_display;     // Display name
 *   url_t               r_url[1];      // Service-Route URL
Pekka Pessi's avatar
Pekka Pessi committed
578
 *   msg_param_t const  *r_params;      // List of parameters
Pekka Pessi's avatar
Pekka Pessi committed
579 580 581 582 583
 * } sip_service_route_t;
 * @endcode
 */

msg_hclass_t sip_service_route_class[] =
584
SIP_HEADER_CLASS(service_route, "Service-Route", "",
Pekka Pessi's avatar
Pekka Pessi committed
585 586
		 r_params, append, any_route);

587
issize_t sip_service_route_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
588 589 590 591
{
  return sip_any_route_d(home, h, s, slen);
}

592
issize_t sip_service_route_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
593 594 595 596
{
  assert(sip_is_service_route(h));
  return sip_any_route_e(b, bsiz, h, flags);
}